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   187211 use strict;
  10         29  
  10         234  
4 10     10   41 use warnings;
  10         18  
  10         175  
5 10     10   147 use 5.008001;
  10         51  
6 10     10   688 use FFI::C::FFI qw( malloc memset );
  10         17  
  10         547  
7 10     10   1098 use FFI::C::Util;
  10         44  
  10         367  
8 10     10   57 use Ref::Util qw( is_blessed_ref is_ref is_plain_hashref is_plain_arrayref );
  10         14  
  10         461  
9 10     10   842 use Sub::Install ();
  10         2988  
  10         190  
10 10     10   785 use Sub::Util ();
  10         591  
  10         11757  
11              
12             our @CARP_NOT = qw( FFI::C );
13              
14             # ABSTRACT: Data definition for FFI
15             our $VERSION = '0.12'; # VERSION
16              
17              
18             sub new
19             {
20 58     58 1 188 my $class = shift;
21              
22 58 50       138 Carp::croak("Attempt to call new on a def object (did you mean ->create?)") if is_blessed_ref $class;
23              
24 58 100 66     319 my $ffi = is_blessed_ref($_[0]) && $_[0]->isa('FFI::Platypus') ? shift : FFI::Platypus->new( api => 1 );
25 58         33809 my %args = @_;
26              
27 58 50       154 Carp::croak("Only works with FFI::Platypus api level 1 or better") unless $ffi->api >= 1;
28 58 100       513 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         326 members => {},
36             align => 0,
37             size => 0,
38             args => \%args,
39             }, $class;
40              
41 57 100       162 if($self->name)
42             {
43 38         70 my $cdef = ref($self);
44 38         178 $cdef =~ s/Def$//;
45 38         85 $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         3554 $ffi->def('FFI::C::Def', $self->name, $self);
53             }
54              
55 57         1112 $self;
56             }
57              
58             sub _generate_class
59             {
60 22     22   50 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         38 foreach my $method (qw( new DESTROY ))
70             {
71 44         66 my $accessor = join '::', $self->class, $method;
72 44 50       61 Carp::croak("$accessor already defined") if $self->class->can($method);
73             }
74              
75 22         40 foreach my $name (@accessors)
76             {
77 47 50       85 next if $name =~ /^:/;
78 47         98 my $accessor = $self->class . '::' . $name;
79 47 50       72 Carp::croak("$accessor already exists")
80             if $self->class->can($name);
81             }
82              
83 22         1281 require FFI::Platypus::Memory;
84              
85 22 100       17214 if($self->isa('FFI::C::ArrayDef'))
86             {
87              
88 5         14 my $size = $self->size;
89 5         12 my $count = $self->{members}->{count};
90 5         13 my $member_size = $self->{members}->{member}->size;
91              
92             my $code = sub {
93 12     12   2634 my $class = shift;
        12      
        9      
94 12         18 my($ptr, $owner);
95              
96 12         14 my $size = $size;
97 12         18 my $count = $count;
98 12 100       34 if(@_ == 1)
99             {
100 7 100       24 if(!is_ref $_[0])
    50          
101             {
102 2         2 $count = shift;
103             }
104             elsif(is_plain_arrayref $_[0])
105             {
106 5         8 $count = scalar @{$_[0]};
  5         9  
107             }
108 7 50       16 if($count)
109             {
110 7         10 $size = $member_size * $count;
111             }
112             }
113              
114 12 100 66     35 if(@_ == 2 && ! is_ref $_[0])
115             {
116 3         6 ($ptr, $owner) = @_;
117             }
118             else
119             {
120 9 100       99 Carp::croak("Cannot create array without knowing the number of elements")
121             unless $size;
122 8         33 $ptr = FFI::Platypus::Memory::malloc($size);
123 8         22 FFI::Platypus::Memory::memset($ptr, 0, $size);
124             }
125 11         38 my $self = bless {
126             ptr => $ptr,
127             owner => $owner,
128             count => $count,
129             }, $class;
130 11 100 66     62 FFI::C::Util::perl_to_c($self, $_[0]) if @_ == 1 && is_ref $_[0];
131 11         54 $self;
132 5         24 };
133              
134 5         11 Sub::Util::set_subname(join('::', $self->class, 'new'), $code);
135 5         12 Sub::Install::install_sub({
136             code => $code,
137             into => $self->class,
138             as => 'new',
139             });
140             }
141             else
142             {
143 17         42 my $size = $self->size;
144 17 50       41 $size = 1 unless $size > 0;
145              
146             my $code = sub {
147 124     124   6913 my $class = shift;
        124      
        124      
        103      
        93      
        87      
        87      
148 124         134 my($ptr, $owner);
149 124 100 66     340 if(@_ == 2 && ! is_ref $_[0])
150             {
151 113         154 ($ptr, $owner) = @_;
152             }
153             else
154             {
155 11         58 $ptr = FFI::Platypus::Memory::malloc($size);
156 11         34 FFI::Platypus::Memory::memset($ptr, 0, $size);
157             }
158 124         300 my $self = bless {
159             ptr => $ptr,
160             owner => $owner,
161             }, $class;
162 124 100 66     244 FFI::C::Util::perl_to_c($self, $_[0]) if @_ == 1 && is_ref $_[0];
163 124         257 $self;
164 17         70 };
165              
166 17         35 Sub::Util::set_subname(join('::', $self->class, 'new'), $code);
167 17         39 Sub::Install::install_sub({
168             code => $code,
169             into => $self->class,
170             as => 'new',
171             });
172             }
173              
174 22         1101 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   18201 my($self) = @_;
184 140 100 66     526 if($self->{ptr} && !$self->{owner})
185             {
186 19         173 FFI::Platypus::Memory::free(delete $self->{ptr});
187             }
188             }
189              
190              
191 182     182 1 19883 sub name { shift->{name} }
192 612     612 1 2018 sub class { shift->{class} }
193 641     641 1 18881 sub ffi { shift->{ffi} }
194 213     213 1 4103 sub size { shift->{size} }
195 26     26 1 986 sub align { shift->{align} }
196 38     38 1 182 sub nullable { shift->{nullable} }
197              
198              
199             sub create
200             {
201 112     112 1 6247 my $self = shift;
202              
203 112 100       179 return $self->class->new(@_) if $self->class;
204              
205 111         146 my $ptr;
206             my $owner;
207              
208 111 100 66     334 if(@_ == 2 && ! is_ref $_[0])
209             {
210 86         132 ($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       59 $ptr = malloc($self->size ? $self->size : 1);
219 25         52 memset($ptr, 0, $self->size);
220             }
221              
222 111         165 my $class = ref($self);
223 111         410 $class =~ s/Def$//;
224              
225 111         362 my $inst = bless {
226             ptr => $ptr,
227             def => $self,
228             owner => $owner,
229             }, $class;
230              
231 111 100 100     243 FFI::C::Util::perl_to_c($inst, $_[0]) if @_ == 1 && is_plain_hashref $_[0];
232              
233 111         346 $inst;
234             }
235              
236             package FFI::Platypus::Type::CDef;
237              
238 10     10   70 use Ref::Util qw( is_blessed_ref );
  10         22  
  10         5200  
239              
240             push @FFI::Platypus::CARP_NOT, __PACKAGE__;
241              
242             sub ffi_custom_type_api_1
243             {
244 38     38   913 my(undef, undef, %args) = @_;
245              
246 38         68 my $perl_to_native;
247             my $native_to_perl;
248              
249 38         60 my $name = $args{name};
250 38         56 my $class = $args{class};
251 38   33     85 my $def = $args{def} || Carp::croak("no def defined");
252 38   33     77 my $cdef = $args{cdef} || Carp::croak("no cdef defined");
253 38         52 my $nullable = $args{nullable};
254              
255 38 100       93 if($class)
    50          
256             {
257             $perl_to_native = sub {
258 8 50 33 8   1231 return undef if !defined $_[0] && $nullable;
259 8 50 33     63 Carp::croak("argument is not a $class")
260             unless is_blessed_ref $_[0]
261             && $_[0]->isa($class);
262 8         11 my $ptr = $_[0]->{ptr};
263 8 50       14 Carp::croak("pointer for $name went away")
264             unless defined $ptr;
265 8         51 $ptr;
266 17         72 };
267             $native_to_perl = sub {
268 5 50   5   123 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   2066 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     95 && $_[0]->{def}->{name} eq $name;
      33        
282 13         23 my $ptr = $_[0]->{ptr};
283 13 50       24 Carp::croak("pointer for $name went away")
284             unless defined $ptr;
285 13         92 $ptr;
286 21         92 };
287             $native_to_perl = sub {
288 5 50   5   298 defined $_[0]
289             ? bless { ptr => $_[0], def => $def, owner => 1 }, $cdef
290             : undef;
291 21         61 };
292             }
293              
294             return {
295 38         166 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   8 my($class, %self) = @_;
306 2         9 bless \%self, $class;
307             }
308              
309 8     8   21 sub str_lookup { shift->{str_lookup} }
310 14     14   29 sub int_lookup { shift->{int_lookup} }
311 12     12   26 sub type { shift->{type} }
312 12     12   30 sub rev { shift->{rev} }
313              
314             1;
315              
316             __END__