File Coverage

blib/lib/FFI/Platypus.pm
Criterion Covered Total %
statement 247 253 97.6
branch 136 160 85.0
condition 53 78 67.9
subroutine 40 40 100.0
pod 28 28 100.0
total 504 559 90.1


line stmt bran cond sub pod time code
1             package FFI::Platypus;
2              
3 56     56   8374753 use strict;
  56         143  
  56         8443  
4 56     56   501 use warnings;
  56         146  
  56         3115  
5 56     56   1136 use 5.008004;
  56         224  
6 56     56   339 use Carp qw( croak );
  56         112  
  56         3752  
7 56     56   28498 use FFI::Platypus::Function;
  56         187  
  56         1998  
8 56     56   35427 use FFI::Platypus::Type;
  56         175  
  56         201187  
9              
10             # ABSTRACT: Write Perl bindings to non-Perl libraries with FFI. No XS required.
11             our $VERSION = '2.11'; # 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 349     349 1 12016104 my($class, %args) = @_;
36 349         945 my @lib;
37 349 100       1591 if(exists $args{lib})
38             {
39 97 100       604 if(!ref($args{lib}))
    50          
40             {
41 2         7 push @lib, $args{lib};
42             }
43             elsif(ref($args{lib}) eq 'ARRAY')
44             {
45 95         191 push @lib, @{$args{lib}};
  95         317  
46             }
47             else
48             {
49 0         0 croak "lib argument must be a scalar or array reference";
50             }
51             }
52              
53 349   100     1949 my $api = $args{api} || 0;
54 349   50     1756 my $experimental = $args{experimental} || 0;
55              
56 349 50       2923 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 349 50 33     5965 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 349         685 my $tp;
71              
72 349 100       1238 if($api == 0)
    100          
    50          
