Fork me on GitHub

lively.vm

lively.vm provides the ability to evaluate JavaScript code in an evaluation context. Normally the JavaScript eval() function uses the local scope of the function it was called in (however, there are some additional weird rules). It offers no further options about scope and bindings.

lively.vm has a range of options that control what bindings are available inside the executed code and also how top-level assignments inside the evaluated code are captured. The latter is helpful when you want to access intermediate results and assignments caused by the evaluation, e.g. to allow incremental development.

Example

When you press the eval button below the content of the text area is passed to lively.vm.runEval(source, options) and the result printed.





      
      

      

Usage

In it's simplest form, use lively.vm.syncEval(code, options). The return value is a result object whose value is the return value of the last statement in the evaluated code;

lively.vm.syncEval("3 + 4"); => { value: 7, isError: false, isPromise: false, warnings: [] }

bindings

To capture the bindings of top-level variables inside the evaluated code, use the topLevelVarRecorder option. Note that this will not pollute the global namespace.

var bindings = {}; lively.vm.syncEval( "var x = 3; var y = 4; x + y;", {topLevelVarRecorder: bindings}); => {value: 7} bindings.x => 3 bindings.y => 4

Similarly, you can make pass bindings into the evaluation:

var bindings = {x: 99}; lively.vm.syncEval("x = x + 2;", {topLevelVarRecorder: bindings}); => {value: 101} bindings.x => 101

callbacks and asynchronous evaluation, custom transpilers

lively.vm.runEval supports evaluation processes that are asynchronous. runEval itself will return a Promise that resolves to the eval result object. You can also specify an onEndEval handler to be notified when the evaluation is done. The following example also uses the transpileroption to allow top-level await (your JavaScript VM needs to support that, alternatively use a transpiler like babeljs in the transpiler function you pass to lively.vm).

await lively.vm.runEval( "await new Promise((resolve, reject) => setTimeout(() => resolve(42), 1000));", { topLevelVarRecorder: {}, wrapInStartEndCall: true, transpiler: source => "(async function() {" + source + "})()", onEndEval: (err, value) => console.log("evaluation done", value) }); => will print "42" after 1 sec.

Using it with lively.modules

lively.modules is a module system that supports interactive runtime changes of source code. When lively.modules is loaded and SystemJS are loaded, you can pass a targetModule option to lively.vm that will run the evaluation in the context of the module, having access to all top-level module bindings (and being able to change those).

Assuming we have a module "test.js" with the following code:

var x = 3; export var y = x + 4;

lively.vm can be used to access as well as modify exports and module internal state:

await lively.vm.runEval("x", {targetModule: "test.js"}); => {value: 3} await lively.vm.runEval("y = 10", {targetModule: "test.js"}); (await System.import("test.js")).y => 10

dynamic code completions

To inspect objects from an editor or repl and get auto completions of properties and methods you can use lively.vm.completions.getCompletions. The result of the getCompletions call is a nested list, providing the prototype hierarchy of the completion target and their respective attributes/methods.

var someObject = { aProperty: 23, anotherProperty: "hello world", xProperty: 99 }, bindings = {topLevelVarRecorder: {someObject}} let result = await lively.vm.completions.getCompletions( source => lively.vm.syncEval(source, bindings), "someObject.a"); => { code: "someObject", startLetters: "a", completions: [ ["[object Object]", ["aProperty", "anotherProperty", "xProperty"] ], ["Object", ["__defineGetter__()", "__defineSetter__()", "__lookupGetter__()", "__lookupSetter__()", "constructor()", "hasOwnProperty()", "isPrototypeOf()", "propertyIsEnumerable()", "toLocaleString()", "toString()",...] ]], }

notifications

There are two types of system-wide notifications:

{type: "lively.vm/doitrequest", code, targetModule, waitForPromise} {type: "lively.vm/doitresult", code, targetModule, waitForPromise, result}

These notifications are all emitted with lively.notifications.

License

MIT