Stream: general

Topic: Java JNI bindings to Wasmtime


view this post on Zulip Benjamin Fry (Oct 04 2020 at 22:40):

I've been playing with this for the past week or so, i.e. Java JNI bindings to Wasmtime. Thought I'd share here, if there are other folks interested. It's very early stages, but I've at least gotten up to calling Java methods from WASM via the Java Wasmtime bindings. There's still a lot to do. But it's really exciting to see this working. Nice work all on all the interfaces, Wasmtime has been a pleasure to work with. https://github.com/bluejekyll/wasmtime-java

Wasmtime bindings for Java. Contribute to bluejekyll/wasmtime-java development by creating an account on GitHub.

view this post on Zulip Alex Crichton (Oct 05 2020 at 01:29):

Nice! Would love to add this to the readme when it's ready and we may want to move into the org if you'd like as well

view this post on Zulip Benjamin Fry (Oct 05 2020 at 03:12):

Yes. I’d definitely love to do that. I’m doing my best to do things correctly. I’m a little nervous about the threading model right now, but am happy with the memory management.

view this post on Zulip Alex Crichton (Oct 05 2020 at 03:44):

I'd be happy to help out with any questions in the wasmtime arena if need be!

view this post on Zulip Alex Crichton (Oct 05 2020 at 03:44):

Although I'm much less knowledgeable in the JNI arena...

view this post on Zulip Benjamin Fry (Oct 05 2020 at 15:15):

Right now I have some concerns about dropping across threads. Java has a new Cleaner facility (deprecates finalize methods), and I think it will potentially cleanup objects on a separate thread. This could mean that !Send and !Sync types (e.g. Store) might get dropped by a different thread. Given that the wasmtime types don’t implement Drop, I think this is ok, but I wonder if there are other concerns there?

view this post on Zulip Alex Crichton (Oct 05 2020 at 15:54):

@Benjamin Fry nah that'll be the cause of many segfaults, they all need to be dropped on the same thread. I had to deal with this with the Go bindings as well, which specifically queued up items for destruction into a shared structure and then the "main item" occasionally gc'd it. That's not a great solution in Go though and I'd ideally like to do better, but I wasn't able to find a way in Go to force things to deallocate on the same thread.

view this post on Zulip Benjamin Fry (Oct 05 2020 at 16:00):

Thanks, @Alex Crichton, that is exactly the type of thing I was worried about.

view this post on Zulip Benjamin Fry (Oct 05 2020 at 16:09):

I'm a little tempted to spawn a thread in Rust to manage all of the Wasmtime components, but that would add a substantial amount of state to manage between threads...

view this post on Zulip Benjamin Fry (Oct 07 2020 at 17:28):

I got end-to-end tests working! Still need to figure up the threading and cleanup, but this is in pretty decent shape for POC and outline.

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:15):

@Alex Crichton taking you up on your offer, is there a standard for array and string conversion into WASM that I can follow? It's not 100% clear to me right now how to pass byte arrays and strings into wasmtime. I'm assuming this is mostly standardized because wasm-bindgen does something on the WASM interface level. Maybe you could point me at where this happens in the Go bindings?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:16):

@Benjamin Fry do you mean at the

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:16):

C API layer?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:16):

or in wasm itself?

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:17):

The "C" api layer, though I'm not using the C api, I'm going straight from Java -> JNI -> Rust -> Wasmtime... it's easier I think than using the C API.

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:19):

ah ok, so wasm-bindgen is not really related to wasmtime in that it's all meant for the web

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:19):

otherwise do you mean like passing wasm bytes to wasm_module_new?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:19):

or

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:19):

getting the name from wasm_importtype_module or something like that?

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:20):

No, the Func interface.

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:20):

so like you want to call a wasm program and give it a string?

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:21):

Or a byte array, yes. I understand this needs to be done with a Memory or Table?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:21):

ah

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:21):

ok

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:21):

er

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:21):

er sry learning a new keyboard layout...

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:21):

but anyway you'll be using Memory

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:22):

you'll at this point basically need to do what wasm-bindgen does by hand

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:22):

right... that's what I figured.

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:22):

which means you need to manually copy the bytes into Memory and then call the wasm function with the pointer

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:22):

currently wasmtime doesn't do anything to help you

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:23):

although this is the whole schtick of interface types

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:23):

What are "interface" types?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:23):

https://github.com/webassembly/interface-types

Contribute to WebAssembly/interface-types development by creating an account on GitHub.

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:24):

