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   7643439 use strict;
  56         292  
  56         1375  
4 56     56   235 use warnings;
  56         88  
  56         1132  
5 56     56   960 use 5.008004;
  56         185  
6 56     56   257 use Carp qw( croak );
  56         175  
  56         2499  
7 56     56   18010 use FFI::Platypus::Function;
  56         108  
  56         1220  
8 56     56   25179 use FFI::Platypus::Type;
  56         114  
  56         135438  
9              
10             # ABSTRACT: Write Perl bindings to non-Perl libraries with FFI. No XS required.
11             our $VERSION = '2.07'; # 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 994148 my($class, %args) = @_;
36 351         597 my @lib;
37 351 100       961 if(exists $args{lib})
38             {
39 97 100       460 if(!ref($args{lib}))
    50          
40             {
41 2         5 push @lib, $args{lib};
42             }
43             elsif(ref($args{lib}) eq 'ARRAY')
44             {
45 95         161 push @lib, @{$args{lib}};
  95         206  
46             }
47             else
48             {
49 0         0 croak "lib argument must be a scalar or array reference";
50             }
51             }
52              
53 351   100     1267 my $api = $args{api} || 0;
54 351   50     1246 my $experimental = $args{experimental} || 0;
55              
56 351 50       1094 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     1540 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         467 my $tp;
71              
72 351 100       888 if($api == 0)
    100          
    50          
