File Coverage

blib/lib/FFI/Platypus.pm
Criterion Covered Total %
statement 247 253 97.6
branch 135 160 84.3
condition 53 78 67.9
subroutine 40 40 100.0
pod 28 28 100.0
total 503 559 89.9


line stmt bran cond sub pod time code
1             package FFI::Platypus;
2              
3 56     56   9612317 use strict;
  56         477  
  56         1702  
4 56     56   340 use warnings;
  56         131  
  56         1346  
5 56     56   1335 use 5.008004;
  56         193  
6 56     56   317 use Carp qw( croak );
  56         133  
  56         2842  
7 56     56   22792 use FFI::Platypus::Function;
  56         157  
  56         1648  
8 56     56   31481 use FFI::Platypus::Type;
  56         145  
  56         172570  
9              
10             # ABSTRACT: Write Perl bindings to non-Perl libraries with FFI. No XS required.
11             our $VERSION = '2.08'; # VERSION
12              
13             # Platypus-Man,
14             # Platypus-Man,
15             # Does Whatever A Platypus Can
16             # Is Mildly Venomous
17             # Hangs Out In Rivers By Caves
18             # Look Out!
19             # Here Comes The Platypus-Man
20              
21             # From the original FFI::Platypus prototype:
22             # Kinda like gluing a duckbill to an adorable mammal
23              
24              
25             our @CARP_NOT = qw( FFI::Platypus::Declare FFI::Platypus::Record );
26              
27             require XSLoader;
28             XSLoader::load(
29             'FFI::Platypus', $FFI::Platypus::VERSION || 0
30             );
31              
32              
33             sub new
34             {
35 351     351 1 1247997 my($class, %args) = @_;
36 351         687 my @lib;
37 351 100       1107 if(exists $args{lib})
38             {
39 97 100       478 if(!ref($args{lib}))
    50          
40             {
41 2         13 push @lib, $args{lib};
42             }
43             elsif(ref($args{lib}) eq 'ARRAY')
44             {
45 95         163 push @lib, @{$args{lib}};
  95         237  
46             }
47             else
48             {
49 0         0 croak "lib argument must be a scalar or array reference";
50             }
51             }
52              
53 351   100     1537 my $api = $args{api} || 0;
54 351   50     1389 my $experimental = $args{experimental} || 0;
55              
56 351 50       1219 if($experimental == 1)
    50          
57             {
58 0         0 Carp::croak("Please do not use the experimental version of api = 1, instead require FFI::Platypus 1.00 or better");
59             }
60             elsif($experimental == 2)
61             {
62 0         0 Carp::croak("Please do not use the experimental version of api = 2, instead require FFI::Platypus 2.00 or better");
63             }
64              
65 351 50 33     1677 if(defined $api && $api > 2 && $experimental != $api)
      33        
66             {
67 0         0 Carp::cluck("Enabling development API version $api prior to FFI::Platypus $api.00");
68             }
69              
70 351         563 my $tp;
71              
72 351 100       1111 if($api == 0)
    100          
    50          
73             {
74 236         483 $tp = 'Version0';
75             }
76             elsif($api == 1)
77             {
78 50         135 $tp = 'Version1';
79             }
80             elsif($api == 2)
81             {
82 65         198 $tp = 'Version2';
83             }
84             else
85             {
86 0         0 Carp::croak("API version $api not (yet) implemented");
87             }
88              
89 351         49968 require "FFI/Platypus/TypeParser/$tp.pm";
90 351         1040 $tp = "FFI::Platypus::TypeParser::$tp";
91              
92             my $self = bless {
93             lib => \@lib,
94             lang => '',
95             handles => {},
96             abi => -1,
97             api => $api,
98             tp => $tp->new,
99             fini => [],
100 351 100       2782 ignore_not_found => defined $args{ignore_not_found} ? $args{ignore_not_found} : 0,
101             }, $class;
102              
103 351   100     2183 $self->lang($args{lang} || 'C');
104              
105 351         1488 $self;
106             }
107              
108             sub _lang_class ($)
109             {
110 498     498   1010 my($lang) = @_;
111 498 100       1897 my $class = $lang =~ m/^=(.*)$/ ? $1 : "FFI::Platypus::Lang::$lang";
112 498 100       3469 unless($class->can('native_type_map'))
113             {
114 54         202 my $pm = "$class.pm";
115 54         373 $pm =~ s/::/\//g;
116 54         23282 require $pm;
117             }
118 498 50       2535 croak "$class does not provide native_type_map method"
119             unless $class->can("native_type_map");
120 498         1298 $class;
121             }
122              
123              
124             sub lib
125             {
126 103     103 1 213960 my($self, @new) = @_;
127              
128 103 100       395 if(@new)
129             {
130 91 100       170 push @{ $self->{lib} }, map { ref $_ eq 'CODE' ? $_->() : $_ } @new;
  91         386  
  91         500  
131 91         265 delete $self->{mangler};
132             }
133              
134 103         203 @{ $self->{lib} };
  103         371  
135             }
136              
137              
138             sub ignore_not_found
139             {
140 66     66 1 176 my($self, $value) = @_;
141              
142 66 100       172 if(defined $value)
143             {
144 3         8 $self->{ignore_not_found} = $value;
145             }
146              
147 66         11866 $self->{ignore_not_found};
148             }
149              
150              
151             sub lang
152             {
153 354     354 1 1241 my($self, $value) = @_;
154              
155 354 50 33     1872 if(defined $value && $value ne $self->{lang})
156             {
157 354         755 $self->{lang} = $value;
158 354         921 my $class = _lang_class($self->{lang});
159 354 100       1697 $self->abi($class->abi) if $class->can('abi');
160              
161             {
162 354         614 my %type_map;
  354         574  
163             my $map = $class->native_type_map(
164             $self->{api} > 0
165             ? (api => $self->{api})
166 354 100       1560 : ()
167             );
168 354         3141 foreach my $key (keys %$map)
169             {
170 18805         25232 my $value = $map->{$key};
171 18805 50       33985 next unless $self->{tp}->have_type($value);
172 18805         34125 $type_map{$key} = $value;
173             }
174 354         1771 $type_map{$_} = $_ for grep { $self->{tp}->have_type($_) }
  5664         10587  
175             qw( void sint8 uint8 sint16 uint16 sint32 uint32 sint64 uint64 float double string opaque
176             longdouble complex_float complex_double );
177 354 100       2543 $type_map{pointer} = 'opaque' if $self->{tp}->isa('FFI::Platypus::TypeParser::Version0');
178 354         1449 $self->{tp}->type_map(\%type_map);
179             }
180              
181 354 100       2400 $class->load_custom_types($self) if $class->can('load_custom_types');
182             }
183              
184 354         743 $self->{lang};
185             }
186              
187              
188 3     3 1 24 sub api { shift->{api} }
189              
190              
191             sub type
192             {
193 278     278 1 47614 my($self, $name, $alias) = @_;
194 278 50 33     1265 croak "usage: \$ffi->type(name => alias) (alias is optional)" unless defined $self && defined $name;
195              
196 278 100       1133 $self->{tp}->check_alias($alias) if defined $alias;
197 278         844 my $type = $self->{tp}->parse($name);
198 265 100       1170 $self->{tp}->set_alias($alias, $type) if defined $alias;
199              
200 265         597 $self;
201             }
202              
203              
204             sub custom_type
205             {
206 207     207 1 159105 my($self, $alias, $cb) = @_;
207              
208 207   100     916 my $argument_count = $cb->{argument_count} || 1;
209              
210 207 50       561 croak "argument_count must be >= 1"
211             unless $argument_count >= 1;
212              
213 207 50 33     868 croak "Usage: \$ffi->custom_type(\$alias, { ... })"
214             unless defined $alias && ref($cb) eq 'HASH';
215              
216             croak "must define at least one of native_to_perl, perl_to_native, or perl_to_native_post"
217 207 50 100     863 unless defined $cb->{native_to_perl} || defined $cb->{perl_to_native} || defined $cb->{perl_to_native_post};
      66        
218              
219 207         803 $self->{tp}->check_alias($alias);
220              
221             my $type = $self->{tp}->create_type_custom(
222             $cb->{native_type},
223             $cb->{perl_to_native},
224             $cb->{native_to_perl},
225             $cb->{perl_to_native_post},
226 207         1344 $argument_count,
227             );
228              
229 205         925 $self->{tp}->set_alias($alias, $type);
230              
231 205         459 $self;
232             }
233              
234              
235             sub load_custom_type
236             {
237 13     13 1 13122 my($self, $name, $alias, @type_args) = @_;
238              
239 13 50 33     71 croak "usage: \$ffi->load_custom_type(\$name, \$alias, ...)"
240             unless defined $name && defined $alias;
241              
242 13 50       98 $name = "FFI::Platypus::Type$name" if $name =~ /^::/;
243 13 50       60 $name = "FFI::Platypus::Type::$name" unless $name =~ /::/;
244              
245 13 100       115 unless($name->can("ffi_custom_type_api_1"))
246             {
247 3         12 my $pm = "$name.pm";
248 3         16 $pm =~ s/::/\//g;
249 3         7 eval { require $pm };
  3         1428  
250 3 50       16 warn $@ if $@;
251             }
252              
253 13 50       87 unless($name->can("ffi_custom_type_api_1"))
254             {
255 0         0 croak "$name does not appear to conform to the custom type API";
256             }
257              
258 13         50 my $cb = $name->ffi_custom_type_api_1($self, @type_args);
259 13         60 $self->custom_type($alias => $cb);
260              
261 13         46 $self;
262             }
263              
264              
265             sub types
266             {
267 3     3 1 73160 my($self) = @_;
268 3 100 66     26 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  2         17  
269 3         35 sort $self->{tp}->list_types;
270             }
271              
272              
273             sub type_meta
274             {
275 131     131 1 63632 my($self, $name) = @_;
276 131 50 33     397 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  131         688  
277 131         461 $self->{tp}->parse($name)->meta;
278             }
279              
280              
281             sub mangler
282             {
283 12     12 1 64 my($self, $sub) = @_;
284 12         70 $self->{mangler} = $self->{mymangler} = $sub;
285             }
286              
287              
288             sub function
289             {
290 1283     1283 1 175275 my $wrapper;
291 1283 100       2999 $wrapper = pop if ref $_[-1] eq 'CODE';
292              
293 1283 50 33     4811 croak "usage \$ffi->function( \$name, \\\@arguments, [\\\@var_args], [\$return_type])" unless @_ >= 3 && @_ <= 6;
294              
295 1283         1906 my $self = shift;
296 1283         1812 my $name = shift;
297 1283         1644 my $fixed_args = shift;
298 1283         1711 my $var_args;
299 1283 100 100     4049 $var_args = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
300 1283         1831 my $ret = shift;
301 1283 100       2323 $ret = 'void' unless defined $ret;
302              
303             # special case: treat a single void argument type as an empty list of
304             # arguments, a la olde timey C compilers.
305 1283 100 100     5394 if( (!defined $var_args) && @$fixed_args == 1 && $fixed_args->[0] eq 'void' )
      100        
306             {
307 1         2 $fixed_args = [];
308             }
309              
310 1283 100       2468 my $fixed_arg_count = defined $var_args ? scalar(@$fixed_args) : -1;
311              
312 1283 50       2498 my @args = map { $self->{tp}->parse($_) || croak "unknown type: $_" } @$fixed_args;
  1536         4298  
313 1283 100       2934 if($var_args)
314             {
315             push @args, map {
316 30         69 my $type = $self->{tp}->parse($_);
  127         252  
317             # https://github.com/PerlFFI/FFI-Platypus/issues/323
318 127 100       375 $type->type_code == 67 ? $self->{tp}->parse('double') : $type
319             } @$var_args;
320             }
321              
322 1283   33     3096 $ret = $self->{tp}->parse($ret) || croak "unknown type: $ret";
323 1283 100       5796 my $address = $name =~ /^-?[0-9]+$/ ? $name : $self->find_symbol($name);
324 1283 100 100     3217 croak "unable to find $name" unless defined $address || $self->ignore_not_found;
325 1225 100       2234 return unless defined $address;
326 1220 100       2639 $address = @args > 0 ? _cast1() : _cast0() if $address == 0;
    100          
327 1220         10357 my $function = FFI::Platypus::Function::Function->new($self, $address, $self->{abi}, $fixed_arg_count, $ret, @args);
328 1219 100       6953 $wrapper
329             ? FFI::Platypus::Function::Wrapper->new($function, $wrapper)
330             : $function;
331             }
332              
333             sub _function_meta
334             {
335             # NOTE: may be upgraded to a documented function one day,
336             # but shouldn't be used externally as we will rename it
337             # if that happens.
338 2     2   1550 my($self, $name, $meta, $args, $ret) = @_;
339 2         6 $args = ['opaque','int',@$args];
340             $self->function(
341             $name, $args, $ret, sub {
342 4     4   9 my $xsub = shift;
343 4         24 $xsub->($meta, scalar(@_), @_);
344             },
345 2         12 );
346             }
347              
348              
349             sub attach
350             {
351 888     888 1 305274 my $wrapper;
352 888 100       2245 $wrapper = pop if ref $_[-1] eq 'CODE';
353              
354 888         1321 my $self = shift;
355 888         1302 my $name = shift;
356 888         1214 my $args = shift;
357 888         1178 my $varargs;
358 888 100 100     3228 $varargs = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
359 888         1431 my $ret = shift;
360 888         1242 my $proto = shift;
361              
362 888 100       1703 $ret = 'void' unless defined $ret;
363              
364 888 100       2375 my($c_name, $perl_name) = ref($name) ? @$name : ($name, $name);
365              
366 888 50       3412 croak "you tried to provide a perl name that looks like an address"
367             if $perl_name =~ /^-?[0-9]+$/;
368              
369 888 100       2518 my $function = $varargs
370             ? $self->function($c_name, $args, $varargs, $ret, $wrapper)
371             : $self->function($c_name, $args, $ret, $wrapper);
372              
373 885 100       1875 if(defined $function)
374             {
375 883         2476 $function->attach($perl_name, $proto);
376             }
377              
378 885         2153 $self;
379             }
380              
381              
382             sub closure
383             {
384 86     86 1 321683 my($self, $coderef) = @_;
385 86 100       288 return undef unless defined $coderef;
386 85 50       276 croak "not a coderef" unless ref $coderef eq 'CODE';
387 85         8651 require FFI::Platypus::Closure;
388 85         403 FFI::Platypus::Closure->new($coderef);
389             }
390              
391              
392             sub cast
393             {
394 137     137 1 46382 $_[0]->function(0 => [$_[1]] => $_[2])->call($_[3]);
395             }
396              
397              
398             sub attach_cast
399             {
400 4     4 1 2269 my($self, $name, $type1, $type2, $wrapper) = @_;
401 4         10 my $caller = caller;
402 4 50       24 $name = join '::', $caller, $name unless $name =~ /::/;
403 4 100 66     18 if(defined $wrapper && ref($wrapper) eq 'CODE')
404             {
405 1         7 $self->attach([0 => $name] => [$type1] => $type2 => '$', $wrapper);
406             }
407             else
408             {
409 3         13 $self->attach([0 => $name] => [$type1] => $type2 => '$');
410             }
411 4         13 $self;
412             }
413              
414              
415             sub sizeof
416             {
417 209     209 1 69219 my($self,$name) = @_;
418             ref $self
419 209 100       847 ? $self->{tp}->parse($name)->sizeof
420             : $self->new->sizeof($name);
421             }
422              
423              
424             sub alignof
425             {
426 74     74 1 16389 my($self, $name) = @_;
427             ref $self
428 74 100       269 ? $self->{tp}->parse($name)->alignof
429             : $self->new->alignof($name);
430             }
431              
432              
433             sub kindof
434             {
435 12     12 1 1552 my($self, $name) = @_;
436             ref $self
437 12 100       52 ? $self->{tp}->parse($name)->kindof
438             : $self->new->kindof($name);
439             }
440              
441              
442             sub countof
443             {
444 12     12 1 2032 my($self, $name) = @_;
445             ref $self
446 12 100       43 ? $self->{tp}->parse($name)->countof
447             : $self->new->countof($name);
448             }
449              
450              
451             sub def
452             {
453 10     10 1 1704 my $self = shift;
454 10   66     36 my $package = shift || caller;
455 10         15 my $type = shift;
456 10 100       27 if(@_)
457             {
458 4         15 $self->type($type);
459 3         14 $self->{def}->{$package}->{$type} = shift;
460             }
461 9         62 $self->{def}->{$package}->{$type};
462             }
463              
464              
465             sub unitof
466             {
467 12     12 1 2786 my($self, $name) = @_;
468             ref $self
469 12 100       51 ? $self->{tp}->parse($name)->unitof
470             : $self->new->unitof($name);
471             }
472              
473              
474             sub find_lib
475             {
476 7     7 1 59 my $self = shift;
477 7         486 require FFI::CheckLib;
478 7         7124 $self->lib(FFI::CheckLib::find_lib(@_));
479 7         21 $self;
480             }
481              
482              
483             sub find_symbol
484             {
485 1173     1173 1 4265 my($self, $name) = @_;
486              
487 1173   66     3232 $self->{mangler} ||= $self->{mymangler};
488              
489 1173 100       2338 unless(defined $self->{mangler})
490             {
491 144         507 my $class = _lang_class($self->{lang});
492 144 100       1078 if($class->can('mangler'))
493             {
494 2         6 $self->{mangler} = $class->mangler($self->lib);
495             }
496             else
497             {
498 142     1127   1042 $self->{mangler} = sub { $_[0] };
  1127         7733  
499             }
500             }
501              
502 1173         1746 foreach my $path (@{ $self->{lib} })
  1173         2797  
503             {
504 56   100 56   515 my $handle = do { no warnings; $self->{handles}->{$path||0} } || FFI::Platypus::DL::dlopen($path, FFI::Platypus::DL::RTLD_PLATYPUS_DEFAULT());
  56         178  
  56         36068  
  1204         1565  
505 1204 100       2689 unless($handle)
506             {
507             warn "warning: error loading $path: ", FFI::Platypus::DL::dlerror()
508 2 100 66     55 if $self->{api} > 0 || $ENV{FFI_PLATYPUS_DLERROR};
509 2         476 next;
510             }
511 1202         2551 my $address = FFI::Platypus::DL::dlsym($handle, $self->{mangler}->($name));
512 1202 100       2766 if($address)
513             {
514 1076   100     3013 $self->{handles}->{$path||0} = $handle;
515 1076         2556 return $address;
516             }
517             else
518             {
519 126 100 100     829 FFI::Platypus::DL::dlclose($handle) unless $self->{handles}->{$path||0};
520             }
521             }
522 97         317 return;
523             }
524              
525              
526             sub bundle
527             {
528 27 50   27 1 134 croak "bundle method only available with api => 1 or better" if $_[0]->{api} < 1;
529 27         8777 require FFI::Platypus::Bundle;
530 27         190 goto &_bundle;
531             }
532              
533              
534             sub package
535             {
536 3 100   3 1 579 croak "package method only available with api => 0" if $_[0]->{api} > 0;
537 2         942 require FFI::Platypus::Legacy;
538 2         12 goto &_package;
539             }
540              
541              
542             sub abis
543             {
544 15     15 1 564 require FFI::Platypus::ShareConfig;
545 15         54 FFI::Platypus::ShareConfig->get("abi");
546             }
547              
548              
549             sub abi
550             {
551 13     13 1 10203 my($self, $newabi) = @_;
552 13 100       72 unless($newabi =~ /^[0-9]+$/)
553             {
554 7 100       19 unless(defined $self->abis->{$newabi})
555             {
556 1         256 croak "no such ABI: $newabi";
557             }
558 6         24 $newabi = $self->abis->{$newabi};
559             }
560              
561 12 100       117 unless(FFI::Platypus::ABI::verify($newabi))
562             {
563 1         94 croak "no such ABI: $newabi";
564             }
565              
566 11         28 $self->{abi} = $newabi;
567 11         63 $self->{tp}->abi($newabi);
568              
569 11         25 $self;
570             }
571              
572             sub DESTROY
573             {
574 249     249   167818 my($self) = @_;
575 249         446 foreach my $fini (@{ $self->{fini} })
  249         686  
576             {
577 1         13 $fini->($self);
578             }
579 249         463 foreach my $handle (values %{ $self->{handles} })
  249         749  
580             {
581 45 50       183 next unless $handle;
582 45         1588 FFI::Platypus::DL::dlclose($handle);
583             }
584 249         8358 delete $self->{handles};
585             }
586              
587             1;
588              
589             __END__