still a wasm proposal, but basically makes strings and such first-class types

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:24):

Ah, that looks nice.

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:25):

you can think of that proposal sort of like wasm-bindgen standardized

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:26):

In the mean time, is there a common pattern for laying out the function parameters that are associated with the Memory being passed in?

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:27):

That is, I believe I have to use a Linker to associate the memory, and then pass in the length of the Memory?

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:33):

I guess I'm looking for naming conventions and parameter ordering/placement.

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:37):

@Benjamin Fry hm I'm not

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:37):

sure that I understand

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:37):

what do you mean by Memory as a parameter?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:38):

b/c that should largely be handled during instantiation, not while you're calling functions

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:39):

yes. but the function definitions need to take offset and length right?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:39):

they

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:39):

and they might be mixed with other parameters?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:39):

true, yeah, but there's no real convention about that since it's more

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:40):

of an ABI thing with the wasm blob

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:40):

any ordering of arguments is basically as easy as all the others, so I think s-to answer your question there's no real conventions or anything like that

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:42):

I see. Do you forsee wasm-bindgen as ever being used for non-js areas?

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:43):

because I would expect it expects the orders and naming of the memory to be done in some consistent way?

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:45):

wasm-bindgen literally as is probably not, but spiritually yes (that's interface types effectively)

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:45):

wasm-bindgen doesn't really deal with naming or ordering conventions, it just reflects what you wrote in the source

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:46):

so if the first argument is a string then the first two abi arguments are the pointer and the length, but that moves around if you move the argument

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:50):

That's exactly what i was looking for... {pointer, length} in that order, based on where the String/byte array appear as arguments in the original language.

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:52):

Thank you!

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:52):

oh

view this post on Zulip Alex Crichton (Oct 09 2020 at 20:52):

oh lol sorry if that was a roundabout way to answer your question

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:53):

I'm sure mostly from my lack of familiarity with this space.

view this post on Zulip Benjamin Fry (Oct 09 2020 at 20:53):

Thanks for helping me grok it.

view this post on Zulip Benjamin Fry (Oct 13 2020 at 19:42):

@Alex Crichton I think, unless I'm doing something wrong, I need to have access to a dynamic version of Func::wrap, that would allow for a list of WasmTy parameters. Is there a reason that implementation doesn't exist? I think I need it to be able to build more complex types for passing via memory regions with Caller, but the compile time bindings of the existing Func::wrap impl is very restrictive.

view this post on Zulip Alex Crichton (Oct 13 2020 at 19:43):

@Benjamin Fry I think that's Func::new?

view this post on Zulip Benjamin Fry (Oct 13 2020 at 19:44):

I'm using that now, but it feels like I don't have access to some of the dynamic type conversion options that we get with the WasmTy implementations (I want a custom version of that for Slices, like what wasm-bindgen has).

view this post on Zulip Benjamin Fry (Oct 13 2020 at 19:46):

Maybe I'm over-thinking this.

view this post on Zulip Alex Crichton (Oct 13 2020 at 19:53):

hm that's true

view this post on Zulip Alex Crichton (Oct 13 2020 at 19:53):

although the conversions are in theory relatively trivial

view this post on Zulip Alex Crichton (Oct 13 2020 at 19:54):

we may need to implement a few more things though

view this post on Zulip Benjamin Fry (Oct 13 2020 at 19:58):

So I'm hoping to use wasm_bindgen for the wasm bindings. It appears that wasm_bindgen only produces 32bit arguments (which makes sense for wasm32 target), but wasmtime appears to directly support 64bit values? This implies that I have to detect if the module is wasm_bindgen generated and then only use 32bit arguments? Am I misunderstanding the lay of the land?

view this post on Zulip Benjamin Fry (Oct 13 2020 at 20:02):

I'll share what I have when I get it working, then if you have time maybe you could review what I've done?

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:12):

yeah

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:13):

yeah this is where wasm-bindgen is very web focused

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:13):

which only some browsers implementin the 64 bit integers in js for now

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:13):

but yeah can help with review!

view this post on Zulip Benjamin Fry (Oct 13 2020 at 20:14):

Ok, do you think it's worth "detecting" if the module is wasm_bindgen, and if so, only drop to 32bit impls in that case? or is there another more general purpose way to detect that?

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:19):

hm maybe? tbh fusing wasm-bindgen and wasmtime is gonna be painful no matter what you do

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:19):