73             {
74 234         517 $tp = 'Version0';
75             }
76             elsif($api == 1)
77             {
78 50         140 $tp = 'Version1';
79             }
80             elsif($api == 2)
81             {
82 65         146 $tp = 'Version2';
83             }
84             else
85             {
86 0         0 Carp::croak("API version $api not (yet) implemented");
87             }
88              
89 349         64247 require "FFI/Platypus/TypeParser/$tp.pm";
90 349         1124 $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 349 100       3660 ignore_not_found => defined $args{ignore_not_found} ? $args{ignore_not_found} : 0,
101             }, $class;
102              
103 349   100     3943 $self->lang($args{lang} || 'C');
104              
105 349         1830 $self;
106             }
107              
108             sub _lang_class ($)
109             {
110 494     494   1100 my($lang) = @_;
111 494 100       2330 my $class = $lang =~ m/^=(.*)$/ ? $1 : "FFI::Platypus::Lang::$lang";
112 494 100       4613 unless($class->can('native_type_map'))
113             {
114 53         132 my $pm = "$class.pm";
115 53         391 $pm =~ s/::/\//g;
116 53         27924 require $pm;
117             }
118 494 50       3062 croak "$class does not provide native_type_map method"
119             unless $class->can("native_type_map");
120 494         1425 $class;
121             }
122              
123              
124             sub lib
125             {
126 101     101 1 157261 my($self, @new) = @_;
127              
128 101 100       372 if(@new)
129             {
130 89 100       184 push @{ $self->{lib} }, map { ref $_ eq 'CODE' ? $_->() : $_ } @new;
  89         460  
  89         508  
131 89         251 delete $self->{mangler};
132             }
133              
134 101         199 @{ $self->{lib} };
  101         411  
135             }
136              
137              
138             sub ignore_not_found
139             {
140 66     66 1 148 my($self, $value) = @_;
141              
142 66 100       192 if(defined $value)
143             {
144 3         5 $self->{ignore_not_found} = $value;
145             }
146              
147 66         12763 $self->{ignore_not_found};
148             }
149              
150              
151             sub lang
152             {
153 352     352 1 1565 my($self, $value) = @_;
154              
155 352 50 33     2685 if(defined $value && $value ne $self->{lang})
156             {
157 352         930 $self->{lang} = $value;
158 352         1834 my $class = _lang_class($self->{lang});
159 352 100       1868 $self->abi($class->abi) if $class->can('abi');
160              
161             {
162 352         643 my %type_map;
  352         661  
163             my $map = $class->native_type_map(
164             $self->{api} > 0
165             ? (api => $self->{api})
166 352 100       1993 : ()
167             );
168 352         3555 foreach my $key (keys %$map)
169             {
170 18695         27977 my $value = $map->{$key};
171 18695 100       38516 next unless $self->{tp}->have_type($value);
172 18357         135252 $type_map{$key} = $value;
173             }
174 352         2314 $type_map{$_} = $_ for grep { $self->{tp}->have_type($_) }
  5632         11062  
175             qw( void sint8 uint8 sint16 uint16 sint32 uint32 sint64 uint64 float double string opaque
176             longdouble complex_float complex_double );
177 352 100       3337 $type_map{pointer} = 'opaque' if $self->{tp}->isa('FFI::Platypus::TypeParser::Version0');
178 352         1865 $self->{tp}->type_map(\%type_map);
179             }
180              
181 352 100       3216 $class->load_custom_types($self) if $class->can('load_custom_types');
182             }
183              
184 352         962 $self->{lang};
185             }
186              
187              
188 3     3 1 15 sub api { shift->{api} }
189              
190              
191             sub type
192             {
193 275     275 1 93995 my($self, $name, $alias) = @_;
194 275 50 33     1431 croak "usage: \$ffi->type(name => alias) (alias is optional)" unless defined $self && defined $name;
195              
196 275 100       1270 $self->{tp}->check_alias($alias) if defined $alias;
197 275         980 my $type = $self->{tp}->parse($name);
198 262 100       1229 $self->{tp}->set_alias($alias, $type) if defined $alias;
199              
200 262         627 $self;
201             }
202              
203              
204             sub custom_type
205             {
206 207     207 1 176672 my($self, $alias, $cb) = @_;
207              
208 207   100     1216 my $argument_count = $cb->{argument_count} || 1;
209              
210 207 50       677 croak "argument_count must be >= 1"
211             unless $argument_count >= 1;
212              
213 207 50 33     1041 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     924 unless defined $cb->{native_to_perl} || defined $cb->{perl_to_native} || defined $cb->{perl_to_native_post};
      66        
218              
219 207         959 $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         1473 $argument_count,
227             );
228              
229 205         1017 $self->{tp}->set_alias($alias, $type);
230              
231 205         683 $self;
232             }
233              
234              
235             sub load_custom_type
236             {
237 13     13 1 13523 my($self, $name, $alias, @type_args) = @_;
238              
239 13 50 33     79 croak "usage: \$ffi->load_custom_type(\$name, \$alias, ...)"
240             unless defined $name && defined $alias;
241              
242 13 50       77 $name = "FFI::Platypus::Type$name" if $name =~ /^::/;
243 13 50       52 $name = "FFI::Platypus::Type::$name" unless $name =~ /::/;
244              
245 13 100       150 unless($name->can("ffi_custom_type_api_1"))
246             {
247 3         8 my $pm = "$name.pm";
248 3         16 $pm =~ s/::/\//g;
249 3         7 eval { require $pm };
  3         1753  
250 3 50       14 warn $@ if $@;
251             }
252              
253 13 50       82 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         48 my $cb = $name->ffi_custom_type_api_1($self, @type_args);
259 13         63 $self->custom_type($alias => $cb);
260              
261 13         49 $self;
262             }
263              
264              
265             sub types
266             {
267 3     3 1 126565 my($self) = @_;
268 3 100 66     34 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  2         25  
269 3         27 sort $self->{tp}->list_types;
270             }
271              
272              
273             sub type_meta
274             {
275 131     131 1 92734 my($self, $name) = @_;
276 131 50 33     543 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  131         976  
277 131         580 $self->{tp}->parse($name)->meta;
278             }
279              
280              
281             sub mangler
282             {
283 12     12 1 62 my($self, $sub) = @_;
284 12         66 $self->{mangler} = $self->{mymangler} = $sub;
285             }
286              
287              
288             sub function
289             {
290 1270     1270 1 230788 my $wrapper;
291 1270 100       3045 $wrapper = pop if ref $_[-1] eq 'CODE';
292              
293 1270 50 33     8567 croak "usage \$ffi->function( \$name, \\\@arguments, [\\\@var_args], [\$return_type])" unless @_ >= 3 && @_ <= 6;
294              
295 1270         1922 my $self = shift;
296 1270         1981 my $name = shift;
297 1270         1744 my $fixed_args = shift;
298 1270         1776 my $var_args;
299 1270 100 100     4805 $var_args = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
300 1270         2347 my $ret = shift;
301 1270 100       2402 $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 1270 100 100     5805 if( (!defined $var_args) && @$fixed_args == 1 && $fixed_args->[0] eq 'void' )
      100        
306             {
307 1         2 $fixed_args = [];
308             }
309              
310 1270 100       2656 my $fixed_arg_count = defined $var_args ? scalar(@$fixed_args) : -1;
311              
312 1270 50       2423 my @args = map { $self->{tp}->parse($_) || croak "unknown type: $_" } @$fixed_args;
  1524         5081  
313 1270 100       2642 if($var_args)
314             {
315             push @args, map {
316 30         64 my $type = $self->{tp}->parse($_);
  127         310  
317             # https://github.com/PerlFFI/FFI-Platypus/issues/323
318 127 100       497 $type->type_code == 67 ? $self->{tp}->parse('double') : $type
319             } @$var_args;
320             }
321              
322 1270   33     3405 $ret = $self->{tp}->parse($ret) || croak "unknown type: $ret";
323 1270 100       6923 my $address = $name =~ /^-?[0-9]+$/ ? $name : $self->find_symbol($name);
324 1270 100 100     3237 croak "unable to find $name" unless defined $address || $self->ignore_not_found;
325 1212 100       2955 return unless defined $address;
326 1207 100       2724 $address = @args > 0 ? _cast1() : _cast0() if $address == 0;
    100          
327 1207         11853 my $function = FFI::Platypus::Function::Function->new($self, $address, $self->{abi}, $fixed_arg_count, $ret, @args);
328 1206 100       6705 $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   2235 my($self, $name, $meta, $args, $ret) = @_;
339 2         7 $args = ['opaque','int',@$args];
340             $self->function(
341             $name, $args, $ret, sub {
342 4     4   30 my $xsub = shift;
343 4         58 $xsub->($meta, scalar(@_), @_);
344             },
345 2         15 );
346             }
347              
348              
349             sub attach
350             {
351 880     880 1 392863 my $wrapper;
352 880 100       2298 $wrapper = pop if ref $_[-1] eq 'CODE';
353              
354 880         1397 my $self = shift;
355 880         1286 my $name = shift;
356 880         1168 my $args = shift;
357 880         1154 my $varargs;
358 880 100 100     3318 $varargs = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
359 880         1306 my $ret = shift;
360 880         1265 my $proto = shift;
361              
362 880 100       1949 $ret = 'void' unless defined $ret;
363              
364 880 100       2411 my($c_name, $perl_name) = ref($name) ? @$name : ($name, $name);
365              
366 880 50       3788 croak "you tried to provide a perl name that looks like an address"
367             if $perl_name =~ /^-?[0-9]+$/;
368              
369 880 100       2610 my $function = $varargs
370             ? $self->function($c_name, $args, $varargs, $ret, $wrapper)
371             : $self->function($c_name, $args, $ret, $wrapper);
372              
373 877 100       1812 if(defined $function)
374             {
375 875         3952 $function->attach($perl_name, $proto);
376             }
377              
378 877         2335 $self;
379             }
380              
381              
382             sub closure
383             {
384 86     86 1 333750 my($self, $coderef) = @_;
385 86 100       312 return undef unless defined $coderef;
386 85 50       302 croak "not a coderef" unless ref $coderef eq 'CODE';
387 85         10538 require FFI::Platypus::Closure;
388 85         497 FFI::Platypus::Closure->new($coderef);
389             }
390              
391              
392             sub cast
393             {
394 137     137 1 59848 $_[0]->function(0 => [$_[1]] => $_[2])->call($_[3]);
395             }
396              
397              
398             sub attach_cast
399             {
400 4     4 1 3097 my($self, $name, $type1, $type2, $wrapper) = @_;
401 4         12 my $caller = caller;
402 4 50       29 $name = join '::', $caller, $name unless $name =~ /::/;
403 4 100 66     25 if(defined $wrapper && ref($wrapper) eq 'CODE')
404             {
405 1         6 $self->attach([0 => $name] => [$type1] => $type2 => '$', $wrapper);
406             }
407             else
408             {
409 3         18 $self->attach([0 => $name] => [$type1] => $type2 => '$');
410             }
411 4         17 $self;
412             }
413              
414              
415             sub sizeof
416             {
417 209     209 1 87351 my($self,$name) = @_;
418             ref $self
419 209 100       1066 ? $self->{tp}->parse($name)->sizeof
420             : $self->new->sizeof($name);
421             }
422              
423              
424             sub alignof
425             {
426 74     74 1 21386 my($self, $name) = @_;
427             ref $self
428 74 100       384 ? $self->{tp}->parse($name)->alignof
429             : $self->new->alignof($name);
430             }
431              
432              
433             sub kindof
434             {
435 12     12 1 2020 my($self, $name) = @_;
436             ref $self
437 12 100       62 ? $self->{tp}->parse($name)->kindof
438             : $self->new->kindof($name);
439             }
440              
441              
442             sub countof
443             {
444 12     12 1 2699 my($self, $name) = @_;
445             ref $self
446 12 100       57 ? $self->{tp}->parse($name)->countof
447             : $self->new->countof($name);
448             }
449              
450              
451             sub def
452             {
453 10     10 1 2429 my $self = shift;
454 10   66     68 my $package = shift || caller;
455 10         20 my $type = shift;
456 10 100       33 if(@_)
457             {
458 4         16 $self->type($type);
459 3         12 $self->{def}->{$package}->{$type} = shift;
460             }
461 9         60 $self->{def}->{$package}->{$type};
462             }
463              
464              
465             sub unitof
466             {
467 12     12 1 3678 my($self, $name) = @_;
468             ref $self
469 12 100       54 ? $self->{tp}->parse($name)->unitof
470             : $self->new->unitof($name);
471             }
472              
473              
474             sub find_lib
475             {
476 7     7 1 45 my $self = shift;
477 7         660 require FFI::CheckLib;
478 7         11246 $self->lib(FFI::CheckLib::find_lib(@_));
479 7         23 $self;
480             }
481              
482              
483             sub find_symbol
484             {
485 1160     1160 1 4715 my($self, $name) = @_;
486              
487 1160   66     3278 $self->{mangler} ||= $self->{mymangler};
488              
489 1160 100       2512 unless(defined $self->{mangler})
490             {
491 142         522 my $class = _lang_class($self->{lang});
492 142 100       887 if($class->can('mangler'))
493             {
494 2         6 $self->{mangler} = $class->mangler($self->lib);
495             }
496             else
497             {
498 140     1114   940 $self->{mangler} = sub { $_[0] };
  1114         6247  
499             }
500             }
501              
502 1160         1736 foreach my $path (@{ $self->{lib} })
  1160         2994  
503             {
504 56   100 56   530 my $handle = do { no warnings; $self->{handles}->{$path||0} } || FFI::Platypus::DL::dlopen($path, FFI::Platypus::DL::RTLD_PLATYPUS_DEFAULT());
  56         134  
  56         43328  
  1191         1525  
505 1191 100       2860 unless($handle)
506             {
507             warn "warning: error loading $path: ", FFI::Platypus::DL::dlerror()
508 2 100 66     50 if $self->{api} > 0 || $ENV{FFI_PLATYPUS_DLERROR};
509 2         638 next;
510             }
511 1189         2909 my $address = FFI::Platypus::DL::dlsym($handle, $self->{mangler}->($name));
512 1189 100       2840 if($address)
513             {
514 1063   100     3028 $self->{handles}->{$path||0} = $handle;
515 1063         2561 return $address;
516             }
517             else
518             {
519 126 100 100     714 FFI::Platypus::DL::dlclose($handle) unless $self->{handles}->{$path||0};
520             }
521             }
522 97         303 return;
523             }
524              
525              
526             sub bundle
527             {
528 27 50   27 1 159 croak "bundle method only available with api => 1 or better" if $_[0]->{api} < 1;
529 27         10866 require FFI::Platypus::Bundle;
530 27         190 goto &_bundle;
531             }
532              
533              
534             sub package
535             {
536 3 100   3 1 975 croak "package method only available with api => 0" if $_[0]->{api} > 0;
537 2         1492 require FFI::Platypus::Legacy;
538 2         15 goto &_package;
539             }
540              
541              
542             sub abis
543             {
544 15     15 1 509 require FFI::Platypus::ShareConfig;
545 15         62 FFI::Platypus::ShareConfig->get("abi");
546             }
547              
548              
549             sub abi
550             {
551 13     13 1 12435 my($self, $newabi) = @_;
552 13 100       88 unless($newabi =~ /^[0-9]+$/)
553             {
554 7 100       21 unless(defined $self->abis->{$newabi})
555             {
556 1         240 croak "no such ABI: $newabi";
557             }
558 6         28 $newabi = $self->abis->{$newabi};
559             }
560              
561 12 100       222 unless(FFI::Platypus::ABI::verify($newabi))
562             {
563 1         144 croak "no such ABI: $newabi";
564             }
565              
566 11         36 $self->{abi} = $newabi;
567 11         90 $self->{tp}->abi($newabi);
568              
569 11         27 $self;
570             }
571              
572             sub DESTROY
573             {
574 248     248   207844 my($self) = @_;
575 248         641 foreach my $fini (@{ $self->{fini} })
  248         1020  
576             {
577 1         14 $fini->($self);
578             }
579 248         668 foreach my $handle (values %{ $self->{handles} })
  248         1008  
580             {
581 44 50       191 next unless $handle;
582 44         2771 FFI::Platypus::DL::dlclose($handle);
583             }
584 248         15315 delete $self->{handles};
585             }
586              
587             1;
588              
589             __END__