File Coverage

blib/lib/Wasm/Wasmtime/FFI.pm
Criterion Covered Total %
statement 106 107 99.0
branch 18 22 81.8
condition 5 6 83.3
subroutine 27 27 100.0
pod n/a
total 156 162 96.3


line stmt bran cond sub pod time code
1             package Wasm::Wasmtime::FFI;
2              
3 35     35   461983 use strict;
  35         82  
  35         1021  
4 35     35   229 use warnings;
  35         85  
  35         773  
5 35     35   544 use 5.008004;
  35         170  
6 35     35   15694 use FFI::C 0.05;
  35         76100  
  35         1298  
7 35     35   11435 use FFI::C::Util ();
  35         156618  
  35         1073  
8 35     35   17268 use FFI::Platypus 1.26;
  35         196441  
  35         956  
9 35     35   16333 use FFI::Platypus::Buffer ();
  35         20655  
  35         1069  
10 35     35   16692 use FFI::CheckLib 0.26 qw( find_lib );
  35         252860  
  35         2407  
11 35     35   10743 use Sub::Install;
  35         42743  
  35         213  
12 35     35   15591 use Devel::GlobalDestruction ();
  35         70306  
  35         723  
13 35     35   227 use constant ();
  35         79  
  35         646  
14 35     35   199 use base qw( Exporter );
  35         78  
  35         11545  