it's just not really built for it

view this post on Zulip Benjamin Fry (Oct 13 2020 at 20:19):

Yeah, I'm starting to get that... ok

view this post on Zulip Benjamin Fry (Oct 13 2020 at 20:20):

different question then, is there a different macro tool like wasm-bindgen that is better to use in this case?

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:22):

unfortunately not currently, no

view this post on Zulip Alex Crichton (Oct 13 2020 at 20:23):

or not that I know of

view this post on Zulip Benjamin Fry (Oct 13 2020 at 20:23):

got it. I did notice some discussion on the wasm-bindgen repo in this area, so i can follow that.

view this post on Zulip Benjamin Fry (Oct 15 2020 at 00:26):

Ugh, I'm so close on this, at least of passing Strings from WASM->Rust->Java, but I have an exception, so need to take a detour to unwrap the chain of exceptions.

view this post on Zulip Benjamin Fry (Oct 15 2020 at 16:08):

Now I have full Java exception stack traces being reported back from the WASM calls to Java Funcs. That's really helpful.

view this post on Zulip Benjamin Fry (Oct 15 2020 at 16:12):

@Alex Crichton Hopefully this is my last pestering question. I have byte array's being passed from WASM to Java now. I'm going to work on the inverse next. I'm curious about something related to Memory usage. Is the general best practice to export allocation methods in the WASM module, and get pointers back from those functions? Or is there another best practice for finding an offset into Memory for that call? I've been reading a lot of the documentation and looking at implementations, and I'm quite confused.

view this post on Zulip fitzgen (he/him) (Oct 15 2020 at 16:47):

for now, exposing allocation functions is probably best.

eventually interface types will handle all of this for you

view this post on Zulip Benjamin Fry (Oct 15 2020 at 16:59):

Thanks, @fitzgen (he/him), that confirms what I was thinking would be needed.

view this post on Zulip Till Schneidereit (Oct 16 2020 at 16:37):

@Benjamin Fry the witx tooling might work for your use case, depending on the details. See this excellent blog post by @Radu M for an overview

In this short article we explore how to get started with WebAssembly interface types by defining a simple API layer, then implementing that using Wiggle and Wasmtime

view this post on Zulip Benjamin Fry (Oct 16 2020 at 16:40):

Yeah, I'm considering that for generating bindings and having a common API that's defined... right now I'm just trying to support Rust in WASM, but the witx stuff looks really useful for when we expand to other languages...

view this post on Zulip Till Schneidereit (Oct 16 2020 at 16:41):

Even for just Rust it might be a good approach, because it's intended to approximate Interface Types, and will evolve in that direction more and more

view this post on Zulip Benjamin Fry (Oct 16 2020 at 16:51):

True... I haven't looked at using it yet. Do you have an opinion on the ease of adoption and usage of witx?

view this post on Zulip Benjamin Fry (Oct 16 2020 at 16:52):

I'm guessing that differs across different languages and their support for witx

view this post on Zulip Till Schneidereit (Oct 16 2020 at 17:10):

yeah, I doubt there's a general answer to be had here — though I'd like to have one, too! :smile:

view this post on Zulip Benjamin Fry (Oct 20 2020 at 07:56):

Thanks for all your help @fitzgen (he/him) and @Alex Crichton. I just got byte arrays as both parameters into WASM and returns working all the way back to Java (using ByteBuffer for the Java type). There's some deallocations that I need to insert as everything is just allocating right now, but it's all working. Now just need to do some cleanup.

view this post on Zulip Benjamin Fry (Oct 28 2020 at 22:01):

FYI, I have this in fairly decent shape at least for POC work. Not sure what the rest of the group thinks about where it lives. I have test automation in place for Linux and macOS, but Windows not yet due to a lack of desire on my part at the moment. In terms of work left, I have something in place at least for guaranteeing that Store's aren't shared across threads on the Java side, but it might need to be better for some of the other types. In terms of GC, all the Java types are auto-closeable, and memory is freed in the close of the Java object, so this should help ensure that all cleanup in the Rust impls happens on the same thread, but since I haven't used this in anger yet, I'm sure there are issues with that model.

view this post on Zulip Andrés Margalef (Sep 21 2022 at 00:08):

Is there any mature binding in Java for wasmtime? I'm trying to build some proof of concept to replace statics sdks with something more dynamic using webassembly from Java, python, go, node and rust


Last updated: Nov 22 2024 at 16:03 UTC