vm.js (GitHub: tarruda/vm.js, License: MIT) is a pretty impressive fully
functional ECMAScript virtual machine that can be used from any
ECMAScript3-compatible environment. Eventually it will provide a
complete ECMAScript 6 environment (for now only some features are
supported).

npm install vm.js

It’s written in CoffeeScript and has plenty of tests. The most
impressive thing is that the tests themselves run inside vm.js.

Here are some possible use cases:

  • Simple in-process javascript sandbox
  • Async-to-sync API adapter using fibers (lightweight in-process threads, not node.js fibers)
  • Use new ECMAScript features in very old browsers

The main API can be accessed through vm instances. Each vm is indirectly
associated with a global object (through a “realm”) and is isolated from
other vms.

Start by creating a new instance:

> Vm = require('vm.js')
> vm = new Vm()

Evaluate simple expressions:

> vm.eval('40 + 2')
42
> vm.eval('[a, b, c] = [1, 2, 3]') // Harmony destructuring assignment
[1, 2, 3]
> vm.realm.global.a
1
> vm.realm.global.b
2
> vm.realm.global.c
3

Compile programs and run later

// pass filename as second argument for stack traces/debugging
> script = Vm.compile('2 + 2', 'sum.js')
> vm.run(script)
4

Compiled scripts can be serialized/deserialized to/from JSON-friendly
structures:

> scriptObj = script.toJSON()
> serializedScript = JSON.stringify(scriptObj)
> deserializedScript = Vm.fromJSON(JSON.parse(serializedScript))
> vm.run(deserializedScript)

Expose objects to be used by code running inside the Vm

> vm.realm.global.factorial = function factorial(n) { return n > 1 ? factorial(n - 1) * n : 1 }
> vm.eval('factorial(5)')
120

The inverse also works:

> vm.eval('function factorial(n) { return n > 1 ? factorial(n - 1) * n : 1 }')
> vm.realm.global.factorial(5)
120

Return values asynchronously using fiber pause/resume:

// created a paused fiber from compiled code
fiber = vm.createFiber(Vm.compile('user = null; user = fetchAsync("/users/1");'))
vm.realm.global.fetchAsync = function(url) {
  fiber.pause() // pause execution
  $.get(url, function(data) {
    // user === null
    fiber.setReturnValue(data)
    fiber.resume()
    // user === data
  });
}
// start fiber
fiber.run()

There are plenty of examples on the
github page.