Quantcast
Channel: Hacker News 50
Viewing all articles
Browse latest Browse all 9433

azakai: Lua in JavaScript: Running a VM in a VM

$
0
0

Comments:"azakai: Lua in JavaScript: Running a VM in a VM"

URL:http://mozakai.blogspot.com/2013/05/lua-in-javascript-running-vm-in-vm.html



Lua is a cool language, and it would be great to run it on the web. It isn't easy to do that though, see this answer (and the thread around it),
[Converting/running Lua in JavaScript] is a recurrent question on the Lua list, i guess because of the superficial similarity of the two languages.Unfortunately, there are many important differences that are not so obvious. Making it work need either a full-blown compiler targeting JS instead of Lua's bytecode, or rewriting the Lua VM in JavaScript. There are in fact projects taking those two approaches, for example lua.js and ljs respectively. But as mentioned in the quote above, it is quite difficult to get the full language (including things like using functions as indexes in tables, possible in Lua but not in JavaScript) with a simple 1-to-1 translation, and writing a full VM is not a quick solution either. Any such efforts miss out on all the work that has already gone into the Lua implementation in C.

There is also a third alternative, which is to compile the Lua C implementation into JavaScript, that is, to run the Lua VM inside your JavaScript VM.


lua.vm.js is a project I started over the last week that does just that, here is a benchmark page and here is a REPL for it.

The Lua VM compiles out of the box using Emscripten, with only a few minor Makefile tweaks. It's straightforward to then create a REPL where you tell the VM to run some code. lua.vm.js does more than that though, as you can see in the example on the REPL page, you can interact with the page using Lua,

local screen = js.global.screenprint("you haz " ..      (screen.width*screen.height)      .. " pixels")local document = js.global.documentprint("this window has title '" ..      document.title      .. "'")local window = js.globalwindow.alert("hello from lua!")window.setTimeout(function()  print('hello from lua callback')end, 2500) Lua is given a js global that lets you refer to things in the JavaScript/DOM world. You can get properties on those objects, call functions, even set callbacks back into Lua.

Since this uses the full Lua VM, it means you get the full Lua language, and it took just a few days since all the work that went into that VM is reused. That includes an incremental GC and everything else.

At this point you might be wondering if this isn't a crazy idea - a VM in a VM? There is definitely a lot of skepticism about that going around, but we won't know if the skepticism is justified or not if we don't try, hence this project.

The first specific concern is about size. It turns out that the entire compiled Lua VM fits in 200K when gzipped. That's too much for some use cases, but certainly acceptable for others (especially with proper caching).

Another concern is performance. That's what the benchmark page is for. As mentioned there, the Lua VM compiled into asm.js can have similar performance to other real-world codebases, around half the speed of native execution. Again, that is too much for some uses cases, but certainly acceptable for others. In particular, remember that the Lua VM is often significantly faster than other dynamic languages like Python and Ruby. These languages are useful in many cases even if they are not super-fast.

A third concern is compatibility. But compiling to JavaScript is the safest way to run everywhere, since it requires nothing nonstandard at all and consequently should run in all modern web browsers. Speed can differ of course, but the good thing about JavaScript performance is that if one browser is faster at something then the others always catch up.

So running a VM in a VM can make sense sometimes. It seems like an odd thing to run an entire VM on top of another, but the imagined overhead is not as big as it ends up in reality. If you have a lightweight, efficient VM in portable C, like Lua does, then when compiled to the web it behaves much like other portable C/C++ codebases, and it is possible to run compiled C code at near-native speeds on the web. JavaScript VMs are very capable these days, I think more than people sometimes assume.

How far can the approach of running a VM in a VM get us? Pretty far I think, this early prototype is further along than I had expected, and this is before any specific optimizations. There are however some tricky issues, for example we can't do cross-VM cycle collection - if a Lua object and a JavaScript object are both not referred to by anything, but do refer to each other, then to be able to free them we would need to be able to traverse the entire heap on both sides, and basically do our own garbage collection in place of the browser's - for normal JavaScript objects, not just a new type of objects like Lua ones. JavaScript engines don't let us do that, for a combination of security and performance reasons. What we can do is allow Lua to hold strong references into the JavaScript world, and automatically free those when Lua GCs such a reference. That limits things, but it's important to remember that cross-VM cycle collection is a hard problem in CS in general - the only easy case is when one VM's objects can be implemented entirely in the other, but that isn't possible in most cases (and not in this one: for example, some Lua objects can have finalizers - __gc - that can't be implemented in JavaScript) and even when it is, performance is a concern. But note that this type of problem would also be present if we shipped two separate VMs in web browsers.

Speaking of the idea of shipping additional VMs, that is a suggestion that is often heard, specifically I have seen people call for shipping Python, Mono, Lua, etc. VMs alongside JavaScript (alongside, because we need JavaScript comptability for the existing web, and none of those VMs can run JavaScript near the current speed, and we need to not regress performance). This approach has appeal, but also downsides. Perhaps the main issues are that when we compare it running other VMs in the JavaScript VM, then running in the JavaScript VM has some advantages:

  • Security is built in, since the new VM runs inside one that has already been heavily tested and hardened. The attack surface is not increased at all.
  • VMs are downloaded like any web content, which means we can use new languages as people port them to JavaScript, we don't need to wait for browsers to decide to support and ship additional VMs, and we don't risk different browsers deciding to support different VMs.
Running in the JavaScript VM also has disadvantages, but as discussed before they do not seem to be that bad in practice, at least for the Lua VM. It looks like the two approaches are architecturally very different, but can lead to similar results. So the bottom line is that instead of shipping additional VMs in web browsers alongside the JavaScript VM, we can run them inside the JavaScript VM, and the difference could be pretty much transparent to users: In both cases you can run Lua on the web, and in both cases performance is reasonable.
Maybe we wouldn't care? ;)

Last thought, what about running a really fast VM like LuaJIT on the web? That is trickier; LuaJIT has a JIT as well as a hand-written interpreter in assembly, so it can't just be compiled like portable C code can. The JIT would need a JavaScript backend, and the interpreter would need to be ported to JavaScript as well. In principle this could work, but it's hard to say how good performance would be, and there are some interesting questions about code invalidation in code JITed to JavaScript, PICs, etc. This is definitely worth trying, but it isn't a project of a few day's work like porting the Lua C implementation was.

In the meantime, if you love Lua, check out lua.vm.js. Feedback and contributions are welcome!


Viewing all articles
Browse latest Browse all 9433

Trending Articles