File Coverage

blib/lib/FFI/C/Def.pm
Criterion Covered Total %
statement 141 141 100.0
branch 52 68 76.4
condition 25 45 55.5
subroutine 39 39 100.0
pod 8 8 100.0
total 265 301 88.0


line stmt bran cond sub pod time code
1             package FFI::C::Def;
2              
3 10     10   225420 use strict;
  10         30  
  10         289  
4 10     10   51 use warnings;
  10         18  
  10         213  
5 10     10   170 use 5.008001;
  10         68  
6 10     10   965 use FFI::C::FFI qw( malloc memset );
  10         35  
  10         650  
7 10     10   1343 use FFI::C::Util;
  10         31  
  10         465  
8 10     10   64 use Ref::Util qw( is_blessed_ref is_ref is_plain_hashref is_plain_arrayref );
  10         23  
  10         508  
9 10     10   1033 use Sub::Install ();
  10         3532  
  10         221  
10 10     10   1032 use Sub::Util ();
  10         636  
  10         14419  
11              
12             our @CARP_NOT = qw( FFI::C );
13              
14             # ABSTRACT: Data definition for FFI
15             our $VERSION = '0.14'; # VERSION
16              
17              
18             sub new
19             {
20 58     58 1 239 my $class = shift;
21              
22 58 50       181 Carp::croak("Attempt to call new on a def object (did you mean ->create?)") if is_blessed_ref $class;
23              
24 58 100 66     408 my $ffi = is_blessed_ref($_[0]) && $_[0]->isa('FFI::Platypus') ? shift : FFI::Platypus->new( api => 1 );
25 58         41420 my %args = @_;
26              
27 58 50       197 Carp::croak("Only works with FFI::Platypus api level 1 or better") unless $ffi->api >= 1;
28 58 100       578 Carp::croak("FFI::C::Def is an abstract class") if $class eq 'FFI::C::Def';
29              
30             my $self = bless {
31             ffi => $ffi,
32             name => delete $args{name},
33             class => delete $args{class},
34             nullable => delete $args{nullable},
35 57         422 members => {},
36             align => 0,
37             size => 0,
38             args => \%args,
39             }, $class;
40              
41 57 100       203 if($self->name)
42             {
43 38         92 my $cdef = ref($self);
44 38         249 $cdef =~ s/Def$//;
45 38         127 $ffi->load_custom_type('::CDef' => $self->name,
46             name => $self->name,
47             class => $self->class,
48             nullable => $self->nullable,
49             def => $self,
50             cdef => $cdef,
51             );
52 38         4533 $ffi->def('FFI::C::Def', $self->name, $self);
53             }
54              
55 57         1476 $self;
56             }
57              
58             sub _generate_class
59             {
60 22     22   65 my($self, @accessors) = @_;
61              
62             # first run through all the members, and make sure that we
63             # can generate a class based on the def. That means that:
64             # 1. there is no constructor or destructor defined yet.
65             # 2. none of the member accessors already exist
66             # 3. Any nested cdefs have Perl classes, this will be done
67             # in the subclass
68              
69 22         58 foreach my $method (qw( new DESTROY ))
70             {
71 44         91 my $accessor = join '::', $self->class, $method;
72 44 50       82 Carp::croak("$accessor already defined") if $self->class->can($method);
73             }
74              
75 22         50 foreach my $name (@accessors)
76             {
77 47 50       100 next if $name =~ /^:/;
78 47         82 my $accessor = $self->class . '::' . $name;
79 47 50       75 Carp::croak("$accessor already exists")
80             if $self->class->can($name);
81             }
82              
83 22         1432 require FFI::Platypus::Memory;
84              
85 22 100       22884 if($self->isa('FFI::C::ArrayDef'))
86             {
87              
88 5         19 my $size = $self->size;
89 5         14 my $count = $self->{members}->{count};
90 5         15 my $member_size = $self->{members}->{member}->size;
91              
92             my $code = sub {
93 12     12   2468 my $class = shift;
        12      
        9      
94 12         22 my($ptr, $owner);
95              
96 12         15 my $size = $size;
97 12         22 my $count = $count;
98 12 100       31 if(@_ == 1)
99             {
100 7 100       29 if(!is_ref $_[0])
    50          
101             {
102 2         4 $count = shift;
103             }
104             elsif(is_plain_arrayref $_[0])
105             {
106 5         8 $count = scalar @{$_[0]};
  5         10  
107             }
108 7 50       19 if($count)
109             {
110 7         13 $size = $member_size * $count;
111             }
112             }
113              
114 12 100 66     42 if(@_ == 2 && ! is_ref $_[0])
115             {
116 3         8 ($ptr, $owner) = @_;
117             }
118             else
119             {
120 9 100       137 Carp::croak("Cannot create array without knowing the number of elements")
121             unless $size;
122 8         36 $ptr = FFI::Platypus::Memory::malloc($size);
123 8         27 FFI::Platypus::Memory::memset($ptr, 0, $size);
124             }
125 11         40 my $self = bless {
126             ptr => $ptr,
127             owner => $owner,
128             count => $count,
129             }, $class;
130 11 100 66     64 FFI::C::Util::perl_to_c($self, $_[0]) if @_ == 1 && is_ref $_[0];
131 11         65 $self;
132 5         27 };
133              
134 5         17 Sub::Util::set_subname(join('::', $self->class, 'new'), $code);
135 5         16 Sub::Install::install_sub({
136             code => $code,
137             into => $self->class,
138             as => 'new',
139             });
140             }
141             else
142             {
143 17         51 my $size = $self->size;
144 17 50       53 $size = 1 unless $size > 0;
145              
146             my $code = sub {
147 124     124   9145 my $class = shift;
        124      
        124      
        103      
        93      
        87      
        87      
148 124         176 my($ptr, $owner);
149 124 100 66     417 if(@_ == 2 && ! is_ref $_[0])
150             {
151 113         217 ($ptr, $owner) = @_;
152             }
153             else
154             {
155 11         70 $ptr = FFI::Platypus::Memory::malloc($size);
156 11         43 FFI::Platypus::Memory::memset($ptr, 0, $size);
157             }
158 124         338 my $self = bless {
159             ptr => $ptr,
160             owner => $owner,
161             }, $class;
162 124 100 66     328 FFI::C::Util::perl_to_c($self, $_[0]) if @_ == 1 && is_ref $_[0];
163 124         317 $self;
164 17         108 };
165              
166 17         41 Sub::Util::set_subname(join('::', $self->class, 'new'), $code);
167 17         43 Sub::Install::install_sub({
168             code => $code,
169             into => $self->class,
170             as => 'new',
171             });
172             }
173              
174 22         1336 Sub::Install::install_sub({
175             code => \&_common_destroy,
176             into => $self->class,
177             as => 'DESTROY',
178             });
179             }
180              
181             sub _common_destroy
182             {
183 140     140   22815 my($self) = @_;
184 140 100 66     644 if($self->{ptr} && !$self->{owner})
185             {
186 19         215 FFI::Platypus::Memory::free(delete $self->{ptr});
187             }
188             }
189              
190              
191 182     182 1 24001 sub name { shift->{name} }
192 612     612 1 2447 sub class { shift->{class} }
193 641     641 1 22346 sub ffi { shift->{ffi} }
194 213     213 1 5138 sub size { shift->{size} }
195 26     26 1 1213 sub align { shift->{align} }
196 38     38 1 154 sub nullable { shift->{nullable} }
197              
198              
199             sub create
200             {
201 112     112 1 8198 my $self = shift;
202              
203 112 100       223 return $self->class->new(@_) if $self->class;
204              
205 111         168 my $ptr;
206             my $owner;
207              
208 111 100 66     407 if(@_ == 2 && ! is_ref $_[0])
209             {
210 86         187 ($ptr, $owner) = @_;
211             }
212             else
213             {
214             # TODO: we use 1 byte for size 0
215             # this is needed if malloc(0) returns undef.
216             # we could special case for platforms where malloc(0)
217             # returns a constant pointer that can be free()'d
218 25 100       66 $ptr = malloc($self->size ? $self->size : 1);
219 25         94 memset($ptr, 0, $self->size);
220             }
221              
222 111         220 my $class = ref($self);
223 111         494 $class =~ s/Def$//;
224              
225 111         410 my $inst = bless {
226             ptr => $ptr,
227             def => $self,
228             owner => $owner,
229             }, $class;
230              
231 111 100 100     314 FFI::C::Util::perl_to_c($inst, $_[0]) if @_ == 1 && is_plain_hashref $_[0];
232              
233 111         362 $inst;
234             }
235              
236             package FFI::Platypus::Type::CDef;
237              
238 10     10   79 use Ref::Util qw( is_blessed_ref );
  10         50  
  10         6382  
