File Coverage

blib/lib/Wasm/Wasmtime/FFI.pm
Criterion Covered Total %
statement 99 100 99.0
branch 17 20 85.0
condition 5 6 83.3
subroutine 25 25 100.0
pod n/a
total 146 151 96.6


line stmt bran cond sub pod time code
1             package Wasm::Wasmtime::FFI;
2              
3 35     35   447984 use strict;
  35         76  
  35         959  
4 35     35   165 use warnings;
  35         74  
  35         733  
5 35     35   518 use 5.008004;
  35         159  
6 35     35   15220 use FFI::C 0.05;
  35         72120  
  35         1092  
7 35     35   15887 use FFI::Platypus 1.26;
  35         167884  
  35         1230  
8 35     35   16052 use FFI::Platypus::Buffer ();
  35         21859  
  35         1013  
9 35     35   15574 use FFI::CheckLib 0.26 qw( find_lib );
  35         96393  
  35         2253  
10 35     35   10862 use Sub::Install;
  35         41096  
  35         200  
11 35     35   15463 use Devel::GlobalDestruction ();
  35         66797  
  35         865  
12 35     35   264 use base qw( Exporter );
  35         66  
  35         10525  
13              
14             # ABSTRACT: Private class for Wasm::Wasmtime
15             our $VERSION = '0.21'; # VERSION
16              
17              
18             our @EXPORT = qw( $ffi $ffi_prefix _generate_vec_class _generate_destroy );
19              
20             sub _lib
21             {
22 35     35   132 my @symbols = (
23             # 0.19.0
24             'wasmtime_func_as_funcref',
25             # 0.20.0 / 0.21.0
26             'wasmtime_module_serialize',
27             'wasmtime_module_deserialize',
28             'wasmtime_store_gc',
29             );
30 35         159 my $lib = find_lib lib => 'wasmtime', symbol => \@symbols;
31 35 50       268119 return $lib if $lib;
32 35         207 $lib = find_lib lib => 'wasmtime', alien => 'Alien::wasmtime', symbol => \@symbols;
33 35 50       2728304 return $lib if $lib;
34 0         0 die 'unable to find wasmtime 0.19.0 or better';
35             }
36              
37             our $ffi_prefix = 'wasm_';
38             our $ffi = FFI::Platypus->new( api => 1 );
39             FFI::C->ffi($ffi);
40             $ffi->lib(__PACKAGE__->_lib);
41             $ffi->mangler(sub {
42             my $name = shift;
43             return $name if $name =~ /^(wasm|wasmtime|wasi)_/;
44             return $ffi_prefix . $name;
45             });
46              
47             { package Wasm::Wasmtime::Vec;
48 35     35   16164 use FFI::Platypus::Record;
  35         53167  
  35         3122  
49             record_layout_1(
50             $ffi,
51             size_t => 'size',
52             opaque => 'data',
53             );
54             }
55              
56             { package Wasm::Wasmtime::ByteVec;
57 35     35   265 use base qw( Wasm::Wasmtime::Vec );
  35         85  
  35         25976  
58              
59             $ffi->type('record(Wasm::Wasmtime::ByteVec)' => 'wasm_byte_vec_t');
60             $ffi_prefix = 'wasm_byte_vec_';
61              
62             sub new
63             {
64 247     247   529 my $class = shift;
65 247 100       613 if(@_ == 1)
66             {
67 169         618 my($data, $size) = FFI::Platypus::Buffer::scalar_to_buffer($_[0]);
68 169         1989 return $class->SUPER::new(
69             size => $size,
70             data => $data,
71             );
72             }
73             else
74             {
75 78         245 return $class->SUPER::new(@_);
76             }
77             }
78              
79             sub get
80             {
81 303     303   7305 my($self) = @_;
82 303         1249 FFI::Platypus::Buffer::buffer_to_scalar($self->data, $self->size);
83             }
84              
85             $ffi->attach( delete => ['wasm_byte_vec_t*'] => 'void' );
86             }
87              
88             sub _generic_vec_delete
89             {
90 180     180   1112 my($xsub, $self) = @_;
91 180         804 $xsub->($self);
92             # cannot use SUPER::DELETE because we aren't
93             # in the right package.
94 180         506 Wasm::Wasmtime::Vec::DESTROY($self);
95             }
96              
97             sub _generate_vec_class
98             {
99 87     87   282 my %opts = @_;
100 87         261 my($class) = caller;
101 87         192 my $type = $class;
102 87         438 $type =~ s/^.*:://;
103 87         195 my $v_type = "wasm_@{[ lc $type ]}_vec_t";
  87         429  
104 87         315 my $vclass = "Wasm::Wasmtime::${type}Vec";
105 87         169 my $prefix = "wasm_@{[ lc $type ]}_vec";
  87         325  
106              
107             Sub::Install::install_sub({
108             code => sub {
109 334     334   1785 my($self) = @_;
110 334         1076 my $size = $self->size;
111 334 100       1077 return () if $size == 0;
112 218         1317 my $ptrs = $ffi->cast('opaque', "opaque[$size]", $self->data);
113 218         24916 map { $class->new($_, $self) } @$ptrs;
  435         1512  
114             },
115 87         928 into => $vclass,
116             as => 'to_list',
117             });
118              
119             {
120 35     35   297 no strict 'refs';
  35         89  
  35         34547  
  87         5835  
121 87         186 @{join '::', $vclass, 'ISA'} = ('Wasm::Wasmtime::Vec');
  87         1881  
122             }
123 87         459 $ffi_prefix = "${prefix}_";
124 87         557 $ffi->type("record($vclass)" => $v_type);
125             $ffi->attach( [ delete => join('::', $vclass, 'DESTROY') ] => ["$v_type*"] => \&_generic_vec_delete)
126 87 100 66     11092 if !defined($opts{delete}) || $opts{delete};
127              
128             }
129              
130             sub _wrapper_destroy
131             {
132 840     840   93919 my($xsub, $self) = @_;
133 840 50       19046 return if Devel::GlobalDestruction::in_global_destruction();
134 840 100 100     9025 if(defined $self->{ptr} && !defined $self->{owner})
135             {
136 197         809 $xsub->($self);
137 197         4694 delete $self->{ptr};
138             }
139             }
140              
141             sub _generate_destroy
142             {
143 401     401   905 my $caller = caller;
144 401         1028 my $type = lc $caller;
145 401 100       1554 if($type =~ /::linker$/)
    100          
146             {
147 9         33 $type = 'wasmtime_linker_t';
148             }
149             elsif($type =~ /::wasi/)
150             {
151 21         196 $type =~ s/^.*::wasi(.*)$/wasi_${1}_t/g;
152             }
153             else
154             {
155 371         1736 $type =~ s/^.*:://;
156 371         908 $type = "wasm_${type}_t";
157             }
158 401         2077 $ffi->attach( [ delete => join('::', $caller, 'DESTROY') ] => [ $type ] => \&_wrapper_destroy);
159             }
160              
161             { package Wasm::Wasmtime::Error;
162              
163             $ffi_prefix = 'wasmtime_error_';
164             $ffi->custom_type(
165             wasmtime_error_t => {
166             native_type => 'opaque',
167             native_to_perl => sub {
168             defined $_[0] ? __PACKAGE__->new($_[0]) : undef
169             },
170             },
171             );
172              
173             Sub::Install::install_sub({
174             code => sub {
175 8     8   22 my($class, $ptr, $owner) = @_;
176 8         58 bless {
177             ptr => $ptr,
178             owner => $owner,
179             }, $class;
180             },
181             into => __PACKAGE__,
182             as => 'new',
183             });
184              
185             $ffi->attach( message => ['wasmtime_error_t','wasm_byte_vec_t*'] => sub {
186             my($xsub, $self) = @_;
187             my $message = Wasm::Wasmtime::ByteVec->new;
188             $xsub->($self->{ptr}, $message);
189             my $ret = $message->get;
190             $message->delete;
191             $ret;
192             });
193              
194             $ffi->attach( [ delete => "DESTROY" ] => ['wasmtime_error_t'] => sub {
195             my($xsub, $self) = @_;
196             if(defined $self->{ptr} && !defined $self->{owner})
197             {
198             $xsub->($self->{ptr});
199             }
200             });
201             }
202              
203             { package Wasm::Wasmtime::Val::Of;
204             FFI::C->union(of_t => [
205             i32 => 'sint32',
206             i64 => 'sint64',
207             f32 => 'float',
208             f64 => 'double',
209             anyref => 'opaque',
210             funcref => 'opaque',
211             ]);
212             }
213              
214             my %kind = (
215             0 => 'i32',
216             1 => 'i64',
217             2 => 'f32',
218             3 => 'f64',
219             128 => 'anyref',
220             129 => 'funcref',
221             );
222              
223             { package Wasm::Wasmtime::Val;
224             FFI::C->struct(wasm_val_t => [
225             kind => 'uint8',
226             of => 'of_t',
227             ]);
228              
229             sub to_perl
230             {
231 59     59   124 my $self = shift;
232 59         159 my $kind = $kind{$self->kind};
233 59         786 $self->of->$kind;
234             }
235             }
236              
237             { package Wasm::Wasmtime::ValVec;
238             FFI::C->array(wasm_val_vec_t => [
239             'wasm_val_t',
240             ], { nullable => 1 });
241              
242             sub to_perl
243             {
244 38     38   66 my $self = shift;
245 38         134 map { $_->to_perl } @$self
  48         2272  
246             }
247              
248             $ffi->attach_cast('from_c', 'opaque', 'wasm_val_vec_t', sub {
249             my($xsub, undef, $ptr) = @_;
250             $xsub->($ptr);
251             });
252              
253             sub from_perl
254             {
255 53     53   424 my($class, $vals, $types) = @_;
256 53 100       197 @$vals ? $class->new([map { { kind => $_->kind_num, of => { $_->kind => shift @$vals } } } @$types]) : undef;
  70         622  
257             }
258             }
259              
260             1;
261              
262             __END__