From cee682e2a22869277c8ffc89c2f6bd0814538736 Mon Sep 17 00:00:00 2001 From: Gavin Hayes Date: Wed, 27 Nov 2024 16:09:25 -0500 Subject: [PATCH] feat: compiled plugin --- Extism/lib/Extism.pm | 1 + Extism/lib/Extism/CompiledPlugin.pm | 58 +++++++++++++++++++++++++++++ Extism/lib/Extism/Plugin.pm | 42 ++++++++------------- Extism/lib/Extism/XS.pm | 3 ++ Extism/lib/Extism/XS.xs | 30 +++++++++++++++ Extism/t/02-extism.t | 19 +++++++++- 6 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 Extism/lib/Extism/CompiledPlugin.pm diff --git a/Extism/lib/Extism.pm b/Extism/lib/Extism.pm index eb3aecb..143f233 100644 --- a/Extism/lib/Extism.pm +++ b/Extism/lib/Extism.pm @@ -4,6 +4,7 @@ use 5.016; use strict; use warnings; use Extism::XS qw(version log_file); +use Extism::CompiledPlugin; use Extism::Plugin; use Extism::Function ':all'; use Exporter 'import'; diff --git a/Extism/lib/Extism/CompiledPlugin.pm b/Extism/lib/Extism/CompiledPlugin.pm new file mode 100644 index 0000000..5ce4114 --- /dev/null +++ b/Extism/lib/Extism/CompiledPlugin.pm @@ -0,0 +1,58 @@ +package Extism::CompiledPlugin; + +use 5.016; +use strict; +use warnings; +use Carp qw(croak); +use Extism::XS qw( + compiled_plugin_new + compiled_plugin_free +); +use Exporter 'import'; +use Data::Dumper qw(Dumper); +use Devel::Peek qw(Dump); +use version 0.77; +our $VERSION = qv(v0.2.0); + +our @EXPORT_OK = qw(BuildPluginNewParams); + +sub BuildPluginNewParams { + my ($wasm, $opt) = @_; + my $functions = $opt->{functions} // []; + my @rawfunctions = map {$$_} @{$functions}; + my %p = ( + wasm => $wasm, + _functions_array => pack('Q*', @rawfunctions) + ); + $p{functions} = unpack('Q', pack('P', $p{_functions_array})); + $p{n_functions} = scalar(@rawfunctions); + $p{wasi} = $opt->{wasi} // 0; + $p{fuel_limit} = $opt->{fuel_limit}; + $p{errptr} = "\x00" x 8; + $p{errmsg} = unpack('Q', pack('P', $p{errptr})); + \%p +} + +sub new { + my ($name, $wasm, $options) = @_; + my %opt = %{$options // {}}; + if (defined $opt{fuel_limit}) { + croak "No way to set fuel for CompiledPlugins yet"; + } + my $p = BuildPluginNewParams($wasm, \%opt); + my $compiled = compiled_plugin_new($p->{wasm}, length($p->{wasm}), $p->{functions}, $p->{n_functions}, $p->{wasi}, $p->{errmsg}); + my %savedoptions; + if ($opt{allow_http_response_headers}) { + $savedoptions{allow_http_response_headers} = $opt{allow_http_response_headers}; + } + my %obj = ( compiled => $compiled, options => \%savedoptions); + bless \%obj, $name +} + +sub DESTROY { + my ($self) = @_; + $self->{compiled} or return; + compiled_plugin_free($self->{compiled}); +} + +1; \ No newline at end of file diff --git a/Extism/lib/Extism/Plugin.pm b/Extism/lib/Extism/Plugin.pm index 1623959..d17389e 100644 --- a/Extism/lib/Extism/Plugin.pm +++ b/Extism/lib/Extism/Plugin.pm @@ -7,6 +7,7 @@ use Carp qw(croak); use Extism::XS qw( plugin_new plugin_new_with_fuel_limit + plugin_new_from_compiled plugin_allow_http_response_headers plugin_new_error_free plugin_call @@ -22,6 +23,7 @@ use Extism::XS qw( ); use Extism::Plugin::CallException; use Extism::Plugin::CancelHandle; +use Extism::CompiledPlugin qw(BuildPluginNewParams); use Data::Dumper qw(Dumper); use Devel::Peek qw(Dump); use JSON::PP qw(encode_json); @@ -31,38 +33,26 @@ our $VERSION = qv(v0.2.0); sub new { my ($name, $wasm, $options) = @_; - my $functions = []; - my $with_wasi = 0; - my $fuel_limit; - my $allow_http_response_headers; - if ($options) { - if (exists $options->{functions}) { - $functions = $options->{functions}; - } - if (exists $options->{wasi}) { - $with_wasi = $options->{wasi}; - } - if (exists $options->{fuel_limit}) { - $fuel_limit = $options->{fuel_limit}; - } - if (exists $options->{allow_http_response_headers}) { - $allow_http_response_headers = $options->{allow_http_response_headers}; - } + my ($plugin, $opt, $errptr); + if (!ref($wasm) || !$wasm->isa('Extism::CompiledPlugin')) { + $opt = defined $options ? {%$options} : {}; + my $p = BuildPluginNewParams($wasm, $opt); + $plugin = ! defined $p->{fuel_limit} + ? plugin_new($p->{wasm}, length($p->{wasm}), $p->{functions}, $p->{n_functions}, $p->{wasi}, $p->{errmsg}) + : plugin_new_with_fuel_limit($p->{wasm}, length($p->{wasm}), $p->{functions}, $p->{n_functions}, $p->{wasi}, $p->{fuel_limit}, $p->{errmsg}); + $errptr = $p->{errptr}; + } else { + $opt = $wasm->{options}; + $errptr = "\x00" x 8; + my $errmsg = unpack('Q', pack('P', $errptr)); + $plugin = plugin_new_from_compiled($wasm->{compiled}, $errmsg); } - my $errptr = "\x00" x 8; - my $errptrptr = unpack('Q', pack('P', $errptr)); - my @rawfunctions = map {$$_} @{$functions}; - my $functionsarray = pack('Q*', @rawfunctions); - my $functionsptr = unpack('Q', pack('P', $functionsarray)); - my $plugin = ! defined $fuel_limit - ? plugin_new($wasm, length($wasm), $functionsptr, scalar(@rawfunctions), $with_wasi, $errptrptr) - : plugin_new_with_fuel_limit($wasm, length($wasm), $functionsptr, scalar(@rawfunctions), $with_wasi, $fuel_limit, $errptrptr); if (! $plugin) { my $errmsg = unpack('p', $errptr); plugin_new_error_free(unpack('Q', $errptr)); croak $errmsg; } - if ($allow_http_response_headers) { + if ($opt->{allow_http_response_headers}) { plugin_allow_http_response_headers($plugin); } bless \$plugin, $name diff --git a/Extism/lib/Extism/XS.pm b/Extism/lib/Extism/XS.pm index 8769555..e2e8778 100644 --- a/Extism/lib/Extism/XS.pm +++ b/Extism/lib/Extism/XS.pm @@ -39,6 +39,9 @@ our @EXPORT_OK = qw( log_file log_custom log_drain + compiled_plugin_new + compiled_plugin_free + plugin_new_from_compiled CopyToPtr ); diff --git a/Extism/lib/Extism/XS.xs b/Extism/lib/Extism/XS.xs index fff9932..d7beb4c 100644 --- a/Extism/lib/Extism/XS.xs +++ b/Extism/lib/Extism/XS.xs @@ -81,6 +81,8 @@ ExtismMemoryHandle T_UV const ExtismCancelHandle * T_PTR PV T_PV uint64_t T_UV +ExtismCompiledPlugin * T_PTR +const ExtismCompiledPlugin * T_PTR HERE const char * @@ -90,6 +92,34 @@ version() OUTPUT: RETVAL +ExtismCompiledPlugin * +compiled_plugin_new(wasm, wasm_size, functions, n_functions, with_wasi, errmsg) + const uint8_t *wasm + ExtismSize wasm_size + const ExtismFunction **functions + ExtismSize n_functions + bool with_wasi + char **errmsg + CODE: + RETVAL = extism_compiled_plugin_new(wasm, wasm_size, functions, n_functions, with_wasi, errmsg); + OUTPUT: + RETVAL + +void +compiled_plugin_free(compiled_plugin) + ExtismCompiledPlugin *compiled_plugin + CODE: + extism_compiled_plugin_free(compiled_plugin); + +ExtismPlugin * +plugin_new_from_compiled(compiled_plugin, errmsg) + const ExtismCompiledPlugin *compiled_plugin + char **errmsg + CODE: + RETVAL = extism_plugin_new_from_compiled(compiled_plugin, errmsg); + OUTPUT: + RETVAL + ExtismPlugin * plugin_new(wasm, wasm_size, functions, n_functions, with_wasi, errmsg) const uint8_t *wasm diff --git a/Extism/t/02-extism.t b/Extism/t/02-extism.t index 61c0a81..262f4e0 100644 --- a/Extism/t/02-extism.t +++ b/Extism/t/02-extism.t @@ -7,7 +7,7 @@ use Extism ':all'; use JSON::PP qw(encode_json decode_json); use File::Temp qw(tempfile); use Devel::Peek qw(Dump); -plan tests => 51; +plan tests => 54; # ... ok(Extism::version()); @@ -261,3 +261,20 @@ ok($@->{message}); my $plugin = Extism::Plugin->new($wasm, {wasi => 1, allow_http_response_headers => 1}); ok($plugin); } + +# compiled plugin +{ + my $compiled = Extism::CompiledPlugin->new($wasm, {wasi => 1}); + for (1..2) { + my $plugin = Extism::Plugin->new($compiled); + my $output = $plugin->call('count_vowels', "this is a test"); + my $outputhash = decode_json($output); + ok($outputhash->{count} == 4); + } +} +{ + eval { + Extism::CompiledPlugin->new($wasm, {wasi => 1, fuel_limit => 20}); + }; + ok($@); +}