jankscripten
is a compiler and runtime system that targets WebAssembly
and supports multiple front-end languages:
-
JankyScript is a language with JavaScript syntax. It supports higher-order functions and a standard library that is similar to JavaScript. The compiler translates JankyScript to NotWasm by performing type inference and closure conversion.
-
NotWasm is an explicitly-typed lower-level language that does not support higher-order functions. However, it does support:
-
Garbage collection: at the moment, a simple mark-and-sweep collector with a stop-and-copy region for floating point numbers.
-
String values: Worth mentioning, since WebAssembly does not support strings natively.
-
Any-typed values (a.k.a. type dynamic): a failed downcast crashes the program with an unrecoverable error (i.e., a WebAssembly trap).
-
Monotonic dynamic objects with prototype inheritance: The fields of these objects are of type Any, and new fields may be added dynamically. However, these objects do not support field deletion. Field lookup in dynamic objects is optimized using inline caching.
-
Hash tables and arrays: these store Any-typed values.
-
Explicit closures: Although NotWasm does not support higher-order functions, it has a representation for closures (i.e., code + environment pointer) which is designed to fit into Any-typed values.
-
ML-style mutable references
-
JankyScript is not JavaScript. It makes several simplifying assumptions, which it inherits from NotWasm. However, as long as you’re working with “sane” JavaScript, e.g., JavaScript generated by a compiler from a saner programming language, you can use JankyScript. With more effort, you can instead use NotWasm as an intermediate language for a compiler that targets WebAssembly.
jankscripten
is written in Rust, and uses the Rust WebAssembly
toolchain. It depends on Rust packages that link to libssl on Linux,
and it relies on the Z3 SMT Solver. Finally, the test suite relies on
Node. Follow these steps to install the prerequisites:
-
Install the Rust toolchain. We require Rust 1.51.0 (released March 2021). More recent versions of Rust changed the ABI of the WebAssembly backend. We will fix this soon. After installing Rust, run the following commands:
rustup toolchain add 1.51.0 rustup default 1.51.0
-
Install Node. We require Node 11 or higher.
-
Install the Rust WebAssembly toolchain:
rustup target add wasm32-unknown-unknown
-
Install the Wasm-Bindgen CLI, to allow Rust unit tests to run in WebAssembly:
cargo install wasm-bindgen-cli
-
On Ubuntu Linux, install libssl and pkg-config:
sudo apt-get install libssl-dev pkg-config
-
Install the Z3 library. On Ubuntu Linux:
sudo apt-get install libz3-dev
-
Build the
jankscripten
compiler:cargo build
-
Build the
jankscripten
runtime[1]:(cd runtime && cargo build)
-
Build the integration testing tool:
(cd integration_tests && npm install)
cargo test
(cd runtime && cargo test) # Runs tests using WebAssembly
(cd integration_tests && npx jest)
To compile filename.ext
to WebAssembly:
./bin/jankscripten compile filename.ext
NOTE: The supported extensions are .js and .notwasm.
To run a compiled WebAssembly program with the jankscripten runtime:
./bin/run-node filename.wasm
To debug or profile a compiled WebAssembly program:
node --inspect-brk bin/run FILENAME.wasm
The Chrome debugger uses source maps correctly to show the original Rust code. You can use Visual Studio Code or Edge, but source maps do not appear to work correctly. See the Node Debugging Guide for more information.
NotWasm is a somewhat low-level language that compiles to WebAssembly. It supports several data structures that WebAssembly does not natively support. It also supports garbage collection (presently, a simple mark-and-sweep collector). NotWasm programs are linked to a runtime system, which is written in Rust and compiled to WebAssembly.
NotWasm programs use the following concrete syntax for types:
Result Types | |||
R | := | T | Result of type T |
void |
|||
Types | |||
T | := | any |
Unknown type (occupies 64-bits) |
i32 |
|||
f64 |
|||
bool |
|||
str |
|||
Array |
|||
DynObject |
|||
HT |
|||
( T ,..., T**) -> ** R |
|||
clos ( T ,..., T**) -> ** R |
|||
Ref( T) |
|||
env |
NotWasm programs use a psuedo-ANF: atoms do no not alter control-flow or allocate memory, expressions may allocate memory but do not alter control-flow, and statements function bodies that may alter the control-flow of the program.
Programs may reference primitive operations that are defined in the
NotWasm runtime system. These operations
are imported at the top of stdlib.notwasm
, which is in the root of the
repository.
Atoms use the following concrete syntax:
bop | := | + |
Integer addition |
- |
|||
* |
|||
> |
|||
< |
|||
>= |
|||
<= |
|||
== |
|||
=== |
|||
+. |
|||
-. |
|||
*. |
|||
/. |
|||
b | := | true |
false |
n | := | ... | Integer literals |
f | := | ... | Floating point literals |
s | := | "..." |
String literals |
a | := | b | |
n | |||
f | |||
s | |||
null |
|||
x | |||
@ prima( a1 ... an) |
|||
* a:T |
|||
a1**. **s |
|||
a1 bop a2 |
Expressions have the following concrete syntax:
e | := | a | Atom |
! prime( x1 ... xn) |
|||
xf( x1 ... xn) |
|||
xf!( xe, x1 ... xn) |
|||
a**. **x = a |
Statements have the following concrete syntax:
blk | := | { s1 ... sn} |
|
s | := | var x**: T= ** e**; ** |
Declare a variable of type T |
x = e**; ** |
|||
**if ( **a ) blk else blk |
|||
loop blk |
|||
return a**; ** |
|||
break l**; ** |
|||
* x= e**; ** |
A program is a list of global variables, followed by a list of functions. Global variables have the following concrete syntax:
var x**: T= ** a**; ** |
Functions have the following concrete syntax:
function xf( x1**: T1 ... xn: Tn): R **blk |
There must be a function called main
that received no arguments and
does not return a result.
- This is a separate step because it targets WebAssembly and not native code