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