Skip to content

A JavaScript Virtual Machine that brings ES6 to ES3 host environments

Notifications You must be signed in to change notification settings

joskid/continuum

Repository files navigation

Continuum - A JavaScript Virtual Machine Built in JavaScript

Continuum is a JavaScript virtual machine built in JavaScript. It assembles bytecode from sourcecode and executes it an ES6 runtime environment. The code of the VM is written in ES3 level JavaScript, which means it can run in browsers as old as IE6. (though currently it's only been tested in IE9+ and there's probably some kinks to work out in older IE's).

ES6 is still an unfinished specification and is still a moving target

Compatibility

Continuum probably works in every modern engine, but has not been tested.

Currently known to work in:

  • IE8+
  • Chrome 23+
  • Firefox 15+
  • Opera 12+

Will soon work in:

  • IE6-7

screenshot

Installation

In the browser, use the combined continuum.js or continuum.min.js. In node

npm install continuum

Quickstart Usage Overview

In the browser, an object named continuum is added to the window, or in node it's the object returned by require('continuum').

Usage of continuum is quite simple and can basically be treated like using eval or node's vm.runInContext. Supply the code, get the result. In ES6 language, a "realm" is basically the container for a context. A realm has a 'global' property which is its global object, and a number of properties that specific to each realm instance, such as the list of "intrinsics" or builtins like Array, Function, Object, etc.

var realm = continuum.createRealm();

var $array = realm.evaluate('[5, 10, 15, 20]');

// you can use the ES internal functions to directly interact with objects
console.log($array.Get(0)) // 5
console.log($array.Get('map')) // $Function object

// these two lines have the same result
var $Function = realm.evaluate('Function');
var $Function = realm.global.Get('Function');

// if your code uses the module system then it must be run asynchronously
realm.evaluateAsync('module F = "@Function"', function(result){
  // statements and declarations have no return value so result is undefined in this case, however...
  console.log(realm.evaluate('F')) // $Module with Function, call, apply, and bind (functional versions)
})

ES6 Implementation Status

Implemented

  • destructuring assignment and arguments
  • spread in arguments and array initializers
  • rest parameters
  • classes and super
  • arrow functions
  • block scope
  • new Math functions
  • new Object functions
  • new String functions
  • concise methods in object literals
  • mutable and deletable proto
  • Map, Set, and WeakMap (garbage collection semantics not fully realized)
  • Iterators and for...of
  • Templates
  • Module system with imports and exports
  • builtin '@std' modules module std = '@std' or import call from '@Function'
  • Generators (kind of broken at the moment though)
  • Proxy and Reflect
  • Private Names

Soon to be Implemented

  • Array Comprehensions
  • Default parameters
  • Tail call optimization
  • Binary data api

API Overview

Core API:

  • createRealm([callback]): creates a new realm (context + global object). Optional debug callback for massive information
  • createBytecode(code|filename): Creates bytecode from the given source and returns it unexecuted (this will be serializable to disk soon).
  • createRenderer(handlers): For debug rendering, like the debugger UI.
  • createNativeFunction(func): Wraps a regular function as a function object that can be used inside the virtual
  • introspect($object): return a debug Mirror object that wraps any type of VM object, primitive, or scope object. This provides an easy to use and normalized interface for querying information about objects.

Extras:

  • utility: substantial amount of useful functionality used in continuum and generally useful in any JS
  • constants: all of the large amount of constants and symbols used by the VM

Additionally exported is the class objects Assembler, Realm, Renderer, and Script.

Realm

A Realm is the main thing you interact with. Each realm has a global object with a unique set of builtin globals. A realm is roughly equivalent to an iframe or a node vm context.

  • realm.evaluate(code): Executes code in the virtual machine and returns the completion value, if any. "code" can be a string or an already compiled Script object. Every time code is executed, the script object is added to realm.scripts, so you can reuse a script if desired.
  • realm.evaluateAsync(code, callback): Primarily for when executing code that uses the module system, which must be run asynchronously if importing remote resources.

VM Events These are emitted by the VM to indicate changes to the state of execution and provide access to information about related objects.

  • realm.on('complete', function(result){}): emitted whenever a script completes execution. The result value is the same value that's returned from realm.evaluate.
  • realm.on('executing', function(thunk){}): emitted whenever execution begins or resume. The thunk is the object which executes the bytecode.
  • realm.on('throw', function(exception){}): throw is emitted on uncaught exception. The thrown object (usually an $Error) will be passed to the callback.
  • realm.on('pause', function(resume){}): pause is emitter when a debugger statement is encountered. A function which will resume execution is passed to the callback, and is also available as realm.resume.
  • realm.on('resume', function(){}): emitted when the resume function provided from the pause event is called and execution begins again.
  • realm.on('op', function(op){}): emitted every time an opcode is executed. Be very careful when listening to this event. A small to medium sized chunk of code can emit hundreds of thousands or millions of ops in a very short amount of time.

API Events These are emitted by functions from inside the VM to simulate things like input/output and require some implementation on your part to do anything useful.

  • realm.on('write', function(text, color){}): emitted by stdout.write, which is used by console.log and friends.
  • realm.on('clear', function(){}): emitted by stdout.clear to provide a way to clear whatever it is you're providing as stdout
  • realm.on('backspace', function(number){}): emitted by stdout.backspace to provide a way to delete a specific amount of characters from the end.

Renderer

A renderer is a visitor used to introspect VM objects and values.

Assembler

An assembler is used to convert AST into bytecode and static script information.

Script

A script contains all the bits related to a given chunk of source. The given options, sourcecode string, AST, bytecode, and the thunk (lazily created upon first execution). Scripts don't contain realm-specific information, so they are portable between realms/globals and can be executed multiple times as needed.

TODO

  • Hook up module system to environment
  • Much work on optimizations
  • Serializable state for saving applications while they run
  • Expanded debugger and eventually full dev environment built around continuum
  • Move more runtime semantics to bytecode
  • Bootstrap the runtime on an even simpler bytecode interpreter

About

A JavaScript Virtual Machine that brings ES6 to ES3 host environments

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published