239              
240             push @FFI::Platypus::CARP_NOT, __PACKAGE__;
241              
242             sub ffi_custom_type_api_1
243             {
244 38     38   1118 my(undef, undef, %args) = @_;
245              
246 38         79 my $perl_to_native;
247             my $native_to_perl;
248              
249 38         80 my $name = $args{name};
250 38         59 my $class = $args{class};
251 38   33     107 my $def = $args{def} || Carp::croak("no def defined");
252 38   33     107 my $cdef = $args{cdef} || Carp::croak("no cdef defined");
253 38         64 my $nullable = $args{nullable};
254              
255 38 100       113 if($class)
    50          
256             {
257             $perl_to_native = sub {
258 8 50 33 8   1729 return undef if !defined $_[0] && $nullable;
259 8 50 33     64 Carp::croak("argument is not a $class")
260             unless is_blessed_ref $_[0]
261             && $_[0]->isa($class);
262 8         15 my $ptr = $_[0]->{ptr};
263 8 50       17 Carp::croak("pointer for $name went away")
264             unless defined $ptr;
265 8         81 $ptr;
266 17         91 };
267             $native_to_perl = sub {
268 5 50   5   148 defined $_[0]
269             ? bless { ptr => $_[0], owner => 1 }, $class
270             : undef;
271 17         48 };
272             }
273              
274             elsif($name)
275             {
276             $perl_to_native = sub {
277 14 100 66 14   2919 return undef if !defined $_[0] && $nullable;
278             Carp::croak("argument is not a $name")
279             unless is_blessed_ref $_[0]
280             && ref($_[0]) eq $cdef
281 13 50 33     154 && $_[0]->{def}->{name} eq $name;
      33        
282 13         28 my $ptr = $_[0]->{ptr};
283 13 50       34 Carp::croak("pointer for $name went away")
284             unless defined $ptr;
285 13         124 $ptr;
286 21         136 };
287             $native_to_perl = sub {
288 5 50   5   369 defined $_[0]
289             ? bless { ptr => $_[0], def => $def, owner => 1 }, $cdef
290             : undef;
291 21         84 };
292             }
293              
294             return {
295 38         208 native_type => 'opaque',
296             perl_to_native => $perl_to_native,
297             native_to_perl => $native_to_perl,
298             }
299             }
300              
301             package FFI::C::EnumDef;
302              
303             sub new
304             {
305 2     2   9 my($class, %self) = @_;
306 2         10 bless \%self, $class;
307             }
308              
309 8     8   24 sub str_lookup { shift->{str_lookup} }
310 14     14   36 sub int_lookup { shift->{int_lookup} }
311 12     12   31 sub type { shift->{type} }
312 12     12   43 sub rev { shift->{rev} }
313              
314             1;
315              
316             __END__