8 ms·
Show HN: Jaws – a JavaScript to WASM ahead-of-time compiler
I've open sourced a JavaScript to WASM compiler. It's an experimental tool, but given the semantics I already implemented, I'm fairly certain I am able to eventually cover 100% of JavaScript spec. Any ideas, questions or critique welcomed! If you are interested in WASM, especially with new proposals like WASM GC or exception handling, it might be a good source of seeing these features in action - the project has a few thousand lines of hand written WAT so far.
- samuelstros 2y agonice, "run JS without (browser) runtime" is coming. perforr, jaws, or another project will eventually succeed.
- jahewson 2y agobun?
- thrw42A8N 2y agoBun is a runtime.. If you're referring to the fact it can produce a single binary, Node.js can do that too.
- drogus 2y agoYeah, bun will still include V8 in the generated binary
- thrw42A8N 2y agoBun will include the Bun runtime with JavaScriptCore, Node.js will include the Node.js runtime with V8.
- IggleSniggle 2y agoBun uses JavaScriptCore as its runtime, not V8
- codesnik 2y agoalso https://docs.docker.com/desktop/features/wasm/ https://docs.docker.com/desktop/features/wasm/
- philipwhiuk 2y agoYou mean like Node?
- usrusr 2y agoI guess "yes" wouldn't be an incorrect answer, but a more nuanced look might want to consider that node does actually contain the js runtime of a browser, just minus all the rest of that browser. A wasm runtime can be far more lightweight than node, not only because node itself is a wasm runtime, plus a lot of other things. Wasm could (or does, already?) occupy a sweet spot where platform independent extensibility is desired, but where that is not enough of a core feature to make inclusion of a heavier runtime advisable. Kind of like how Lua has its place, but with more focus on near-native speed and less focus on ad-hoc programming (aka scripting).
- nilslice 2y agocheck out https://extism.org https://extism.org!
- deleted 2y ago[deleted]
- asabla 2y agoTitle might need to include "Show HN". Very cool and interesting project! How are build times? And how big are the artifacts? I'll for sure keep an eye on this, and add it to my ever expanding list of tech to explore. Thank you for sharing!
- deleted 2y ago[deleted]
- drogus 2y agoThe binaries are a few KBs at the moment. I haven't measured the build times, cause it's too early for it to mean much. The amount of supported types and functions is very limited, so it will change a lot over time as I add more stuff. One interesting thing is that `eval()` support will require custom WebAssembly host functions, cause you can't do custom code generation in WASM. Thus by default the project will assume "no eval" compilation. In this mode it will be possible to do a lot of optimizations, like for example remove unused parts of the language/types, do certain optimiztions knowing exactly what types the script is dealing with etc. So a simple script that doesn't use a lot of the builtins should eventually result in a fairly small binary.
- deleted 2y ago[deleted]
- lagrange77 2y agoVery cool! Does it support ArrayBuffers?
- drogus 2y agoNot yet, it's very early stage where I'm mostly implementing full JavaScript semantics (and hopefully finding some funding/support for the project), but as soon as I'm done with async/await and a few simpler missing pieces, I will start implementing JS builtin types
- lagrange77 2y agoCool, thanks and all the best with the project.
- OscarDC 2y agoI read the README.md of the project but I'm still not sure: What's the expected usage of this? How does the outputed WASM code then interacts with a runtime (and with which, is it intended to be a tool compatible with browsers and other WASM runtimes or is it only compatible with a runtime linked to the project)? Somewhat linked questions: How does it react if it encounters e.g. web APIs inside the JavaScript code or other global identifiers only defined in some environment (e.g. a recent browser, Node.js etc.)? Or if it's not intended for those environments, how are you supposed to do I/O when using this?
- drogus 2y agoThese are very good questions, I'll respond here, but I'll also add more info to the README. This project is mainly targeting WebAssembly usage on the server, cause I think it makes little sense to run JavaScript in WebAssembly in JavaScript (although time will tell, maybe it will be useful for sandboxing frontend plugins?). Regardless if it's running in the browser or a backend runtime like WasmTime or WasmEdge, at the moment running JavaScript inside WebAssembly is not ideal. You either have to compile a JS engine like V8 or SpiderMonkey to WASM and then use it to run your script or you have to settle for an "almost JavaScript" language like AssemblyScript. This is a limiting factor for running server workloads. For example Fastly uses SpiderMonkey for their WASM workers, but it means that each instance uses 5-10MBs of memory even for a hello world. Shopify, on the other hand, uses WASM for customizing server side of their shops, and they decided they only allow WASM binaries up to 250KBs, which is a no-go for embedding any interpreter. Thus their "blessed" language is AssemblyScript. They outline reasons for that here: https://shopify.engineering/shopify-webassembly https://shopify.engineering/shopify-webassembly This is all due to a fact that historically WASM was a very simple runtime. It was relatively easy to compile C code to WASM, just like you compile C code to machine code, but even though a WebAssembly is a kind of interpreter by itself, it wasn't easy to interpret higher level languages on top of it. With new proposals being standardized, like garbage collection support or exception handling support, WebAssembly becomes much more powerful interpreter, with stuff like structs, arrays, function references etc. Jaws leverages that fact translating JS code to WASM code in a way that WASM interprets the resulting code, without the need of a JS engine like SpiderMonkey. In practice it mainly means that a binary generated by Jaws will be probably under 50KBs vs 10MBs when you compile SpiderMonkey to WASM and run your script on top of that. Memory usage will be also significantly lower. For companies like Fastly this would mean orders of magnitude lower memory usage and thus server costs. For companies like Shopify it would mean they could leverage JavaScript code already available (think NPM packkages) and JavaScript ecosystem for people writing plugins for Shopify's backend. > is it intended to be a tool compatible with browsers and other WASM runtimes or is it only compatible with a runtime linked to the project The only runtime the project uses is WebAssembly. The generated code is mostly 3k lines of WAT code form this file: https://github.com/drogus/jaws/blob/main/src/wat/template.wat https://github.com/drogus/jaws/blob/main/src/wat/template.wa... and whatever your JS code is translated to. For example for a very simple program like "console.log('foo')" the entire "generated" part is this: https://gist.github.com/drogus/1c49c25ed0b14804b2f27e10d2a79928#file-sample-wat-L2690-L2706 https://gist.github.com/drogus/1c49c25ed0b14804b2f27e10d2a79..., which more or less prepares an argument (with new_static_string) and then calls console.log. Right now I need a bit of glue code on the host, but eventually it will be possible to execute such a binary with any runtime that supports WASIp2, WASM GC and exception handling proposals. > Somewhat linked questions: How does it react if it encounters e.g. web APIs inside the JavaScript code or other global identifiers only defined in some environment (e.g. a recent browser, Node.js etc.)? Or if it's not intended for those environments, how are you supposed to do I/O when using this? None of this is implemented yet, but I can tell you how it will work. I plan to support Node.js APIs through WASI. WASI is a standard for communicating between WASM programs and the outside world. For example WASI defines a standard set of functions you can use to send an HTTP request, or write to STDOUT, or read/write to a file. So when I get to APIs like `fetch` or `fs`, it should work with any runtime that supports WASI preview2. Browsers could also be supported with polyfills, but in this case I/O support is more custom. Like, if you decide you allow WASM programs to write or read files, you would have to provide a mechanism to do that, for example save files to localStorage or an SQLite database compiled to WASM (or I guess even send them to S3 or something along the lines).
- Aldipower 2y agoJAWS is a well-known screen reader for blind people with a more then 30 years history.
- refulgentis 2y agoEven worse, JAWS is a well-known movie with a 49 year history. I strongly believe the screen reader and the WASM compiler both knew this at the time, quite damning.
- drogus 2y agoI am painfully aware the project will never be the first hit in Google by just typing it's name lol Btw, I am very bad at naming, I chose the name cause it has most of the letters that "JS-WASM" has (or all of them if you consider W is just inverted M)
- refulgentis 2y agoDon't even think about it :) I'm just teasing the person I'm replying to, its a way of saying "yeah I don't think that name collision is significant" that A) assumes they desire conversation, so it's okay to reply B) engages without asserting C) expresses disagreement in a jovial matter, rather than confrontational
- benmccann 2y agoJAWSM might be a more unique alternative if you're looking for any. Anyway, it's a cool project. Thanks for sharing!
- chamomeal 2y agoI love the name!
- Aldipower 2y agoApples and peaches. Jaws the movie is another section, the movie section. Jaws and JAWS are both in the software section. There's the potential for conflicted naming. Sorry to be emotionless here, but at least you could respect the fact that there is a popular software with the same name, without making jokes about it.
- mmoskal 2y agoBack in the day I did an almost-Typescript (though much closer than assembly script) to embedded ARM compiler. Some of the techniques may be useful. https://www.microsoft.com/en-us/research/uploads/prod/2019/09/static-typescript-draft2.pdf https://www.microsoft.com/en-us/research/uploads/prod/2019/0...
- drogus 2y agoNice! I'll definitely take a look!
- kengoa 2y ago> I'm fairly certain I am able to eventually cover 100% of JavaScript spec. Any ideas, questions or critique welcomed! Do you have the results of test262_runner.rb? I came to know about test262 at a talk by the porffor's author and something like https://github.com/CanadaHonk/porffor?tab=readme-ov-file#test262 https://github.com/CanadaHonk/porffor?tab=readme-ov-file#tes... in README would be great to show this progress. Great project by the way!
- drogus 2y agoYeah, at the moment it's passing about 12% of tests, but there is a lot of low hanging fruits to implement, especially considering I started the project two weeks ago. This doesn't mean I will hit 100% in linear time, unfortunately, cause there is a long tail of builtin types and functions, but as I started by implementing the "hard parts" I left some easy parts not done. For example I implemented only enough syntax to allow running conditionals and a while loop cause it was needed for the test262 harness, but I left all the other loops (for, for in, for of, do while) and conditional expressions (switch) unimplemented. Implementing them will be more or less analogous to existing implementations for if/else and while. Once I finish implementing `await` and generators, which are the last hard to implement semantic concepts, I will be implementing those low hanging fruits. It's hard to say how much coverage that will give me, but just to give an example: currently 1200 tests fails, cause `object["foo"]` syntax is not implemented. Ie. `object.foo` works, but `object["foo"]` does not. It doesn't mean that those 1200 tests automatically will pass, cause they might be testing other stuff, but there is a lot of such relatively simple syntax ommissions that make hundreds of tests fail. And yes, I would love to have a nice graph like porffor has! :D
- kengoa 2y agoYes now I'm looking at the tests I can see that the coverage won't increase linearly, but 12% (with some of the hard parts) in two weeks is impressive. I starred the repo, good luck with your journey :)
- philipwhiuk 2y agoWe're one step closer: https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript https://www.destroyallsoftware.com/talks/the-birth-and-death...
- sedatk 2y agoOff-topic, but, why does he pronounce JavaScript "YavaScript"?
- pvg 2y agoWhy do we say 'wat' instead of hwæt? Same thing.
- Kiro 2y agoHow else would you pronounce it?
- pvg 2y agoJ as in Jacques, 'script' like 'esprit'. Zhavascree, stress on final syllable.
- noman-land 2y agoThe joke is that he's speaking from the future where they inexplicably pronounce javascript "yavascript". This is how you know he's really from the future.
- teaearlgraycold 2y agoI get a build error where it can not find "prepend.js". Indeed it appears to not be in the repo.
- drogus 2y agothanks for reporting and sorry about that! I was prepending some JavaScript code to be compiled for test262 harness before, but at the moment it was just an empty file, so I removed it and pushed a fix. Please try again now and let me know if you run into any issues!
- andout_ 2y agoReally clever use of the new WASM GC proposal. All the JS -> WASM compilers so far have basically just been shipping a whole JS engine - this is the first one I've seen that actually tries to map JS constructs directly to WASM primitives.
- drogus 2y agoThanks! Although, to be fair, it's a clever use mostly cause I'm too dumb to write the full interpreter on top of WASM lol
- laurencerowe 2y agoI think both Porffor https://porffor.dev/ https://porffor.dev/ and Static Hermes https://hermesengine.dev/ https://hermesengine.dev/ also take a compilation approach. Would be interesting to see how Jaws compares.
- drogus 2y agoI haven't seen Static Hermes before, I'll take a look, thanks for the link! With regards to porffor. It's a very good project and people behind it are probably much better at writing interpreters/compilers, but the biggest difference is in what WASM features the projects use. porffor uses only core WASM, which means they have to implement a lot more features themselves. Data types like arrays, structs/objects, garbage collection, exception handling etc. I am using WASM features added in proposals standardized very recently, like WASM GC or exception handling, which means I get a lot of the features almost for free. And I suppose that's also why semantics like scopes/closures are really hard to do for projects like porffor or even AssemblyScript and were relatively easy in Jaws. The trade off is mainly in support. porffor compiled binaries can run on a lot of different WASM runtimes, I think there are even some runtimes for Core WASM for embedded devices. In case of Jaws, the runtime needs to support the proposals I use. Currently there are only two runtimes, that I know of, supporting both WASM GC and exception handling: V8 and WasmEdge. I believe more runtimes will get there, like for example WasmTime people are working towards exception handling, but it will take some time. Which is not a problem for me, cause it will definitely take a bit to reach any production level JS compatibility - I think the runtimes will have time to catch up with the proposals by then.
- tomcam 2y agoOr, as some of us call it, a compiler. Nice work btw!
- drogus 2y agoHaha, I haven't even noticed it doesn't make too much sense when I was writing the title :D
- huijzer 2y agoJust when I was thinking: do we need more compilers or do they all exist already? Thank you for working on this! I think it’s a great idea.
- drogus 2y agoJust one more compiler, bro. Just one more, I promise. This is the last one, just one more
- owenpalmer 2y ago> As much as I love writing Rust, I also know it's not a widely popular language Is this true? Rust is hyped like crazy and seems to be used everywhere these days.
- drogus 2y agoHyped and visible on socials doesn't necessarily mean it's widely used, but I guess it also depends on how you define widely. According to most stats I've seen Rust usage is maybe about 5-10% of languages like JavaScript and Python when you look at StackOverflow, indexes like PyPl, or GitHub statistics.
- meiraleal 2y agoWhich puts it already in the second tier of most used languages with big guys like Java, C#. I think Rust will soon join the ranks of JavaScript, Python and C.
- pier25 2y agoSeriously doubt it but time will tell. There aren't that may new projects where rust would be a good fit. And projects in C++ won't be rewritten in rust.
- kfajdsl 2y ago> There aren't that may new projects where rust would be a good fit Aren't almost all new projects that otherwise would have been written in C or C++ good fits for Rust?
- dmitry-vsl 2y agoDoes it implement JS standard library, for example Map, Set etc?
- spankalee 2y agoI really like this approach. Building for WASM directly, rather than trying to also directly generate binaries, means you can rely on WASM GC and the async support that (I think?) is supposed to be part of WASI 0.3.
- pier25 2y agoSo does this execute faster than just running the same code in js or is this for interop for other languages?
- drogus 2y agoHard to say at this point, but I really doubt it will be ever faster than SpiderMonkey or V8 with JIT enabled. Modern JavaScript compilers are quite good at optimizing hot paths through JIT. The use case of this project is to be able to run JavaScript in WebAssembly sandboxed environment. Like, for example Shopify allows you to extend their backend code using WebAssembly, but there is a binary limit capped at 250KBs. At that size you can't really use JavaScript at the moment, cause even simple interpreters like QuickJS take a few MBs compiled to WASM.
- 0xfffafaCrash 2y agoHow are string encoding discrepancies and related utilities dealt with? My vague understanding is that WASM supports UTF-8 while JS supports (potentially malformed) UTF-16
- epcoa 2y agoLike a CPU ISA, the WASM abstract machine doesn't have a concept of strings or encoding. It's just bytes in a linear memory. You implement whatever encoding you want. WASM specifies UTF-8 for the encoding of names in its file formats, but that doesn't involve the runtime VM.
- k__ 2y agoCan you make it deterministic?
- spintin 2y ago[dead]