15              
16             # ABSTRACT: Private class for Wasm::Wasmtime
17             our $VERSION = '0.23'; # VERSION
18              
19              
20             our @EXPORT = qw( $ffi $ffi_prefix _generate_vec_class _generate_destroy );
21              
22             sub _lib
23             {
24 35 50   35   354 return $ENV{WASM_WASMTIME_FFI} if defined $ENV{WASM_WASMTIME_FFI};
25 35         180 my @symbols = (
26             # 0.19.0
27             'wasmtime_func_as_funcref',
28             # 0.20.0 / 0.21.0
29             'wasmtime_module_serialize',
30             'wasmtime_module_deserialize',
31             'wasmtime_store_gc',
32             ## 0.23.0
33             'wasmtime_config_consume_fuel_set',
34             #'wasmtime_config_max_instances_set', # removed in 0.27.0
35             );
36 35         153 my $lib = find_lib lib => 'wasmtime', symbol => \@symbols;
37 35 50       283303 return $lib if $lib;
38 35         254 $lib = find_lib lib => 'wasmtime', alien => 'Alien::wasmtime', symbol => \@symbols;
39 35 50       2807737 return $lib if $lib;
40 0         0 die 'unable to find wasmtime 0.19.0 or better';
41             }
42              
43             our $ffi_prefix = 'wasm_';
44             our $ffi = FFI::Platypus->new( api => 1 );
45             FFI::C->ffi($ffi);
46             $ffi->lib(__PACKAGE__->_lib);
47             $ffi->mangler(sub {
48             my $name = shift;
49             return $name if $name =~ /^(wasm|wasmtime|wasi)_/;
50             return $ffi_prefix . $name;
51             });
52              
53             { package Wasm::Wasmtime::Vec;
54 35     35   16258 use FFI::Platypus::Record;
  35         53977  
  35         4097  
55             record_layout_1(
56             $ffi,
57             size_t => 'size',
58             opaque => 'data',
59             );
60             }
61              
62             { package Wasm::Wasmtime::ByteVec;
63 35     35   327 use base qw( Wasm::Wasmtime::Vec );
  35         86  
  35         28016  
64              
65             $ffi->type('record(Wasm::Wasmtime::ByteVec)' => 'wasm_byte_vec_t');
66             $ffi_prefix = 'wasm_byte_vec_';
67              
68             sub new
69             {
70 247     247   556 my $class = shift;
71 247 100       707 if(@_ == 1)
72             {
73 169         563 my($data, $size) = FFI::Platypus::Buffer::scalar_to_buffer($_[0]);
74 169         2055 return $class->SUPER::new(
75             size => $size,
76             data => $data,
77             );
78             }
79             else
80             {
81 78         263 return $class->SUPER::new(@_);
82             }
83             }
84              
85             sub get
86             {
87 303     303   7878 my($self) = @_;
88 303         1237 FFI::Platypus::Buffer::buffer_to_scalar($self->data, $self->size);
89             }
90              
91             $ffi->attach( delete => ['wasm_byte_vec_t*'] => 'void' );
92             }
93              
94             sub _generic_vec_delete
95             {
96 180     180   1114 my($xsub, $self) = @_;
97 180         816 $xsub->($self);
98             # cannot use SUPER::DELETE because we aren't
99             # in the right package.
100 180         520 Wasm::Wasmtime::Vec::DESTROY($self);
101             }
102              
103             sub _generate_vec_class
104             {
105 87     87   327 my %opts = @_;
106 87         265 my($class) = caller;
107 87         199 my $type = $class;
108 87         441 $type =~ s/^.*:://;
109 87         201 my $v_type = "wasm_@{[ lc $type ]}_vec_t";
  87         389  
110 87         313 my $vclass = "Wasm::Wasmtime::${type}Vec";
111 87         177 my $prefix = "wasm_@{[ lc $type ]}_vec";
  87         318  
112              
113             Sub::Install::install_sub({
114             code => sub {
115 334     334   1822 my($self) = @_;
116 334         1010 my $size = $self->size;
117 334 100       1108 return () if $size == 0;
118 218         1308 my $ptrs = $ffi->cast('opaque', "opaque[$size]", $self->data);
119 218         24647 map { $class->new($_, $self) } @$ptrs;
  435         1469  
120             },
121 87         877 into => $vclass,
122             as => 'to_list',
123             });
124              
125             {
126 35     35   307 no strict 'refs';
  35         139  
  35         39986  
  87         5907  
127 87         167 @{join '::', $vclass, 'ISA'} = ('Wasm::Wasmtime::Vec');
  87         1985  
128             }
129 87         458 $ffi_prefix = "${prefix}_";
130 87         574 $ffi->type("record($vclass)" => $v_type);
131             $ffi->attach( [ delete => join('::', $vclass, 'DESTROY') ] => ["$v_type*"] => \&_generic_vec_delete)
132 87 100 66     10703 if !defined($opts{delete}) || $opts{delete};
133              
134             }
135              
136             sub _wrapper_destroy
137             {
138 840     840   101787 my($xsub, $self) = @_;
139 840 50       19795 return if Devel::GlobalDestruction::in_global_destruction();
140 840 100 100     9525 if(defined $self->{ptr} && !defined $self->{owner})
141             {
142 197         927 $xsub->($self);
143 197         5292 delete $self->{ptr};
144             }
145             }
146              
147             sub _generate_destroy
148             {
149 401     401   917 my $caller = caller;
150 401         1082 my $type = lc $caller;
151 401 100       1627 if($type =~ /::linker$/)
    100          
152             {
153 9         31 $type = 'wasmtime_linker_t';
154             }
155             elsif($type =~ /::wasi/)
156             {
157 21         236 $type =~ s/^.*::wasi(.*)$/wasi_${1}_t/g;
158             }
159             else
160             {
161 371         1831 $type =~ s/^.*:://;
162 371         950 $type = "wasm_${type}_t";
163             }
164 401         2191 $ffi->attach( [ delete => join('::', $caller, 'DESTROY') ] => [ $type ] => \&_wrapper_destroy);
165             }
166              
167             { package Wasm::Wasmtime::Error;
168              
169             $ffi_prefix = 'wasmtime_error_';
170             $ffi->custom_type(
171             wasmtime_error_t => {
172             native_type => 'opaque',
173             native_to_perl => sub {
174             defined $_[0] ? __PACKAGE__->new($_[0]) : undef
175             },
176             },
177             );
178              
179             Sub::Install::install_sub({
180             code => sub {
181 8     8   39 my($class, $ptr, $owner) = @_;
182 8         65 bless {
183             ptr => $ptr,
184             owner => $owner,
185             }, $class;
186             },
187             into => __PACKAGE__,
188             as => 'new',
189             });
190              
191             $ffi->attach( message => ['wasmtime_error_t','wasm_byte_vec_t*'] => sub {
192             my($xsub, $self) = @_;
193             my $message = Wasm::Wasmtime::ByteVec->new;
194             $xsub->($self->{ptr}, $message);
195             my $ret = $message->get;
196             $message->delete;
197             $ret;
198             });
199              
200             $ffi->attach( [ delete => "DESTROY" ] => ['wasmtime_error_t'] => sub {
201             my($xsub, $self) = @_;
202             if(defined $self->{ptr} && !defined $self->{owner})
203             {
204             $xsub->($self->{ptr});
205             }
206             });
207             }
208              
209             { package Wasm::Wasmtime::Val::Of;
210             FFI::C->union(of_t => [
211             i32 => 'sint32',
212             i64 => 'sint64',
213             f32 => 'float',
214             f64 => 'double',
215             anyref => 'opaque',
216             funcref => 'opaque',
217             ]);
218             }
219              
220             my %kind = (
221             0 => 'i32',
222             1 => 'i64',
223             2 => 'f32',
224             3 => 'f64',
225             128 => 'anyref',
226             129 => 'funcref',
227             );
228              
229             { package Wasm::Wasmtime::Val;
230             FFI::C->struct(wasm_val_t => [
231             kind => 'uint8',
232             of => 'of_t',
233             ]);
234              
235             sub to_perl
236             {
237 59     59   105 my $self = shift;
238 59         156 my $kind = $kind{$self->kind};
239 59         720 $self->of->$kind;
240             }
241             }
242              
243             { package Wasm::Wasmtime::ValVec;
244             FFI::C->array(wasm_val_vec_t => [
245             'wasm_val_t',
246             ], { nullable => 1 });
247              
248             sub to_perl
249             {
250 38     38   69 my $self = shift;
251 38         121 map { $_->to_perl } @$self
  48         2283  
252             }
253              
254             {
255             package Wasm::Wasmtime::ValVecWrapper;
256             FFI::C->struct(wasm_val_vec_wrapper_t => [
257             size => 'size_t',
258             data => 'opaque',
259             ]);
260              
261             }
262              
263             $ffi->attach_cast('from_c', 'opaque', 'wasm_val_vec_wrapper_t', sub {
264             my($xsub, undef, $ptr) = @_;
265             my $wrapper = $xsub->($ptr);
266             my $inner = $ffi->cast('opaque', 'wasm_val_vec_t', $wrapper->data);
267             FFI::C::Util::set_array_count($inner, $wrapper->size);
268             return $inner;
269             });
270              
271             sub from_perl
272             {
273 53     53   151 my($class, $vals, $types) = @_;
274 53 100       178 @$vals ? $class->new([map { { kind => $_->kind_num, of => { $_->kind => shift @$vals } } } @$types]) : undef;
  70         621  
275             }
276             }
277              
278             1;
279              
280             __END__