73             {
74 236         386 $tp = 'Version0';
75             }
76             elsif($api == 1)
77             {
78 50         110 $tp = 'Version1';
79             }
80             elsif($api == 2)
81             {
82 65         123 $tp = 'Version2';
83             }
84             else
85             {
86 0         0 Carp::croak("API version $api not (yet) implemented");
87             }
88              
89 351         41589 require "FFI/Platypus/TypeParser/$tp.pm";
90 351         875 $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       2315 ignore_not_found => defined $args{ignore_not_found} ? $args{ignore_not_found} : 0,
101             }, $class;
102              
103 351   100     1855 $self->lang($args{lang} || 'C');
104              
105 351         1155 $self;
106             }
107              
108             sub _lang_class ($)
109             {
110 498     498   843 my($lang) = @_;
111 498 100       1635 my $class = $lang =~ m/^=(.*)$/ ? $1 : "FFI::Platypus::Lang::$lang";
112 498 100       3061 unless($class->can('native_type_map'))
113             {
114 54         244 my $pm = "$class.pm";
115 54         260 $pm =~ s/::/\//g;
116 54         18765 require $pm;
117             }
118 498 50       2164 croak "$class does not provide native_type_map method"
119             unless $class->can("native_type_map");
120 498         982 $class;
121             }
122              
123              
124             sub lib
125             {
126 103     103 1 176861 my($self, @new) = @_;
127              
128 103 100       314 if(@new)
129             {
130 91 100       140 push @{ $self->{lib} }, map { ref $_ eq 'CODE' ? $_->() : $_ } @new;
  91         249  
  91         417  
131 91         209 delete $self->{mangler};
132             }
133              
134 103         165 @{ $self->{lib} };
  103         302  
135             }
136              
137              
138             sub ignore_not_found
139             {
140 66     66 1 156 my($self, $value) = @_;
141              
142 66 100       140 if(defined $value)
143             {
144 3         6 $self->{ignore_not_found} = $value;
145             }
146              
147 66         8838 $self->{ignore_not_found};
148             }
149              
150              
151             sub lang
152             {
153 354     354 1 1070 my($self, $value) = @_;
154              
155 354 50 33     1650 if(defined $value && $value ne $self->{lang})
156             {
157 354         643 $self->{lang} = $value;
158 354         725 my $class = _lang_class($self->{lang});
159 354 100       1380 $self->abi($class->abi) if $class->can('abi');
160              
161             {
162 354         560 my %type_map;
  354         454  
163             my $map = $class->native_type_map(
164             $self->{api} > 0
165             ? (api => $self->{api})
166 354 100       1285 : ()
167             );
168 354         2644 foreach my $key (keys %$map)
169             {
170 18805         20319 my $value = $map->{$key};
171 18805 50       27698 next unless $self->{tp}->have_type($value);
172 18805         27963 $type_map{$key} = $value;
173             }
174 354         1393 $type_map{$_} = $_ for grep { $self->{tp}->have_type($_) }
  5664         8642  
175             qw( void sint8 uint8 sint16 uint16 sint32 uint32 sint64 uint64 float double string opaque
176             longdouble complex_float complex_double );
177 354 100       2110 $type_map{pointer} = 'opaque' if $self->{tp}->isa('FFI::Platypus::TypeParser::Version0');
178 354         1253 $self->{tp}->type_map(\%type_map);
179             }
180              
181 354 100       1999 $class->load_custom_types($self) if $class->can('load_custom_types');
182             }
183              
184 354         619 $self->{lang};
185             }
186              
187              
188 3     3 1 12 sub api { shift->{api} }
189              
190              
191             sub type
192             {
193 278     278 1 38241 my($self, $name, $alias) = @_;
194 278 50 33     999 croak "usage: \$ffi->type(name => alias) (alias is optional)" unless defined $self && defined $name;
195              
196 278 100       930 $self->{tp}->check_alias($alias) if defined $alias;
197 278         618 my $type = $self->{tp}->parse($name);
198 265 100       903 $self->{tp}->set_alias($alias, $type) if defined $alias;
199              
200 265         476 $self;
201             }
202              
203              
204             sub custom_type
205             {
206 207     207 1 131538 my($self, $alias, $cb) = @_;
207              
208 207   100     823 my $argument_count = $cb->{argument_count} || 1;
209              
210 207 50       458 croak "argument_count must be >= 1"
211             unless $argument_count >= 1;
212              
213 207 50 33     757 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     732 unless defined $cb->{native_to_perl} || defined $cb->{perl_to_native} || defined $cb->{perl_to_native_post};
      66        
218              
219 207         629 $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         971 $argument_count,
227             );
228              
229 205         766 $self->{tp}->set_alias($alias, $type);
230              
231 205         418 $self;
232             }
233              
234              
235             sub load_custom_type
236             {
237 13     13 1 12508 my($self, $name, $alias, @type_args) = @_;
238              
239 13 50 33     58 croak "usage: \$ffi->load_custom_type(\$name, \$alias, ...)"
240             unless defined $name && defined $alias;
241              
242 13 50       74 $name = "FFI::Platypus::Type$name" if $name =~ /^::/;
243 13 50       44 $name = "FFI::Platypus::Type::$name" unless $name =~ /::/;
244              
245 13 100       92 unless($name->can("ffi_custom_type_api_1"))
246             {
247 3         9 my $pm = "$name.pm";
248 3         13 $pm =~ s/::/\//g;
249 3         6 eval { require $pm };
  3         1126  
250 3 50       14 warn $@ if $@;
251             }
252              
253 13 50       69 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         45 my $cb = $name->ffi_custom_type_api_1($self, @type_args);
259 13         51 $self->custom_type($alias => $cb);
260              
261 13         34 $self;
262             }
263              
264              
265             sub types
266             {
267 3     3 1 57966 my($self) = @_;
268 3 100 66     17 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  2         12  
269 3         15 sort $self->{tp}->list_types;
270             }
271              
272              
273             sub type_meta
274             {
275 131     131 1 52115 my($self, $name) = @_;
276 131 50 33     321 $self = $self->new unless ref $self && eval { $self->isa('FFI::Platypus') };
  131         568  
277 131         357 $self->{tp}->parse($name)->meta;
278             }
279              
280              
281             sub mangler
282             {
283 12     12 1 49 my($self, $sub) = @_;
284 12         65 $self->{mangler} = $self->{mymangler} = $sub;
285             }
286              
287              
288             sub function
289             {
290 1283     1283 1 157454 my $wrapper;
291 1283 100       2313 $wrapper = pop if ref $_[-1] eq 'CODE';
292              
293 1283 50 33     4098 croak "usage \$ffi->function( \$name, \\\@arguments, [\\\@var_args], [\$return_type])" unless @_ >= 3 && @_ <= 6;
294              
295 1283         1558 my $self = shift;
296 1283         1437 my $name = shift;
297 1283         1397 my $fixed_args = shift;
298 1283         1425 my $var_args;
299 1283 100 100     3357 $var_args = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
300 1283         1575 my $ret = shift;
301 1283 100       2156 $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     4405 if( (!defined $var_args) && @$fixed_args == 1 && $fixed_args->[0] eq 'void' )
      100        
306             {
307 1         2 $fixed_args = [];
308             }
309              
310 1283 100       2052 my $fixed_arg_count = defined $var_args ? scalar(@$fixed_args) : -1;
311              
312 1283 50       1960 my @args = map { $self->{tp}->parse($_) || croak "unknown type: $_" } @$fixed_args;
  1536         3570  
313 1283 100       2554 if($var_args)
314             {
315             push @args, map {
316 30         44 my $type = $self->{tp}->parse($_);
  127         201  
317             # https://github.com/PerlFFI/FFI-Platypus/issues/323
318 127 100       313 $type->type_code == 67 ? $self->{tp}->parse('double') : $type
319             } @$var_args;
320             }
321              
322 1283   33     2576 $ret = $self->{tp}->parse($ret) || croak "unknown type: $ret";
323 1283 100       4717 my $address = $name =~ /^-?[0-9]+$/ ? $name : $self->find_symbol($name);
324 1283 100 100     2587 croak "unable to find $name" unless defined $address || $self->ignore_not_found;
325 1225 100       1915 return unless defined $address;
326 1220 100       2185 $address = @args > 0 ? _cast1() : _cast0() if $address == 0;
    100          
327 1220         8632 my $function = FFI::Platypus::Function::Function->new($self, $address, $self->{abi}, $fixed_arg_count, $ret, @args);
328 1219 100       5358 $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   1302 my($self, $name, $meta, $args, $ret) = @_;
339 2         5 $args = ['opaque','int',@$args];
340             $self->function(
341             $name, $args, $ret, sub {
342 4     4   7 my $xsub = shift;
343 4         20 $xsub->($meta, scalar(@_), @_);
344             },
345 2         10 );
346             }
347              
348              
349             sub attach
350             {
351 888     888 1 263668 my $wrapper;
352 888 100       1839 $wrapper = pop if ref $_[-1] eq 'CODE';
353              
354 888         1157 my $self = shift;
355 888         1028 my $name = shift;
356 888         979 my $args = shift;
357 888         955 my $varargs;
358 888 100 100     2732 $varargs = shift if defined $_[0] && ref($_[0]) eq 'ARRAY';
359 888         1213 my $ret = shift;
360 888         1004 my $proto = shift;
361              
362 888 100       1461 $ret = 'void' unless defined $ret;
363              
364 888 100       1952 my($c_name, $perl_name) = ref($name) ? @$name : ($name, $name);
365              
366 888 50       2866 croak "you tried to provide a perl name that looks like an address"
367             if $perl_name =~ /^-?[0-9]+$/;
368              
369 888 100       2050 my $function = $varargs
370             ? $self->function($c_name, $args, $varargs, $ret, $wrapper)
371             : $self->function($c_name, $args, $ret, $wrapper);
372              
373 885 100       1498 if(defined $function)
374             {
375 883         2065 $function->attach($perl_name, $proto);
376             }
377              
378 885         1678 $self;
379             }
380              
381              
382             sub closure
383             {
384 86     86 1 261499 my($self, $coderef) = @_;
385 86 100       246 return undef unless defined $coderef;
386 85 50       264 croak "not a coderef" unless ref $coderef eq 'CODE';
387 85         7339 require FFI::Platypus::Closure;
388 85         379 FFI::Platypus::Closure->new($coderef);
389             }
390              
391              
392             sub cast
393             {
394 137     137 1 37108 $_[0]->function(0 => [$_[1]] => $_[2])->call($_[3]);
395             }
396              
397              
398             sub attach_cast
399             {
400 4     4 1 1816 my($self, $name, $type1, $type2, $wrapper) = @_;
401 4         9 my $caller = caller;
402 4 50       16 $name = join '::', $caller, $name unless $name =~ /::/;
403 4 100 66     16 if(defined $wrapper && ref($wrapper) eq 'CODE')
404             {
405 1         5 $self->attach([0 => $name] => [$type1] => $type2 => '$', $wrapper);
406             }
407             else
408             {
409 3         11 $self->attach([0 => $name] => [$type1] => $type2 => '$');
410             }
411 4         11 $self;
412             }
413              
414              
415             sub sizeof
416             {
417 209     209 1 55515 my($self,$name) = @_;
418             ref $self
419 209 100       673 ? $self->{tp}->parse($name)->sizeof
420             : $self->new->sizeof($name);
421             }
422              
423              
424             sub alignof
425             {
426 74     74 1 13370 my($self, $name) = @_;
427             ref $self
428 74 100       219 ? $self->{tp}->parse($name)->alignof
429             : $self->new->alignof($name);
430             }
431              
432              
433             sub kindof
434             {
435 12     12 1 1281 my($self, $name) = @_;
436             ref $self
437 12 100       39 ? $self->{tp}->parse($name)->kindof
438             : $self->new->kindof($name);
439             }
440              
441              
442             sub countof
443             {
444 12     12 1 1604 my($self, $name) = @_;
445             ref $self
446 12 100       37 ? $self->{tp}->parse($name)->countof
447             : $self->new->countof($name);
448             }
449              
450              
451             sub def
452             {
453 10     10 1 1378 my $self = shift;
454 10   66     28 my $package = shift || caller;
455 10         12 my $type = shift;
456 10 100       21 if(@_)
457             {
458 4         10 $self->type($type);
459 3         7 $self->{def}->{$package}->{$type} = shift;
460             }
461 9         33 $self->{def}->{$package}->{$type};
462             }
463              
464              
465             sub unitof
466             {
467 12     12 1 2204 my($self, $name) = @_;
468             ref $self
469 12 100       41 ? $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         432 require FFI::CheckLib;
478 7         5692 $self->lib(FFI::CheckLib::find_lib(@_));
479 7         21 $self;
480             }
481              
482              
483             sub find_symbol
484             {
485 1173     1173 1 3554 my($self, $name) = @_;
486              
487 1173   66     2630 $self->{mangler} ||= $self->{mymangler};
488              
489 1173 100       1995 unless(defined $self->{mangler})
490             {
491 144         411 my $class = _lang_class($self->{lang});
492 144 100       720 if($class->can('mangler'))
493             {
494 2         11 $self->{mangler} = $class->mangler($self->lib);
495             }
496             else
497             {
498 142     1127   833 $self->{mangler} = sub { $_[0] };
  1127         6450  
499             }
500             }
501              
502 1173         1486 foreach my $path (@{ $self->{lib} })
  1173         2354  
503             {
504 56   100 56   492 my $handle = do { no warnings; $self->{handles}->{$path||0} } || FFI::Platypus::DL::dlopen($path, FFI::Platypus::DL::RTLD_PLATYPUS_DEFAULT());
  56         118  
  56         28222  
  1204         1319  
505 1204 100       2332 unless($handle)
506             {
507             warn "warning: error loading $path: ", FFI::Platypus::DL::dlerror()
508 2 100 66     41 if $self->{api} > 0 || $ENV{FFI_PLATYPUS_DLERROR};
509 2         365 next;
510             }
511 1202         2162 my $address = FFI::Platypus::DL::dlsym($handle, $self->{mangler}->($name));
512 1202 100       2366 if($address)
513             {
514 1076   100     2324 $self->{handles}->{$path||0} = $handle;
515 1076         2269 return $address;
516             }
517             else
518             {
519 126 100 100     690 FFI::Platypus::DL::dlclose($handle) unless $self->{handles}->{$path||0};
520             }
521             }
522 97         253 return;
523             }
524              
525              
526             sub bundle
527             {
528 27 50   27 1 109 croak "bundle method only available with api => 1 or better" if $_[0]->{api} < 1;
529 27         6866 require FFI::Platypus::Bundle;
530 27         151 goto &_bundle;
531             }
532              
533              
534             sub package
535             {
536 3 100   3 1 525 croak "package method only available with api => 0" if $_[0]->{api} > 0;
537 2         814 require FFI::Platypus::Legacy;
538 2         10 goto &_package;
539             }
540              
541              
542             sub abis
543             {
544 15     15 1 522 require FFI::Platypus::ShareConfig;
545 15         43 FFI::Platypus::ShareConfig->get("abi");
546             }
547              
548              
549             sub abi
550             {
551 13     13 1 8129 my($self, $newabi) = @_;
552 13 100       59 unless($newabi =~ /^[0-9]+$/)
553             {
554 7 100       18 unless(defined $self->abis->{$newabi})
555             {
556 1         197 croak "no such ABI: $newabi";
557             }
558 6         22 $newabi = $self->abis->{$newabi};
559             }
560              
561 12 100       108 unless(FFI::Platypus::ABI::verify($newabi))
562             {
563 1         76 croak "no such ABI: $newabi";
564             }
565              
566 11         25 $self->{abi} = $newabi;
567 11         44 $self->{tp}->abi($newabi);
568              
569 11         20 $self;
570             }
571              
572             sub DESTROY
573             {
574 249     249   136672 my($self) = @_;
575 249         377 foreach my $fini (@{ $self->{fini} })
  249         606  
576             {
577 1         8 $fini->($self);
578             }
579 249         382 foreach my $handle (values %{ $self->{handles} })
  249         618  
580             {
581 45 50       135 next unless $handle;
582 45         1382 FFI::Platypus::DL::dlclose($handle);
583             }
584 249         7085 delete $self->{handles};
585             }
586              
587             1;
588              
589             __END__