From 489317fff453b376eccd8170f448584e4832aac8 Mon Sep 17 00:00:00 2001 From: Tsung Wu Date: Tue, 20 Aug 2024 14:16:04 +0800 Subject: [PATCH 1/6] Provide pid in imports callback context Then we can call exported functions in imported functions. --- lib/wasmex.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/wasmex.ex b/lib/wasmex.ex index d3efebc..bf39750 100644 --- a/lib/wasmex.ex +++ b/lib/wasmex.ex @@ -74,7 +74,8 @@ defmodule Wasmex do %{ memory: %Wasmex.Memory{}, - caller: %Wasmex.StoreOrCaller{} + caller: %Wasmex.StoreOrCaller{}, + pid: pid(), } = context The `caller` MUST be used instead of a `store` in Wasmex API functions. @@ -517,7 +518,8 @@ defmodule Wasmex do context, %{ memory: Wasmex.Memory.__wrap_resource__(Map.get(context, :memory)), - caller: Wasmex.StoreOrCaller.__wrap_resource__(Map.get(context, :caller)) + caller: Wasmex.StoreOrCaller.__wrap_resource__(Map.get(context, :caller)), + pid: self() } ) From 6d2d29d0b7d2761265743af284e0d7274e4b8792 Mon Sep 17 00:00:00 2001 From: Tsung Wu Date: Mon, 26 Aug 2024 18:47:06 +0800 Subject: [PATCH 2/6] feat: add instance to callback context --- lib/wasmex.ex | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/wasmex.ex b/lib/wasmex.ex index bf39750..c5c3102 100644 --- a/lib/wasmex.ex +++ b/lib/wasmex.ex @@ -76,6 +76,7 @@ defmodule Wasmex do memory: %Wasmex.Memory{}, caller: %Wasmex.StoreOrCaller{}, pid: pid(), + instance: %Wasmex.Instance{} } = context The `caller` MUST be used instead of a `store` in Wasmex API functions. @@ -438,6 +439,10 @@ defmodule Wasmex do @spec module(pid()) :: {:ok, Wasmex.Module.t()} | {:error, any()} def module(pid), do: GenServer.call(pid, {:module}) + + @spec instance() :: {:ok, Wasmex.Instance.t()} | {:error, any()} + def instance(pid), do: GenServer.call(pid, {:instance}) + defp stringify_keys(struct) when is_struct(struct), do: struct defp stringify_keys(map) when is_map(map) do @@ -482,6 +487,11 @@ defmodule Wasmex do {:reply, {:ok, module}, state} end + @impl true + def handle_call({:instance}, _from, %{instance: instance} = state) do + {:reply, {:ok, instance}, state} + end + @impl true def handle_call( {:exported_function_exists, name}, @@ -511,7 +521,7 @@ defmodule Wasmex do @impl true def handle_info( {:invoke_callback, namespace_name, import_name, context, params, token}, - %{imports: imports} = state + %{imports: imports, instance: instance} = state ) do context = Map.merge( @@ -519,7 +529,8 @@ defmodule Wasmex do %{ memory: Wasmex.Memory.__wrap_resource__(Map.get(context, :memory)), caller: Wasmex.StoreOrCaller.__wrap_resource__(Map.get(context, :caller)), - pid: self() + pid: self(), + instance: instance } ) From ad4d800a5b60b3b7b91b79eb517c6bed29debc9b Mon Sep 17 00:00:00 2001 From: Tsung Wu Date: Tue, 27 Aug 2024 00:08:15 +0800 Subject: [PATCH 3/6] fix: add missing pid spec --- lib/wasmex.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasmex.ex b/lib/wasmex.ex index c5c3102..02a9f75 100644 --- a/lib/wasmex.ex +++ b/lib/wasmex.ex @@ -440,7 +440,7 @@ defmodule Wasmex do def module(pid), do: GenServer.call(pid, {:module}) - @spec instance() :: {:ok, Wasmex.Instance.t()} | {:error, any()} + @spec instance(pid()) :: {:ok, Wasmex.Instance.t()} | {:error, any()} def instance(pid), do: GenServer.call(pid, {:instance}) defp stringify_keys(struct) when is_struct(struct), do: struct From 686e68d6d50bdae6b854b9c50e5f44588c90526b Mon Sep 17 00:00:00 2001 From: Tsung Wu Date: Thu, 29 Aug 2024 17:27:33 +0800 Subject: [PATCH 4/6] test: call exported function in imported function callback --- test/wasmex_test.exs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/wasmex_test.exs b/test/wasmex_test.exs index 0a1c83e..1b3760b 100644 --- a/test/wasmex_test.exs +++ b/test/wasmex_test.exs @@ -427,4 +427,40 @@ defmodule WasmexTest do assert Wasmex.Memory.read_string(store, memory, string_ptr, length) == "Hello World!" end end + + describe "call exported function in imported function callback" do + test "using instance in callback" do + wat = """ + (module + (func $add_import (import "env" "add_import") (param i32 i32) (result i32)) + + (func $call_import (export "call_import") (param i32 i32) (result i32) + (call $add_import (local.get 0) (local.get 1)) + ) + + (func $call_add (export "call_add") (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.add + ) + ) + """ + + imports = %{ + env: %{add_import: {:fn, [:i32, :i32], [:i32], fn %{instance: instance, caller: caller}, a, b -> + Wasmex.Instance.call_exported_function(caller, instance, "call_add", [a,b], :from) + receive do { + :returned_function_call, {:ok, [result]}, :from} -> :ok + result + after 1000 -> + raise "timeout on exported function call" end + end}} + } + + {:ok, store} = Wasmex.Store.new() + {:ok, module} = Wasmex.Module.compile(store, wat) + pid = start_supervised!({Wasmex, %{store: store, module: module, imports: imports}}) + assert Wasmex.call_function(pid, :call_import, [1, 2]) == {:ok, [3]} + end + end end From dcdbb263a77e61d87f4947c98dc183942b4dd91c Mon Sep 17 00:00:00 2001 From: Tsung Wu Date: Sat, 31 Aug 2024 13:50:17 +0800 Subject: [PATCH 5/6] Update test/wasmex_test.exs Co-authored-by: Philipp Tessenow --- test/wasmex_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/wasmex_test.exs b/test/wasmex_test.exs index 1b3760b..44f02b2 100644 --- a/test/wasmex_test.exs +++ b/test/wasmex_test.exs @@ -448,7 +448,7 @@ defmodule WasmexTest do imports = %{ env: %{add_import: {:fn, [:i32, :i32], [:i32], fn %{instance: instance, caller: caller}, a, b -> - Wasmex.Instance.call_exported_function(caller, instance, "call_add", [a,b], :from) + Wasmex.Instance.call_exported_function(caller, instance, "call_add", [a, b], :from) receive do { :returned_function_call, {:ok, [result]}, :from} -> :ok result From 7b8799723f7a656759057cf3834a925af5e6aff5 Mon Sep 17 00:00:00 2001 From: Tsung Wu Date: Mon, 2 Sep 2024 02:35:34 +0800 Subject: [PATCH 6/6] style: code format --- lib/wasmex.ex | 7 +++++++ test/wasmex_test.exs | 28 ++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/wasmex.ex b/lib/wasmex.ex index 02a9f75..a1bd4a4 100644 --- a/lib/wasmex.ex +++ b/lib/wasmex.ex @@ -439,7 +439,14 @@ defmodule Wasmex do @spec module(pid()) :: {:ok, Wasmex.Module.t()} | {:error, any()} def module(pid), do: GenServer.call(pid, {:module}) + @doc ~S""" + Returns the `Wasmex.Instance` of the Wasm instance. + + ## Example + iex> {:ok, pid} = Wasmex.start_link(%{bytes: File.read!(TestHelper.wasm_test_file_path())}) + iex> {:ok, %Wasmex.Instance{}} = Wasmex.instance(pid) + """ @spec instance(pid()) :: {:ok, Wasmex.Instance.t()} | {:error, any()} def instance(pid), do: GenServer.call(pid, {:instance}) diff --git a/test/wasmex_test.exs b/test/wasmex_test.exs index 44f02b2..c7a4756 100644 --- a/test/wasmex_test.exs +++ b/test/wasmex_test.exs @@ -447,14 +447,26 @@ defmodule WasmexTest do """ imports = %{ - env: %{add_import: {:fn, [:i32, :i32], [:i32], fn %{instance: instance, caller: caller}, a, b -> - Wasmex.Instance.call_exported_function(caller, instance, "call_add", [a, b], :from) - receive do { - :returned_function_call, {:ok, [result]}, :from} -> :ok - result - after 1000 -> - raise "timeout on exported function call" end - end}} + env: %{ + add_import: + {:fn, [:i32, :i32], [:i32], + fn %{instance: instance, caller: caller}, a, b -> + Wasmex.Instance.call_exported_function(caller, instance, "call_add", [a, b], :from) + + receive do + { + :returned_function_call, + {:ok, [result]}, + :from + } -> + :ok + result + after + 1000 -> + raise "timeout on exported function call" + end + end} + } } {:ok, store} = Wasmex.Store.new()