I made a Minecraft plugin in Java that, via JNI, allows you to make Minecraft plugins using shared libraries. I'm now realizing the shared libraries part is unstable, and thus I wanted to offer WASM libraries as an alternative (but not a replacement, because both approaches has their own tradeoffs).
If you've never used JNI, what you need to know is that a function exported from this shared library would look like this:
#[no_mangle]
pub extern "system" fn __on__PluginEnableEvent(env: JNIEnv, obj: JObject<'_>) {
// ...
}
JNIEnv is supposed to be a Rust abstraction for the unsigned 64-bit integer that represents that address for JNIEnv, of course. Same with JObject.
...and when I use wasmtime's wasi tool to compile, it's compiled to a signed 32-bit integer. Both of them. And even if I cast the address to a signed 32-bit integer, I get this specific error:
0: memory fault at wasm address 0xc4bbef50 in linear memory of size 0x130000
1: wasm trap: out of bounds memory access', src/wasm.rs:187:18
I assume at this point that I'm trying to do something impossible that's never going to be supported. But I titled the post this way because, if I'm right, then the question I'd ask next would be too long to fit into the field: If WASM cannot do it, is there another portable, platform-agnostic binary format similar to WASM that could do this?
WebAssembly programs effectively run in their own address space meaning that if JNIEnv
is a pointer to something on the host, for example, that can't be dereferenced in wasm because wasm doesn't have access to that. You'll need to either copy data into the wasm program or otherwise have an abstract "handle" that the wasm program uses which the host operates on behalf of wasm. For example JNIEnv
might be behind a handle where the guest wasm calls a function which causes the host to use the JNIEnv
value it can access.
FWIW this is all what the component model is targeted at solving -- providing abstractions for common types like resources (handles), strings, lists, etc, and all the management of that is done via bindings generation in the host and the guest
That's what I figured. I could try the latter.
Rust compiles JNIEnv to i32, though. Would I have to change the type in the function signatures to a new type?
Right, you wouldn't actually carry anything like a JNIEnv
into the guest Rust -- that type carries a lot of implications with it (actual pointer, how to use it, etc). The "handle" approach usually works by keeping an external (host-side) table of pointers/state, and giving indices into this table to the guest as handles. You wouldn't want to pass the raw pointer in as e.g. an i64
because then you're trusting the guest Wasm to preserve its value, and allowing it to control raw memory accesses, which defeats the point of the sandbox
basically, you need to build a "proxy" layer where all of these types stay outside, and you design an API at the Wasm level that uses basic Wasm types
(this is more or less what wit-bindgen does automatically, which is why Alex refers to the component model; it's fairly bleeding-edge at the moment though, fwiw)
I see.
Last updated: Nov 22 2024 at 16:03 UTC