I’ve been working on p2w, a side project that compiles a subset of Python to WebAssembly, using the WASM GC proposal for memory management. The compiler translates Python source directly to WAT, with no intermediate libraries (IL), which makes the codegen surprisingly easy to follow, though probably not good enough if we ever want proper optimisation passes.
The project was inspired by Eli Bendersky’s blog post Compiling Scheme to WebAssembly and his bobscheme compiler, as well as other previous projects, including several Python-to-JavaScript compilers.
What’s the point?
Mostly curiosity: how far can Python semantics be mapped onto WebAssembly and its GC extensions? Can we run Python in the browser without shipping a full interpreter?
I don’t pretend it has any industrial quality, and I’m not sure if this could be useful to anyone (even myself). But it does compile and run most of the close to 100 toy example programs I use to measure its progress, which is satisfying enough.
What works today (at least in part)
Data types: integers (arbitrary precision via i31/i64 boxing), floats, booleans, strings, bytes, lists, tuples, dicts, sets
Control flow: if/elif/else, for/while with break/continue/else, match statements
100+ golden test programs, property-based testing with Hypothesis
On performance
Benchmarks show x3 to x10 acceleration over CPython — but only when using unboxed native integers (i32) via type annotations like x: i32, so it’s not really a fair comparison, but it’s also largely the point of the experiment. This comes at the expense of strict Python compatibility, since i32 will happily overflow where Python integers would not.
What this is not
This is a side project. Many Python features are still missing or incomplete. There’s no package ecosystem, no stdlib beyond builtins, etc. No serious application has been written in it yet. Expect (very) rough edges.
Very cool! I gotta say, I am amused that the target language is called “Wat”
(from your README:)
Nice. I was under the impression that WebAssembly code ran on a separate thread to anything that can use the DOM. Is that no longer the case? Am I out of date? If it’s now possible for Python code to compile directly to something that runs in a browser and has full access to the DOM, that’s extremely cool and I want to delve into it.
Do you have any sort of list of features that aren’t supported? And, is it your goal ultimately to support all features, or do you intend for this to forever be restricted (like PyPy’s rpy)?
How does this interaction with JS work? Is every call done asynchronously by sending a message to the JS event loop and getting something back? At what point does this happen? For example,with js.document.getElementById("chart"), a naive approach would be “hey JS, what is document? hey JS, take that thing, what is its getElementById? hey JS, call that thing with the parameter "chart" and give me back the result”, which would make for a pretty inefficient messaging system. OTOH, batching things up would mean you have to figure out when to actually send the batch across, increasing complexity.
Completely fair.
Cool, maybe I’ll try the core CPython test suite and see how much fails. There’s going to be a ton of stuff in the stdlib that will fail (presumably things like opening files and sockets won’t be permitted), but it would be neat to get a rundown of each failure categorizing them as “Not possible”, “Planned/desired”, “Not out of the question but I only have 168 hours a week”, etc.
The parts of the CPython test suite that are used for emscripten builds might be a better check, as I assume that this compiler will have most, if not all, of the same limitations that emscripten does.
It almost certainly will do. But simple web assembly modules can be much much smaller, than a full posix emulation setup from emscripten (which can also be finecky).
Okay, so, one big one that’s gonna be needed is imports I don’t think I can really try this without the ability to import other modules. But I did find a couple of fairly simple scripts of mine that don’t have a single import in them, and got this error when trying to compile:
This is using wabt 1.0.32 as provided by Debian Bookworm, and also 1.0.36 as in Devuan Excalibur. What version of wabt have you been using? I see on GitHub that they’re up to 1.0.40, would have to try that next.
It’s extremely ad-hoc at this point, but all happens in a single thread, to answer your question. There must be (given the current specs, AFAIK, though I also understand there are proposals under way to get rid of it) a JS module between the WASM context and the browser.
Do you have any sort of list of features that aren’t supported?
In addition to “eval()”, I should add that the general philosophy is that things work better (or at all) when the Python source to be compiled is properly annotated.
It seems this was added with the GC proposal, so wat2wasm cannot compile it:
Add a new form of typed reference typeref $t and a nullable variant (ref null $t), where $t is a type index; can be used as both a value type or an element type for tables
I came to this conclusion while trying to compile the demos, since I’m not very familiar with the WebAssembly specs.
Alternatively, on Linux distros that tend to favor stable packages, I install homebrew to get access to recent packages without breaking the system, and contains wasm-tools: