# JS-Interpreter Documentation

JS-Interpreter is a sandboxed JavaScript interpreter written in JavaScript. It allows for execution of arbitrary JavaScript code line by line. Execution is completely isolated from the main JavaScript environment. Multiple instances of the JS-Interpreter allow for multi-threaded concurrent JavaScript without the use of Web Workers.

Play with the JS-Interpreter demo.

Get the source code.

## Usage

Start by including the two JavaScript source files:

        <script src="acorn.js"></script>
        <script src="interpreter.js"></script>

Alternatively, use the compressed bundle (70kb):

        <script src="acorn_interpreter.js"></script>

Next, instantiate an interpreter with the JavaScript code that needs to be parsed:

        var myCode = 'var a=1; for(var i=0;i<4;i++){a*=i;} a;';
        var myInterpreter = new Interpreter(myCode);

Additional JavaScript code may be added at any time (frequently used to interactively call previously defined functions):

        myInterpreter.appendCode('foo();');

To run the code step by step, call the `step` function repeatedly until it returns false:

        function nextStep() {
          if (myInterpreter.step()) {
            window.setTimeout(nextStep, 0);
          }
        }
        nextStep();

Alternatively, if the code is known to be safe from infinite loops, it may be executed to completion by calling the `run` function once:

        myInterpreter.run();

In cases where the code encounters asynchronous API calls (see below), `run` will return true if it is blocked and needs to be reexecuted at a later time.

## External API

Similar to the `eval` function, the result of the last statement executed is available in `myInterpreter.value`:

        var myInterpreter = new Interpreter('6 * 7');
        myInterpreter.run();
        alert(myInterpreter.value);

Additionally, API calls may be added to the interpreter during creation. Here is the addition of `alert()` and a `url` variable:

        var myCode = 'alert(url);';
        var initFunc = function(interpreter, scope) {
          interpreter.setProperty(scope, 'url',
              interpreter.createPrimitive(location.toString()));

          var wrapper = function(text) {
            text = text ? text.toString() : '';
            return interpreter.createPrimitive(alert(text));
          };
          interpreter.setProperty(scope, 'alert',
              interpreter.createNativeFunction(wrapper));
        };
        var myInterpreter = new Interpreter(myCode, initFunc);

See the JSON demo for an example of exchanging JSON between the browser and the interpreter. For more complicated examples, see the `initGlobalScope` function which creates APIs for Math, Array, Function, and other globals.

Asynchronous API functions may wrapped so that they appear to be synchronous to interpreter. For example, a `getXhr(url)` function that returns the contents of an XMLHttpRequest could be defined in `initFunc` like this:

        var wrapper = function getXhr(href, callback) {
          href = href ? href.toString() : '';
          var req = new XMLHttpRequest();
          req.open('GET', href, true);
          req.onreadystatechange = function() {
            if (req.readyState == 4 && req.status == 200) {
              callback(interpreter.createPrimitive(req.responseText));
            }
          };
          req.send(null);
        };
        interpreter.setProperty(scope, 'getXhr',
            interpreter.createAsyncFunction(wrapper));

This snippet uses `createAsyncFunction` in the same way that `createNativeFunction` was used earlier. The difference is that the wrapped asynchronous function's return value is ignored. Instead, an extra callback function is passed in when the wrapper is called. When the wrapper is ready to return, it calls the callback function with the value it wishes to return. From the point of view of the code running inside the JS-Interpreter, a function call was made and the result was returned immediately.

For a working example, see the async demo.

## Limitations

The version of JavaScript implemented by the interpreter has a few differences from that which executes in a browser:

API
  None of the DOM APIs are exposed. That's kind of the point of a sandbox. If you need these, write your own interfaces.
ES6
  More recent additions to JavaScript such as `let` or `Set` aren't implemented. Feel free to add them if you need them.
Wat
  Some of the more obscure type conversions (e.g. `[] + {}`) may return different results from JavaScript. Patches are welcome.
Passed functions
  `Array.sort` does not support custom compare functions. A workaround can be found in the sort demo. Likewise, `String.replace` does not support custom replacement functions. `Array.every/filter/map` does not exist.
Performance
  No attempt has been made to make the interpreter particularly efficient.

## Dependency

The only dependency is Acorn, a beautifully written JavaScript parser by Marijn Haverbeke. It is included in the JS-Interpreter package.

## Compatibility

The limiting factor for browser support is the use of `Object.create(null)` to create hash objects in both Acorn and JS-Interpreter. This results in the following minimum browser requirements:

- Chrome 5
- Firefox 4.0
- IE 9
- Opera 11.6
- Safari 5

## Disclaimer

This project is not an official Google product.