Scripting Languages and Compiled Languages in WebAssembly

The promise of WebAssembly is that it can be a common runtime for all sorts of languages. But there are differences between how we traditionally write in scripting languages (JavaScript, Python, Ruby) versus compiled languages (C/C++, Go, Rust). There are also compiled languages that run in an interpreter, like Java running in a JVM. In this post, we survey the WebAssembly landscape to see what is happening along these fronts.

A Bird’s Eye View of Programming Languages

Taking a look at the most popular programming languages, we get a glimpse of three different types of programming languages:

1 JavaScript
2 Python
2 Java
4 PHP
5 CSS
5 C++
5 C#
8 TypeScript
9 Ruby
10 C

In the top 10, we see a few compiled languages, a pair of virtual machine languages, and five scripting languages.

Compiled Languages

In the top ten list, C++ (#5) and C (#10) are languages compiled to native. Rust and Go are two other popular compiled languages that didn’t quite make the top 10. In general, these languages are compiled to a binary format specific to the architecture and operating system. A compiled language does not need an interpreter or virtual machine to execute the program.

Compiled languages have enjoyed early WebAssembly support. In fact, all four (C, C++, Rust, and Go) can be compiled to WebAssembly. This might seem surprising, as these languages are typically compiled per architecture and OS. That is, a C program usually must be compiled one way to run on ARM and Linux, and compiled a different way to run on macOS with an Intel processor (and sometimes the code must be changed per platform). But when they are compiled to WebAssembly, they can run on any WebAssembly runtime regardless of operating system, architecture, or other similar low-level details.

When it comes to WebAssembly compiler support, Rust and C are the furthest along, each with impeccable support. C++ support comes by way of the C compilers, and consequently is also well supported. Go’s official distribution (owned by Google) is a little behind, but the TinyGo project has made up the difference. All of these languages are production-ready on WebAssembly.

Effectively, compiling one of these “native” languages to WebAssembly means they behave more like virtual machine languages.

Virtual Machine Languages

In the top ten, there are two virtual machine languages: Java (#2) and C# (#5). Other popular virtual machine languages include Scala, F#, and Kotlin. These languages are compiled, but not to native code. They are compiled to an intermediate format generically called “bytecode.” That code is then fed into a special runtime or virtual machine (the JVM for Java or the CLR for C#).

.NET (including C#, F#, and ASP.net) has excellent support for WebAssembly in the browser, and we know that the Microsoft team is working on many other enhancements. Kotlin support is coming along rapidly, and there are a number of projects for Java and Scala.

As these languages are ported to WebAssembly, they will no longer need their language-specific runtimes. For example, Kotlin will not need the JVM. It will be compiled directly to WebAssembly that can be executed directly on a WebAssembly runtime.

Right now support for these languages is less robust than the compiled languages we looked at above (though all are rapidly improving). But if the momentum keeps as it is today, we will soon see many virtual machine languages compiling to WebAssembly.

Scripting (Interpreted) Languages

Five scripting languages made the top ten programming languages list: JavaScript (#1), Python (#2), PHP (#4), TypeScript (#8), and Ruby (#9). Scripting languages are typically not compiled. Instead, the code is fed directly into an interpreter at runtime. The interpreter reads and executes the program on-the-fly. Because the code is loaded and executed in tandem, scripting language environments sometimes do things that compiled languages cannot. It is, for example, trivial to include a new piece of source code at runtime, while that is not a feature of most compiled languages.

While work on WebAssembly support for compiled languages has focused on compiling the programs themselves to WebAssembly, the work with scripting languages has focused on compiling the scripting runtime to WebAssembly. In this model, the engine is loaded into the WebAssembly runtime, and then the scripts are loaded into the engine. The two-tier execution model this presents is more complicated than the compiled languages. But by sticking to the familiar, this model is also the easiest for developers in that language. For example, they do not need to learn how to compile their previously interpreted code.

Of the scripting languages in the top ten, JavaScript is under the most active development. Notably, the SpiderMonkey engine that powers Firefox’s JavaScript implementation now compiles to WebAssembly.

As of January, 2022, both Ruby and Python can now compile their respective runtimes to WebAssembly. While support for Wasm is early in both of these, we believe they will mature very quickly now that the hard part is done. (We have a few blog posts on the horizon to show Ruby and Python in action!)

TypeScript is usually translated to JavaScript. Then the generated file is fed into the JavaScript interpreter. So JavaScript’s success automatically accrues to TypeScript. But TypeScript developers have an interesting alternative. AssemblyScript is a superset of TypeScript that compiles to WebAssembly. While it keeps the feel of a scripting language, it ultimately behaves like a compiled language.

Finally, PHP is slightly behind the other scripting languages in support. A few non-official projects have added limited support for executing PHP in the browser by loading a WebAssembly-compiled PHP interpreter into the browser and executing code there. Some of the examples are exciting. PHP, with it’s request/response model, would be an excellent language for authoring WebAssembly cloud applications. We are excited about the future prospects.

Scripting Languages May Become Compiled Languages

There is one final observation regarding current differences in languages. With WebAssembly, compiled languages like C, Rust, and Go effectively become virtual machine languages. Can we expect the same thing to occur in the scripting world? Will scripting languages be pre-compiled into WebAssembly binaries?

There are some reasons to think that the answer to this question will be yes.

  • A shared model of execution makes running WebAssembly simpler. And the two-stage loading of scripting languages could be replaced by a single stage.
  • Performance, too, is an issue. It is hard to optimize a two-stage process as much as one can optimize a single-stage process.
  • Code redundancy is a third reason: Right now, every WebAssembly-targeted Python script must ship with a complete copy of the Python environment. By compiling the end code to Python, unused parts of the Python environment could be stripped out at compile time, making smaller application sizes
  • AssemblyScript already is leading the way by sharing the design of TypeScript, but compiling directly to WebAssembly bytecode.

JavaScript, being the most popular programming language, is likely the one we should watch for indications of what the future of scripting languages in WebAssembly will be. But presently, it is safe to say that the two-stage interpreter model is the primary method for executing scripting languages in WebAssembly.

Conclusion

Support for executing popular languages in WebAssembly is growing fast. We surveyed the top 10 languages, but if we were to expand to the top 20, we would see the same pattern playing out. Many languages, popular and esoteric, are gaining WebAssembly support. As WebAssembly itself gains new features, we have every reason to believe that WebAssembly will become a popular target for an increasing array of languages.

Interested in learning more?

Get Updates