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   9120586 use strict;
  56         355  
  56         1699  
4 56     56   309 use warnings;
  56         114  
  56         1416  
5 56     56   1315 use 5.008004;
  56         190  
6 56     56   317 use Carp qw( croak );
  56         128  
  56         3075  
7 56     56   22010 use FFI::Platypus::Function;
  56         150  
  56         1530  
8 56     56   29474 use FFI::Platypus::Type;
  56         142  
  56         165716  
9              
10             # ABSTRACT: Write Perl bindings to non-Perl libraries with FFI. No XS required.
11             our $VERSION = '2.06_01'; # TRIAL 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 1264439 my($class, %args) = @_;
36 351         658 my @lib;
37 351 100       1202 if(exists $args{lib})
38             {
39 97 100       509 if(!ref($args{lib}))
    50          
40             {
41 2         5 push @lib, $args{lib};
42             }
43             elsif(ref($args{lib}) eq 'ARRAY')
44             {
45 95         181 push @lib, @{$args{lib}};
  95         248  
46             }
47             else
48             {
49 0         0 croak "lib argument must be a scalar or array reference";
50             }
51             }
52              
53 351   100     1590 my $api = $args{api} || 0;
54 351   50     1438 my $experimental = $args{experimental} || 0;
55              
56 351 50       1301 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     1737 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         629 my $tp;
71              
72 351 100       1076 if($api == 0)
    100          
    50          
73             {
74 236         508 $tp = 'Version0';
75             }
76             elsif($api == 1)
77             {
78 50         132 $tp = 'Version1';
79             }
80             elsif($api == 2)
81             {
82 65         158 $tp = 'Version2';
83             }
84             else
85             {
86 0         0 Carp::croak("API version $api not (yet) implemented");
87             }
88              
89 351         50216 require "FFI/Platypus/TypeParser/$tp.pm";
90 351         1047 $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       2798 ignore_not_found => defined $args{ignore_not_found} ? $args{ignore_not_found} : 0,
101             }, $class;
102              
103 351   100     2296 $self->lang($args{lang} || 'C');
104              
105 351         1334 $self;
106             }
107              
108             sub _lang_class ($)
109             {
110 498     498   1002 my($lang) = @_;
111 498 100       1989 my $class = $lang =~ m/^=(.*)$/ ? $1 : "FFI::Platypus::Lang::$lang";
112 498 100       3579 unless($class->can('native_type_map'))
113             {
114 54         194 my $pm = "$class.pm";
115 54         319 $pm =~ s/::/\//g;
116 54         29847 require $pm;
117             }
118 498 50       2642 croak "$class does not provide native_type_map method"
119             unless $class->can("native_type_map");
120 498         1224 $class;
121             }
122              
123              
124             sub lib
125             {
126 103     103 1 216531 my($self, @new) = @_;
127              
128 103 100       342 if(@new)
129             {
130 91 100       172 push @{ $self->{lib} }, map { ref $_ eq 'CODE' ? $_->() : $_ } @new;
  91         293  
  91         507  
131 91         240 delete $self->{mangler};
132             }
133              
134 103         182 @{ $self->{lib} };
  103         347  
135             }
136              
137              
138             sub ignore_not_found
139             {
140 66     66 1 221 my($self, $value) = @_;
141              
142 66 100       185 if(defined $value)
143             {
144 3         6 $self->{ignore_not_found} = $value;
145             }
146              
147 66         11034 $self->{ignore_not_found};
148             }
149              
150              
151             sub lang
152             {
153 354     354 1 1286 my($self, $value) = @_;
154              
155 354 50 33     1971 if(defined $value && $value ne $self->{lang})
156             {
157 354         734 $self->{lang} = $value;
158 354         933 my $class = _lang_class($self->{lang});
159 354 100       1691 $self->abi($class->abi) if $class->can('abi');
160              
161             {
162 354         597 my %type_map;
  354         562  
163             my $map = $class->native_type_map(
164             $self->{api} > 0
165             ? (api => $self->{api})
166 354 100       1598 : ()
167             );
168 354         3216 foreach my $key (keys %$map)
169             {
170 18805         25787 my $value = $map->{$key};
171 18805 50       34830 next unless $self->{tp}->have_type($value);
172 18805         35200 $type_map{$key} = $value;
173             }
174 354         1701 $type_map{$_} = $_ for grep { $self->{tp}->have_type($_) }
  5664         10828  
175             qw( void sint8 uint8 sint16 uint16 sint32 uint32 sint64 uint64 float double string opaque
176             longdouble complex_float complex_double );
177 354 100       2558 $type_map{pointer} = 'opaque' if $self->{tp}->isa('FFI::Platypus::TypeParser::Version0');
178 354         1500 $self->{tp}->type_map(\%type_map);
179             }
180              
181 354 100       2395 $class->load_custom_types($self) if $class->can('load_custom_types');
182             }
183              
184 354         753 $self->{lang};
185             }
186              
187              
188 3     3 1 15 sub api { shift->{api} }
189              
190              
191             sub type
192             {
193 278     278 1 48900 my($self, $name, $alias) = @_;
194 278 50 33     1187 croak "usage: \$ffi->type(name => alias) (alias is optional)" unless defined $self && defined $name;
195              
196 278 100       1430 $self->{tp}->check_alias($alias) if defined $alias;
197 278         769 my $type = $self->{tp}->parse($name);
198 265 100       1113 $self->{tp}->set_alias($alias, $type) if defined $alias;
199              
200 265         584 $self;
201             }
202              
203              
204             sub custom_type
205             {
206 207     207 1 158862 my($self, $alias, $cb) = @_;
207              
208 207   100     964 my $argument_count = $cb->{argument_count} || 1;
209              
210 207 50       531 croak "argument_count must be >= 1"
211             unless $argument_count >= 1;
212              
213 207 50 33     927 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     818 unless defined $cb->{native_to_perl} || defined $cb->{perl_to_native} || defined $cb->{perl_to_native_post};
      66        
218              
219 207         804 $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         1187 $argument_count,
227             );
228              
229 205         907 $self->{tp}->set_alias($alias, $type);
230              
231 205         492 $self;
232             }
233              
234              
235             sub load_custom_type
236             {
237 13     13 1 13873 my($self, $name, $alias, @type_args) = @_;
238              
239 13 50 33     73 croak "usage: \$ffi->load_custom_type(\$name, \$alias, ...)"
240             unless defined $name && defined $alias;
241              
242 13 50       89 $name = "FFI::Platypus::Type$name" if $name =~ /^::/;
243 13 50       56 $name = "FFI::Platypus::Type::$name" unless $name =~ /::/;
244              
245 13 100       116 unless($name->can("ffi_custom_type_api_1"))
246             {
247 3         11 my $pm = "$name.pm";
248 3         15 $pm =~ s/::/\//g;
249 3         7 eval { require $pm };
  3         1429  
250 3 50       15 warn $@ if $@;
251             }
252              
253 13 50       83 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         54 my $cb = $name->ffi_custom_type_api_1($self, @type_args);
259 13         66 $self->custom_type($alias => $cb);
260              
261 13         40 $self;
262             }
263              
264              
265             sub types
266             {
267 3     3 1 85352 my($self) = @_;
268 3 100 66     18 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  2         14  
269 3         22 sort $self->{tp}->list_types;
270             }
271              
272              
273             sub type_meta
274             {
275 131     131 1 65268 my($self, $name) = @_;
276 131 50 33     396 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  131         666  
277 131         534 $self->{tp}->parse($name)->meta;
278             }
279              
280              
281             sub mangler
282             {
283 12     12 1 49 my($self, $sub) = @_;
284 12         79 $self->{mangler} = $self->{mymangler} = $sub;
285             }
286              
287              
288             sub function
289             {
290 1283     1283 1 177920 my $wrapper;
291 1283 100       2812 $wrapper = pop if ref $_[-1] eq 'CODE';
292              
293 1283 50 33     4845 croak "usage \$ffi->function( \$name, \\\@arguments, [\\\@var_args], [\$return_type])" unless @_ >= 3 && @_ <= 6;
294              
295 1283         1917 my $self = shift;
296 1283         1869 my $name = shift;
297 1283         1686 my $fixed_args = shift;
298 1283         1636 my $var_args;
299 1283 100 100     4139 $var_args = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
300 1283         1881 my $ret = shift;
301 1283 100       2429 $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     5412 if( (!defined $var_args) && @$fixed_args == 1 && $fixed_args->[0] eq 'void' )
      100        
306             {
307 1         2 $fixed_args = [];
308             }
309              
310 1283 100       2487 my $fixed_arg_count = defined $var_args ? scalar(@$fixed_args) : -1;
311              
312 1283 50       2420 my @args = map { $self->{tp}->parse($_) || croak "unknown type: $_" } @$fixed_args;
  1536         4324  
313 1283 100       2921 if($var_args)
314             {
315             push @args, map {
316 30         51 my $type = $self->{tp}->parse($_);
  127         246  
317             # https://github.com/PerlFFI/FFI-Platypus/issues/323
318 127 100       394 $type->type_code == 67 ? $self->{tp}->parse('double') : $type
319             } @$var_args;
320             }
321              
322 1283   33     3126 $ret = $self->{tp}->parse($ret) || croak "unknown type: $ret";
323 1283 100       5953 my $address = $name =~ /^-?[0-9]+$/ ? $name : $self->find_symbol($name);
324 1283 100 100     3188 croak "unable to find $name" unless defined $address || $self->ignore_not_found;
325 1225 100       2296 return unless defined $address;
326 1220 100       2707 $address = @args > 0 ? _cast1() : _cast0() if $address == 0;
    100          
327 1220         9959 my $function = FFI::Platypus::Function::Function->new($self, $address, $self->{abi}, $fixed_arg_count, $ret, @args);
328 1219 100       6653 $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   1532 my($self, $name, $meta, $args, $ret) = @_;
339 2         6 $args = ['opaque','int',@$args];
340             $self->function(
341             $name, $args, $ret, sub {
342 4     4   8 my $xsub = shift;
343 4         25 $xsub->($meta, scalar(@_), @_);
344             },
345 2         10 );
346             }
347              
348              
349             sub attach
350             {
351 888     888 1 304378 my $wrapper;
352 888 100       2273 $wrapper = pop if ref $_[-1] eq 'CODE';
353              
354 888         1373 my $self = shift;
355 888         1190 my $name = shift;
356 888         1179 my $args = shift;
357 888         1219 my $varargs;
358 888 100 100     3431 $varargs = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
359 888         1414 my $ret = shift;
360 888         1232 my $proto = shift;
361              
362 888 100       1814 $ret = 'void' unless defined $ret;
363              
364 888 100       2370 my($c_name, $perl_name) = ref($name) ? @$name : ($name, $name);
365              
366 888 50       3560 croak "you tried to provide a perl name that looks like an address"
367             if $perl_name =~ /^-?[0-9]+$/;
368              
369 888 100       2481 my $function = $varargs
370             ? $self->function($c_name, $args, $varargs, $ret, $wrapper)
371             : $self->function($c_name, $args, $ret, $wrapper);
372              
373 885 100       1900 if(defined $function)
374             {
375 883         2436 $function->attach($perl_name, $proto);
376             }
377              
378 885         2102 $self;
379             }
380              
381              
382             sub closure
383             {
384 86     86 1 317077 my($self, $coderef) = @_;
385 86 100       276 return undef unless defined $coderef;
386 85 50       274 croak "not a coderef" unless ref $coderef eq 'CODE';
387 85         8616 require FFI::Platypus::Closure;
388 85         450 FFI::Platypus::Closure->new($coderef);
389             }
390              
391              
392             sub cast
393             {
394 137     137 1 43626 $_[0]->function(0 => [$_[1]] => $_[2])->call($_[3]);
395             }
396              
397              
398             sub attach_cast
399             {
400 4     4 1 2402 my($self, $name, $type1, $type2, $wrapper) = @_;
401 4         10 my $caller = caller;
402 4 50       22 $name = join '::', $caller, $name unless $name =~ /::/;
403 4 100 66     20 if(defined $wrapper && ref($wrapper) eq 'CODE')
404             {
405 1         6 $self->attach([0 => $name] => [$type1] => $type2 => '$', $wrapper);
406             }
407             else
408             {
409 3         14 $self->attach([0 => $name] => [$type1] => $type2 => '$');
410             }
411 4         14 $self;
412             }
413              
414              
415             sub sizeof
416             {
417 209     209 1 71697 my($self,$name) = @_;
418             ref $self
419 209 100       842 ? $self->{tp}->parse($name)->sizeof
420             : $self->new->sizeof($name);
421             }
422              
423              
424             sub alignof
425             {
426 74     74 1 16761 my($self, $name) = @_;
427             ref $self
428 74 100       259 ? $self->{tp}->parse($name)->alignof
429             : $self->new->alignof($name);
430             }
431              
432              
433             sub kindof
434             {
435 12     12 1 1601 my($self, $name) = @_;
436             ref $self
437 12 100       47 ? $self->{tp}->parse($name)->kindof
438             : $self->new->kindof($name);
439             }
440              
441              
442             sub countof
443             {
444 12     12 1 2073 my($self, $name) = @_;
445             ref $self
446 12 100       46 ? $self->{tp}->parse($name)->countof
447             : $self->new->countof($name);
448             }
449              
450              
451             sub def
452             {
453 10     10 1 1748 my $self = shift;
454 10   66     38 my $package = shift || caller;
455 10         19 my $type = shift;
456 10 100       27 if(@_)
457             {
458 4         14 $self->type($type);
459 3         7 $self->{def}->{$package}->{$type} = shift;
460             }
461 9         49 $self->{def}->{$package}->{$type};
462             }
463              
464              
465             sub unitof
466             {
467 12     12 1 2784 my($self, $name) = @_;
468             ref $self
469 12 100       44 ? $self->{tp}->parse($name)->unitof
470             : $self->new->unitof($name);
471             }
472              
473              
474             sub find_lib
475             {
476 7     7 1 53 my $self = shift;
477 7         556 require FFI::CheckLib;
478 7         7131 $self->lib(FFI::CheckLib::find_lib(@_));
479 7         22 $self;
480             }
481              
482              
483             sub find_symbol
484             {
485 1173     1173 1 4402 my($self, $name) = @_;
486              
487 1173   66     3193 $self->{mangler} ||= $self->{mymangler};
488              
489 1173 100       2396 unless(defined $self->{mangler})
490             {
491 144         470 my $class = _lang_class($self->{lang});
492 144 100       917 if($class->can('mangler'))
493             {
494 2         6 $self->{mangler} = $class->mangler($self->lib);
495             }
496             else
497             {
498 142     1127   941 $self->{mangler} = sub { $_[0] };
  1127         7419  
499             }
500             }
501              
502 1173         1773 foreach my $path (@{ $self->{lib} })
  1173         2857  
503             {
504 56   100 56   513 my $handle = do { no warnings; $self->{handles}->{$path||0} } || FFI::Platypus::DL::dlopen($path, FFI::Platypus::DL::RTLD_PLATYPUS_DEFAULT());
  56         137  
  56         34219  
  1204         1694  
505 1204 100       2714 unless($handle)
506             {
507             warn "warning: error loading $path: ", FFI::Platypus::DL::dlerror()
508 2 100 66     60 if $self->{api} > 0 || $ENV{FFI_PLATYPUS_DLERROR};
509 2         435 next;
510             }
511 1202         2485 my $address = FFI::Platypus::DL::dlsym($handle, $self->{mangler}->($name));
512 1202 100       2703 if($address)
513             {
514 1076   100     2857 $self->{handles}->{$path||0} = $handle;
515 1076         2614 return $address;
516             }
517             else
518             {
519 126 100 100     853 FFI::Platypus::DL::dlclose($handle) unless $self->{handles}->{$path||0};
520             }
521             }
522 97         314 return;
523             }
524              
525              
526             sub bundle
527             {
528 27 50   27 1 125 croak "bundle method only available with api => 1 or better" if $_[0]->{api} < 1;
529 27         8507 require FFI::Platypus::Bundle;
530 27         180 goto &_bundle;
531             }
532              
533              
534             sub package
535             {
536 3 100   3 1 623 croak "package method only available with api => 0" if $_[0]->{api} > 0;
537 2         1036 require FFI::Platypus::Legacy;
538 2         12 goto &_package;
539             }
540              
541              
542             sub abis
543             {
544 15     15 1 545 require FFI::Platypus::ShareConfig;
545 15         53 FFI::Platypus::ShareConfig->get("abi");
546             }
547              
548              
549             sub abi
550             {
551 13     13 1 11514 my($self, $newabi) = @_;
552 13 100       78 unless($newabi =~ /^[0-9]+$/)
553             {
554 7 100       21 unless(defined $self->abis->{$newabi})
555             {
556 1         256 croak "no such ABI: $newabi";
557             }
558 6         25 $newabi = $self->abis->{$newabi};
559             }
560              
561 12 100       113 unless(FFI::Platypus::ABI::verify($newabi))
562             {
563 1         138 croak "no such ABI: $newabi";
564             }
565              
566 11         27 $self->{abi} = $newabi;
567 11         70 $self->{tp}->abi($newabi);
568              
569 11         24 $self;
570             }
571              
572             sub DESTROY
573             {
574 249     249   166907 my($self) = @_;
575 249         453 foreach my $fini (@{ $self->{fini} })
  249         713  
576             {
577 1         16 $fini->($self);
578             }
579 249         451 foreach my $handle (values %{ $self->{handles} })
  249         777  
580             {
581 45 50       165 next unless $handle;
582 45         1566 FFI::Platypus::DL::dlclose($handle);
583             }
584 249         8431 delete $self->{handles};
585             }
586              
587             1;
588              
589             __END__