Anything built with emscripten expects the emscripten runtime to be provided for those imports. The default output of emscripten isn't a WASM file that's meant to be used on its own, but a WASM file plus a JS file that sets up the runtime. It looks like they're trying to use the WASM file on its own and running into the problems that come from that.
Standards like WASI are meant to define a common runtime for WASM modules. A WASM module built for WASI would fit the author's expectations better (as they could possibly find an all-in-one WASM+WASI library or attach a WASI implementation to a WASM library to run the WASM file), though I don't know anything about emscripten and WASI compatibility.
Edit: You can use the STANDALONE_WASM emscripten option to make it not depend on the emscripten runtime and use WASI instead. This is what the author should use.
It's straightforward to create a WASI executable with Emscripten:
emcc hello.c -o hello.wasm
This generates a single self-contained .wasm file that's runnable as 'WASI executable':
wasmtime hello
Hello World!
Some advice for C/C++ library design to make life easier for users in such 'esoteric environments':
- distribute as source code, ideally as a small number of .h and .c files
- ideally don't distribute any build system files, instead make the library configurable via a handful of preprocessor defines, list those defines in the readme
- don't have builtin IO calls such as fopen/fread/fclose, instead let the library user provide any data to be processed in memory
- don't have built-in multithreading calls, instead provide functions which allow processing data in slices, so that the library user can easily call those 'slice functions' from multiple threads (or a similar solution to prevent multithreading from being baked into the library)
- ideally let the user also override memory allocation functions
You could also add an optional 'convenience layer' over the lower-level flexible-but-inconvenient core library, as long as the core library can be built and used without the convenience layer.
In essence it's just a way to decouple the actually important library functionality from runtime environment details which might be better implemented outside the C and C++ stdlibs anyway.
It's already as simple as the stdlib IO functions not being asynchrononous while many operating systems provide more modern and performant asynchronous IO APIs. For a specific type of library (such as an image decoder) it's often better to delegate such details to the library user instead of either using the stdlib or having the library talk directly to OS APIs.
PS: this type of library design is actually also quite common in game development, because such libraries must be embeddable in game engine code bases which usually implement their own IO-, threading- and memory-management systems.
There's nothing really standard, every environment will have its own APIs. WASI is not going to run in a web browser without additional modules.
If system calls are not required, code can be compiled to `wasm32-freestanding`. That works everywhere, as it ensures that there are no dependencies on external APIs.
When specifically targeting WASI, the code should be compiled to `wasm32-wasi`. That can be done using `clang`, with the addition of a specific libc and the `libclang_rt.builtins-wasm32.a` file.
But the easiest and probably most common way is to use `zig cc`, followed by `-target wasm32-wasi` or `-target wasm32-freestanding`.
For the web, or any environment with Javascript around, Emscripten remains by far the easiest way to go, since it provides a really amazing POSIX emulation.
The problem is that the instructions for actually running the WASM file are not that clear... the docs the author mentions shows how to compile to WASM, which is easy enough, but then here's the instructions to make that actually work in the browser:
Yeah, you need some mysterious Python script, a JS service worker at runtime, choose whether you want the WASM or WASM_SIMD target, use a browser that supports Threads and SIMD if you chose that, make sure to serve everything with the appropriate custom HTTP headers... just reading that, I can see that to get this stuff working on non-browser WASM targets would likely require expertise in WASM, which is the point of the OP. WASM's UX is just not there yet.
Most of the stuff there has nothing to do with WASM itself though. Loading an running a WASM module is very simple. In this case it’s mostly web craziness like bundling and the hoops you have to jump through to enable some features post-spectre mitigations. Then seemingly that the go runtimes suck.
This is a disappointing post -- they picked up a complex thing that they didn't fully understand, pushed some buttons, and then complained when it didn't work as they expected. I expect more from this blogger, whose content I think of as usually good.
They are right about the DX with wasm, it's simply not there. Those error messages are all too common and there's a lot of vague things going on when you compile it down. The whole ecosystem is quite fragmented and a bit of a mess.
"The whole ecosystem is quite fragmented and a bit of a mess."
Just the general web style of doing things?
I was hoping wasm to be somewhat cleaner and I think it is compared to html and co, but I also ran into quite some wtf moments, with the little experiments I was doing.
I said along the lines of, paraphrasing myself, in my beginner talk about Wasm + Ruby at OSS Summit, "I distinctly began to get the impression that Wasm was trying to sell me a compiler, so I decided that I should probably go ahead and let it..."
A big issue is that it doesn’t currently replace the old webtech, but instead just adds to it. You can’t use WASM in the browser without also using JS, which is unfortunate.
That's the direction they're actively working towards. Unfortunately you can't expect to design a big novel piece of web tech and every intended interface point correctly in a way that a cohort of unrelated browser vendors will implement before the very first release.
The lack of integration with anything. The primary method of storage being flat memory that cannot hold a DOM reference in a way that doesn't break garbage collection. The lack of string handling.
I'm not saying that it is impossible, but you would end up pretty much building a new language with all the required features, sitting next to the useless array-based thing.
The ability to support GC'able DOM references in browsers through integration with the host-side GC is absolutely being worked on, it just took several years to stabilize because it's a tricky feature. It will also allow GC'd languages to target standalone runtimes more easily. The developers just didn't want to block the early 1.0 spec, early toolchain and software support, etc. all on that. It was just easier to ship the C/C++ style memory management model because a lot of existing code could immediately be reused and it was easier to implement. But it was always planned from a very early phase to have GC integration, at minimum, since web usage was always a big priority.
The string story is a bit unfortunate (how to handle conversions between modules in different languages). Until the component model is fully fleshed out I don't think there's a clear answer here besides things like bindgen-style interfaces, but I agree it's a sore spot.
I don't know what a rewrite of the spec would achieve at this point since most of these things would be impossible to get right on the first try, I suspect, and the current one is actually pretty good for the cases it does support.
If you do a rewrite you can throw away the memory buffer and handle all data with opaque pointers, thus allowing complete integration with the GC.
And yes, you could just leave the buffer dangling there, and tell people not to use it, but it doesn't ease any part of the task of designing the new thing.
Writing a spec and implementing a spec are 2 different things. But they are connected. A spec can sound good in theory, but can turn out to be allmost impossible to implement. Do you think, you could write a better spec for wasm2, that browser vendors can realistically implement? Well, they are open for participation.
> The lack of integration with anything. The primary method of storage being flat memory that cannot hold a DOM reference in a way that doesn't break garbage collection. The lack of string handling.
WASM spec authors and implementors seem to agree that this all can be naturally handled by providing direct bindings to regular web APIs in WASM.
I would tend to agree... Those don't exactly seem core to the design of the runtime?
The DOM is garbage collected, you cannot hold a reference to a DOM element in a non-garbage-collected environment without having to manually mark as held and free.
Remember that some old IEs had memory leaks because one could make circular references through the DOM and JavaScript, and they each had separate garbage collectors. Well working garbage collection is not an afterthought, and you can't get two distinct garbage systems to work together in the seamless manner that a single system can operate.
I have honestly no idea what you're saying isn't possible.
I've been writing a bunch of Rust code running in browsers, for work and play. I'm not futzing with the details, I just use wasm-bindgen and web-sys.
I've honestly never worried about garbage collection before, one of the professional hazards of working with a well-designed Rust API. I guess that web-sys provides types with a Drop impl that tell the browser side the object is dead.
> without having to manually mark as held and free.
I guess that's what you're worried about? It's never been a concern of mine. Maybe if one were writing WASM by hand?
Being perfectly frank: this really resonated with me. I fought through all this about 18 months ago and it was worth it. Tried again 3 months ago and it was impossible, gave it a solid 3-5 days before I pulled back and just did FFI on native and JS on web.
WASM has an extremely high learning curve and this post did a great job representing it. Even the "oh you just need X Y and Z" post is indecipherable to me and I learned the acronyms, again, just 3 months ago.
The post carefully avoids making any more expansive claims than "I tried to use WASM in the most obvious way apparent to me and ran into a lot of difficulties". What this implies for the grandiose claims of some WASM boosters is left to the reader to determine. (And I'd describe myself as fairly pro-WASM myself.) In that sense it's a great counterbalance to the hundreds of "here's how I used WASM to accomplish some trivial or meaningless task" pieces you see out there.
From what I can tell the issue here is that the author has produced a WASM module with minified dynamic dependencies. This is not an issue with the runtimes but with the earlier compilation toolchain.
In another thread it was suggested that Emscripten was used, but its runtime wasn’t provided.
As someone who's been toying with wasm runtimes for the last year or two in bursts, I feel this pain so much. Just yesterday I was like "hey let's try this thing I tried last year but was too messy, WASM had advanced".
And oh man, what a ride it was. The WASM ecosystem DX is in a terrible state.
First, the "choose a runtime" part is a timesink in itself. Which one is WASI compliant, what are the supported features, is it active, is it stable, how are the docs (mostly terrible), what are the issues? And invariably you get into the Wasmer debate on the way and using other engines with WASI or their engine and WASIX. If I depend on a VC funded runtime, will the company survive, will it be free?
After choosing a runtime, installing it, setting the host and guest project up, you can finally.. wait, what? do I need to use wit-bindgen first to generate bindings? No, wait the compiler uses `wasi_snapshot_preview1` for which I need to use adapters to use with `wit-bindgen`? Another rabbit hole to dive into.
Then finally, you start working and try to do it by the docs - you get some minimal cases covered and are left to sift through the code, comments and github issues to actually understand what the authors meant and how we should use the API. Instead of showing `guest language` code you compile and embed, they show minified WAT that exports a function returning a hardcoded number. "Oh sorry, you want to try anything else? Good luck and grab your towel while you navigate the WASM universe with it's insane amount of WASM 101 tutorials."
I understand it's early, I understand it's in progress, but man there is a ton to improve on the DX side alone. It feels like the race went to "enable every language ever and make the fastest runtime around" vs "actually support people to properly adopt and build upon the ecosystem". And if it feels like that for someone who understands what's going on and has patience to sift through the docs and the code, I can't imagine how it is for somebody just hearing about it or starting to work on it.
In the end, I managed to implement what I wanted, but it took me a ton of glue and deep dives and searches just to get it running in it's primitive state relying on API's that will break in a month or two. And while I'm okay with sniffing that much glue for a weekend project, a lot of developers won't be, reducing the reach and adoption by a large margin.
Don't get the rant wrong tho - I absolutely love WASM and find it super fun. Hell, I even enjoy reading WASM code itself and figuring out what it does. But damn, the DX side _needs_ some serious improvements.
> Wasmer debate on the way and using other engines with WASI or their engine and WASIX
Thanks for bringing this. The runtime will be free, as our monetization plans are outside of the infrastructure. At the end, Wasmer Runtime is way more than just an engine, is a universal API for using WebAssembly. As such, you can even use other engines in Wasmer (such as JavascriptCore [1] or the browser [2]) under the hood.
On top of that, we are also working on WASIX [3] with hopes for being adopted by the community and becoming a standard. WASIX is not something proprietary of Wasmer, even though at the moment we are the only runtime supporting it.
It's normal for JS tooling to minify symbol names in order to shrink the amount of JS being pushed to user agents; those a... symbols might actually have sensible names somewhere along the compilation pipeline.
I do wish this was less common. It's kinda hostile to any users who might want to fire up a debugger for learning or solving a problem with your site, and as we can see can mess with developers too.
And while it does speed up load time a bit in JS engines, this was never a measurable amount in any JS library I used. Bytes pushed out from the site were only slightly lower than the non-minified, since both were compressed in transit anyway.
Assuming they are working and were created yeah. It just seems like another hack layer on top of something that really doesn't seem to add much in the first place. I mean, if you're wanting every last bit of performance, maybe just use WASM or something? Bonus, always obfuscated.
Examining this kind of (unintentionally) obfuscated code can be a learning opportunity as well.
Forgetting the intuitive meaning of symbols is often helpful in doing mathematical proofs (I think Weyl has a famous quote about that). Who knows, maybe the same is true of programming! I've been trying to train myself to to read code purely based on their form, replacing variables names by purely unsuggestive random letters and training myself to visually pattern match first elementary, then increasingly complicated patterns. Still too early to tell whether this will lead anywhere. But in some cases it definitely saved me time by removing the temptation to wonder what the original author's "thinking" was behind this or that choice of variable/function name.
The native Golang wasm support isn’t bad actually. It gives you a js file with the bindings to import, then load the wasm binary that got built with the normal ‘go build’ command
Not the smallest binary, but I had good success with it. Needs a bit of glue for making it run nicely in both browser and node environment at the same time because of different imports and runtime, but outside of publishing npm packages you don’t really need that anyway
When I ran into that situation in the past, the imports had reasonable names (e.g. "fd_write"). Which WebAssembly toolchain is just using letters of the alphabet? Is that common practice now?
It seems libjxl does not compile for WASI, but for emscripten. Hence that minified JS is likely the emscripten glue code that's required at runtime, but which the libjxl documentation fails to mention clearly (as it just tells users to run some python script and get a magic file included at runtime). To run that outside the browser, the author will need to generate a emcripten runtime that works with WASI, which as far as I know is not possible.
I really don't get the WebAssembly design. It is not Assembly, it doesn't offer any of the benefits of Assembly. The only thing that makes it assembly-like is that it is completely unreadable and mostly unwritable to humans. For those who don't know, WebAssembly implements a stack machine VM, the only appreciable effect of this design decision is that all code becomes spaghetti code.
We could have made a simple AOT-compileable Basic/C/Java-like language, called it a web standard and let people decide for themselves how many layers of compilers they would like to stick in front. But no, that would make everything too easy to debug. We gotta have an incomprehensible binary file, because that looks fast the same way a red car does.
WASM doesn't require a VM, you can compile it AOT just fine. There is really no difference between a C-like language that every browser engine would have to parse and compile, and WASM. Except that parsing WASM is easier and faster, and it uses less bandwidth to send over the wire.
It is a virtual machine by the normal definition of the word, doesn't mean it can't be AOT compiled.
The part of parsing that can be skipped is a minuscule part of compilation, won't do much for performance.
I guess that the binary representation could theoretically be a bit smaller. However the current status is that compilers need to ship a fair amount runtime stuff, like an allocator and basic string handling, so the resulting binary isn't necessarily smaller than the source code of a fully featured language. That of course weighs down compilation as well.
When I said WASM doesn't require a VM, I meant it doesn't need fallback byte-code interpreters, multi-tier instrumented tracing compilers, invasive managed memory, deoptimization checks, etc. Just compiling it once and running that compiled code is fine.
I see this kind of problem hit people all the time -- the space is still early and teams are figuring it out. Sometimes its just that there is nuanced differences in some of these tools that make it hard to understand "why something doesn't work here since it works there".. like jQuery abstracted browser APIs and unified them into a single one.
We built Extism[0] to help ease much of this complexity, and if OP would try this instead of the lower level runtimes directly, they'd see Wasm can actually "just work" the way they want it to.
I'm guessing (it appears to be true) that every single one of the people writing about their issues here (1) didn't try Spin, because (2) they're intent on targeting a web browser as a runtime environment, and (3) Spin does not run there.
The DX in Wasm is pretty complicated, but it gets a lot better IMO through Spin.
But that was a marketing name and a campaign that worked apparently too well. I try to think of it as a naming failure, and tell everyone I can that Wasm isn't for web browsers. I mean, it can run in a web browser, sure, but if what you're building is a server, the chances that you're going to want to run it in a web browser in the end are practically nil, aren't they? (I guess for the author, that wasn't exactly true.)
And everyone I know is building servers, or something like a component that should sit behind a server, or libraries for command line. Nobody I know is building flash games, or planning on extracting compute out of their users who arrive via web-browser, none of our code needs to run in the browser sandbox, and nobody I know would pick up Wasm for that reason (unless by some fluke they really needed to do that, and it was the thing they had been told that Wasm was supposed to do. Which many people apparently have been told by now!)
It is for sure a thing that has been promised (Wasm is for web browsers) and must be delivered, but I don't think it's where the implementation of Wasm is going to take off right now. I think people need to take their server-side apps and achieve those 1000x performance gains that have been promised, reshape their runtime profiles to be serverless-capable as it's done with the Spin runtime, and then I really do think things will start to improve for the rest of us as the tooling matures.
If you spend a lot of money on servers and you can spend less money on servers by making them start up very quickly, and shutting them down when they're not actively serving requests, then you can save a lot of money. That's the story that Spin is telling, and that's where I expect to see the movement happening at first, especially if we are heading into a recession as has been predicted by almost everybody. If you are spending money on waste, now is a great time to try and cut it out. Web Assembly has great potential to help with this.
> I mean, it can run in a web browser, sure, but if what you're building is a server, the chances that you're going to want to run it in a web browser in the end are practically nil, aren't they?
We’ve been able to compile almost any language to native code runnable on a server for several decades. That problem is solved. We don’t need any new tech here.
The big thing about WebAssembly was that it promised us the ability to do that in the browser too, where we until recently were cursed with JavaScript as our only option to run code.
So yes. People intend to use WebAssembly in the browser. For many people that is its primary and only use-case.
> That problem is solved. We don’t need any new tech here.
Solved by who exactly? You can't tell me with a straight face if I can show you a way that you can spend 1/100 of what you currently do on servers that run full-time by changing the architecture into a serverless one, with web assembly providing the faster startup times required to make it perform acceptably, that you're not interested in saving that money.
Performance is always a goal. Anyone that tells you performance is a solved problem is lying.
But perhaps we've heard "serverless is going to save you" one too many times from AWS and friends, and now we don't want to hear it anymore.
News flash, AWS and Azure both want to sell you more servers. So they cloud providers are all not going to seriously invest in serverless. It's not a solved problem at all. They'd all love it if serverless was an abominable failure altogether. Which is how I'll bet you actually perceive all that mess, am I right? Give me more servers, they actually fucking work.
The goal for me has never been to run code in web browsers. It has always been to write code in whatever language suits me, and run it wherever, especially libraries for reuse. To avoid rewriting the same code over and over. I never pick the browser as a target, because it puts so many things irredeemably outside of my control (which it must, because it's not my machine.)
> Ah, I see now why they didn’t feel the need to document these imports. They’re so simple and obvious, it probably never occurred to anybody that this would benefit from documentation. I like that the list starts with “b” and ends with “a”.
I just turn this stuff off for now, the only use I can see for it is unskippable / unblockable ads no matter what neat things are theoretically possible. Soon we'll have "booter" sites which suck their data from who knows where, without the browser being able to intercept and block the baddies.
There is nothing that WASM can do to interfere with adblocking that Javascript can not already do. The reason ads remain blockable is logistics. You want to serve the ads from a different server, if for no other reason than billing.
Standards like WASI are meant to define a common runtime for WASM modules. A WASM module built for WASI would fit the author's expectations better (as they could possibly find an all-in-one WASM+WASI library or attach a WASI implementation to a WASM library to run the WASM file), though I don't know anything about emscripten and WASI compatibility.
Edit: You can use the STANDALONE_WASM emscripten option to make it not depend on the emscripten runtime and use WASI instead. This is what the author should use.