Exception when passing a callback as parameter
kamphaus opened this issue · comments
Describe the bug
Trying to call a WASM method passing a callback parameter fails with a RuntimeException.
Steps to reproduce
The following Rust code builds the WASM code passed to Instance:
#[no_mangle]
pub extern fn greet(print: extern fn(u8)) {
print(42)
}
The following Java code fails when trying to call the greet
method:
Instance instance = new Instance(wasm);
Consumer<Object> print = (Object o) -> System.out.println("Printed: " + ((Integer) o));
instance.exports.getFunction("greet").apply(print);
instance.close();
Actual behavior
When run, this fails with the following exception:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: java.lang.RuntimeException: Failed to convert the argument 0nth of `greet` into a WebAssembly value.
at org.wasmer.Instance.nativeCallExportedFunction(Native Method)
at org.wasmer.Exports.lambda$null$0(Exports.java:85)
at org.example.wasmhost.WasmHostApplication.main(WasmHostApplication.java:45)
... 8 more
Additional context
My Java version is:
$ java -version
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.18.04)
OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.18.04, mixed mode, sharing)
Hello,
That's expected. Wasm understand i32
, i64
, f32
and f64
. A callback (which is a function pointer) isn't accepted :-).
I see that it's possible with the embedded WASM in javascript (using the wasm-bindgen rust library):
https://rustwasm.github.io/wasm-bindgen/reference/receiving-js-closures-in-rust.html
Also wasmer seems to support callbacks since wasmerio/wasmer#670 was closed in 2019.
wasm-bindgen
is really tied to Rust and Javascript. It instruments Rust to generate specific code for Javascript. It's not the case here. wasm-bindgen
is not language agnostic.
In wasmerio/wasmer#670, it's not related to passing callbacks from host to Wasm. It was related to calling Wasm functions by their index. And the index was encoded in a u32
. But we were talking about Wasm functions, not host functions.
wasmer-java
needs more love to support imported functions, memories etc. I'll soon get more time to work on it and to improve it largely, but supporting host functions as Wasm exported function's argument is not part of the plan, and it's very unlikely to be possible in a short-term future.
Note, you can store your function pointer in a Table
and pass the table index to the function.
Note 2: wasmer-java
doesn't support Table
yet for the moment (but wasmer
does support it!)
Thanks, that would be great if calling back to Java code from Wasm was possible.
I also tried out GraalVM's polyglot wasm support, but no luck with that either.
Can you describe me what you're trying to achieve please? Maybe it's possible in another way.
I'm trying to develop a plugin system for a Spring Boot Java API.
It should be possible to load/unload plugins without restarting the JVM and have well defined interfaces.
I thought it might be simpler to manage a wasm context per plugin rather than OSGi or webhooks, especially if we might use GraalVM native-image in the future.
Wasm is a good fit for your usecase indeed!
You can callback Java from Wasm with imported (host) function. It's not possible yet with wasmer-java
but I'll add it soon :-).