Hacker News new | ask | show | jobs
by fschuett 1299 days ago
WASM is executable from every language to every language, you wouldn't have to write code specifically targeting the JNI for example. Say you want to use a PDF library from Java, a web server from C# and some scripts someone wrote in Python, and for whatever reason your preferred language of the day is Haskell. In the normal case you'd have to rely on someone to maintain Java -> Haskell, C# -> Haskell and Python -> Haskell glue code. But with wasm, you just pull in csharp.wasm, python.wasm and java.wasm, so it's just one binding layer, not three.

It's often the case that people choose languages based on the availability of the libraries written in those languages. The goal of non-browser WASM is to get rid of that and to minimize the friction of inter-language communication, so you don't have to rewrite it in $language. WASM is not tied to any language (like the JNI).

4 comments

> But with wasm, you just pull in csharp.wasm, python.wasm and java.wasm, so it's just one binding layer, not three.

A bit of a rant, but there's nothing specific to WebAssembly about this.

Whether you're using WebAssembly or native code:

- The basic interface you get between caller and callee is a low-level calling convention, where essentially all you have are integers (which can be pointers), and any higher-level data structures need to be built out of that.

- A slightly higher-level interface is the C ABI; many languages support exporting and consuming APIs using the C ABI, but it can be frustratingly low-level.

- To provide an even higher-level interface, there are bindings generators, which could in principle either be designed as direct language-to-language bridges, or as a single common standard that any language can provide and consume APIs for.

The WebAssembly Component Model standard and the wit-bindgen tool are an example of the latter. They happen to be defined specifically for WebAssembly rather than generically for any architecture. If they become ubiquitous at some point in the future like they're supposed to, then WebAssembly will have an advantage when it comes to high-level bindings, not because of any fundamental technical aspect of WebAssembly itself, but just because people building these bindings on top of WebAssembly have more motivation and more consensus. (WebAssembly does have a technical advantage when it comes to sandboxing between components, but this is relatively unimportant for most use cases.)

But for now, wit-bindgen isn't ubiquitous, and most of those languages don't even work in WebAssembly yet…

I work on wit-bindgen. One major advantage of the WebAssembly VM is that we can trust the call stack, which gives cross-language calls optimization opportunities to skip serializarion or copies. This will be an even more important when we add async to the component model, and e.g. async rust can await on async typescript, all in the same “executor” in Rust terms.

Sandboxing also goes a long way towards containing the supply chain security issues that every language is susceptible to.

What do you mean by ‘trust the call stack’? Do you mean trusting that a function you call won’t longjmp out or otherwise break out of the call without returning? (Plenty of native code already assumes that functions it calls won’t do that.) Or something else?
Wasm doesn’t yet have any sort of longjmp instructions. Stack switching is currently a proposal and the leading candidate is based on delimited continuations. Additionally, there are no wasm instructions that can manipulate the control stack besides call, indirect call, and return, which are always typechecked. So, if you call untrusted code, you can be assured that it will not be able to manipulate your stack and that it will either return or trap.

Native code might assume that code it calls won’t misbehave in these ways, but wasm guarantees it, and that allows wasm VMs which run untrusted code to have more efficient implementations.

> A slightly higher-level interface is the C ABI;

Small remark: There is nothing like "the C ABI".

https://faultlore.com/blah/c-isnt-a-language/#c-doesnt-actua...

you wouldn't have to write code specifically targeting the JNI for example

Why not? You still need operations like "create object", "look up method", "invoke method", "convert type between languages" etc. WASM doesn't have any kind of polyglot layer that does this automatically.

Ironically, Graal/Truffle actually do. There's a whole infrastructure where languages can expose their structures, functions, objects etc via a language-neutral in-process protocol and the compiler understands how to optimize across the transitions, so you can actually load JavaScript into Python and do those sorts of things:

https://www.graalvm.org/latest/reference-manual/polyglot-pro...

The API you use to import code from other languages is standardized, so it doesn't matter what language the other side is using. But WASM doesn't do anything like that.

"with wasm, you just pull in csharp.wasm, python.wasm and java.wasm, so it's just one binding layer, not three"

Are these real files/projects you're talking about here? If so, can you show us these things? Because WASM seems far too low level to define a Truffle-like polyglot layer let alone make it fast.

That does look pretty awesome - wouldn't it hurt the performance tho? Especially with the DB Example, a database usually needs to be fast.
In the short term, you wouldn't want to run databases in WASM. You could, but it's not really worth the effort, as long as the WASM runtime allows TCP connections, you can just connect to any hosted DB as usual.

For performance, we ship three compiler backends: LLVM (fast execution, but slowest compilation), cranelift and singlepass (our own compiler, very fast compilation and you can compile untrusted code - but slowest execution). There is a slowdown, but the goal is to keep that at a minimum (proper performance tracking is on the bucket list). We are pre-compiling the python.wasm (which was already pre-optimized when the compilation to WASM happened) with LLVM, so you should get assembly that is very close to the native execution, with the exception of the necessary VM overhead. The goal is to make it so that the interoperability gains are worth the performance hit.

Sure, maybe it's not as fast to execute as another implementation, but having ANY implementation is far more useful than none at all.

Here's an example where performance wouldn't matter as much:

Let's say I'm the author of a programming language and I want to add a db layer to my standard lib.

Via WASM I can use a working backend while I'm protyping the API for my db layer. If the performance isn't fast enough I can start implementing my own "native" version with feature parity.

I can then use some of the tests from the original library (via WASM) to make sure my implementation has feature parity to the original.

How would you do that? Could you please show a working example?

What you're saying is completely impossible right now AFIK…