Stephen Crane

Low-Level JavaScript

This summer I’m taking a short break from grad school and working as a
research intern at Mozilla. Awesome experience so far, and it’s great
to be back to working on open source projects! I don’t think I’ve ever
cloned so many github repos in such a short time.

Ever wanted to have manual memory allocation in JavaScript? How about
types? One of the projects that I’m helping out with is
LLJS, a typed dialect of JavaScript which resembles
C/C++. It features manual memory allocation when you want it,
co-existing alongside JavaScript’s normal garbage-collected object
system. The goal here is to allow programmers to write fast JS code
with a familiar syntax. Definitely be sure to check out the
interactive demos to see examples of LLJS in action.

What’s new with LLJS?

We recently added arrays, with both statically stack-allocated and
dynamically heap-allocated variants. These resemble C and C++ array
syntax, respectively. I also added union types, to complement our
existing struct types. These behave precisely as you would expect
from C. Recently I added syntax for defining structs and unions inside
other structs or unions, which significantly simplifies writings
structs.

I also worked on optimizing our malloc implementation, which is much
faster now. We use a naive malloc algorithm from the K&R book, but
since we don’t actually need to worry about paging, I modified this so
that all memory is on a single page. This cut out function calls for
allocating new pages, speeding allocation up quite a bit.

My mentor here at Mozilla, Michael Bebenita, added functions and
optional constructors to structs, which are beginning to look a lot
like C++ structs rather than C structs.

Finally, perhaps the most exciting new thing in LLJS is
memory checking!
Tim Disney, another intern here at Mozilla, implemented Valgrind style
memory checking for LLJS. It can currently detect the most common
memory errors: use after free, uninitialized reads, double frees, and
memory leaks.

Esprima in LLJS

As part of another project, I needed super fast JS parsing in
JS. Since I was already working with LLJS, I figured I might as well
try it out on a larger scale and see if I couldn’t get
Esprima ported over to LLJS and using structs
for the generated AST. I hoped this would make an already fast parser
even faster and leaner by using manual memory allocation.

Well, over 4000 lines of LLJS later, I finished the port. String
handling is somewhat inefficient (it creates a new C string for every
string in the program), but it works! Check out the sources on
github if you’re interested.

So, was it faster? Well… not really. As it turns out, modern JS
engines are very good at allocating objects, so manual allocation does
not appear to be faster than the engine object allocation. The LLJS
version does use about %10 less memory when parsing large JS sources,
so that’s a win. From some preliminary testing, traversing the AST
appears to be faster with LLJS, since property access is fast, but
this comes at the cost of making traversal code harder to write.

Where I think we can definitely win, though, is code which allocates
and frees code very often. Manual memory reuse and freeing could
provide speedups here over engine garbage collection for some
applications (note: I have yet to test this…).

What’s next?

In the beginning of the summer I started off adding source map support
to LLJS, so that debug tools in the browser will show the
corresponding LLJS sources instead of the compiled JS sources. This is
mostly implemented in
escodegen, which we use
to generate the JS code from a rewritten AST. This got a little bogged
down due to a bug in the chrome devtools
(not loading sources from a data URI source-map),
so I haven’t finished it off yet. Pretty much all that’s left to do is
testing there, though.

As far as language features go, I will definitely be adding support
for enum types as soon as I get the chance. We are also looking at
implementing bit fields
as a convenience for bit packing in structs.

If you have more ideas for where LLJS should head, please start an
issue on github or (even better) toss us a pull request. Most
importantly though, go try it out! If you find it helpful (or
frustrating), let us know.