Skip to content

Commit

Permalink
Added a contextify_vm to handle code execution
Browse files Browse the repository at this point in the history
  • Loading branch information
rehanift committed Apr 29, 2012
1 parent f44773c commit d806a8d
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 3 deletions.
6 changes: 4 additions & 2 deletions lib/engine/piston.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ piston.create = function(config){
console_endpoint: config.console_endpoint
});

var execution_strategy = piston.execution_strategies.node_vm.make();
//var execution_strategy = piston.execution_strategies.node_vm.make();
var execution_strategy = piston.execution_strategies.contextify_vm.create();

var context = require("zmq");
var console_socket = context.socket("push");
Expand Down Expand Up @@ -70,7 +71,8 @@ piston.prototype.close = function(){
};

var execution_strategies = {
node_vm: require("./piston/execution_strategies/node_vm").node_vm
node_vm: require("./piston/execution_strategies/node_vm").node_vm,
contextify_vm: require("./piston/execution_strategies/contextify_vm").contextify_vm
};
piston.execution_strategies = execution_strategies;
piston.sandbox_generator = require("./piston/sandbox_generator").sandbox_generator;
Expand Down
10 changes: 10 additions & 0 deletions lib/engine/piston/execution_strategies/context-bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use strict";

Function.prototype.constructor = function(){ throw new SecurityError("The Function constructor may not be called"); };
Function.prototype.toString = function(){ throw new SecurityError("'toString' may not be called on functions"); };

var SecurityError = function(message){
this.message = message;
this.name = "SecurityError";
};
SecurityError.prototype = Error.prototype;
125 changes: 125 additions & 0 deletions lib/engine/piston/execution_strategies/contextify_vm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
var vm = function(){
//avoid contextify's js wrapper
var contextifyPath = require('path').resolve(require.resolve('contextify'), '..', '..');
var contextify = require('bindings')({
module_root: contextifyPath,
bindings: 'contextify.node'
});
// basic WeakMap shim if not available
var WM = typeof WeakMap !== 'undefined' ? WeakMap : function WeakMap(){
var keys = [], values = [];
return {
set: function(key, val){
keys.push(key);
values.push(val);
return val;
},
get: function(key){
var index = keys.indexOf(key);
if (~index) return values[index];
},
has: function(key){
return !!~keys.indexOf(key);
},
delete: function(key){
var index = keys.indexOf(key);
if (~index) {
keys.splice(index, 1);
values.splice(index, 1);
return true;
}
return false;
}
};
};


// allow for proper garbage collection
var contexts = new WM;
var Context = contextify.ContextifyContext;


function createContext(sandbox){
if (sandbox == null) {
sandbox = {};
} else if (Object(sandbox) !== sandbox) {
throw new TypeError('Sandbox must be an object');
}
contexts.set(sandbox, new Context(sandbox));
return sandbox;
}

function runInContext(code, sandbox){
if (Object(sandbox) === sandbox) {
if (!contexts.has(sandbox)) {
createContext(sandbox);
}
return contexts.get(sandbox).run(code);
} else {
throw new TypeError('Context must be an object');
}
}

function runInThisContext(code){
return runInContext(code, global);
}

function runInNewContext(code){
var sandbox = createContext();
var result = runInContext(code, sandbox);
dispose(sandbox);
return result;
}

function dispose(sandbox){
contexts.delete(sandbox);
}

return {
createContext: createContext,
runInContext: runInContext,
runInThisContext: runInThisContext,
runInNewContext: runInNewContext,
dispose: dispose
};
}();


var ContextifyVm = function(){};

ContextifyVm.make = function(){
var vm = new ContextifyVm();
return vm;
};

ContextifyVm.create = function(){
var vm = ContextifyVm.make({});
return vm;
};

ContextifyVm.prototype.execute = function(code, sandbox){
"use strict";

var ctx = vm.createContext(sandbox), last_eval;

var fs = require("fs"),
path = require("path");

var bootstrap_file = path.dirname(require.resolve("engine.js")) + "/lib/engine/piston/execution_strategies/context-bootstrap.js";

var bootstrap_code = fs.readFileSync(bootstrap_file,"utf-8");

vm.runInContext(bootstrap_code, ctx);

try {
last_eval = vm.runInContext("'use strict'; " + code, ctx);
} catch (e) {
last_eval = e.name + ': ' + e.message;
}

vm.dispose(ctx);

return last_eval;
};

exports.contextify_vm = ContextifyVm;
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"zmq" : "*",
"node-uuid" : "*",
"jasmine-node" : "1.0.19",
"underscore" : "*"
"underscore" : "*",
"contextify" : "*",
"bindings" : "*"
}
}
28 changes: 28 additions & 0 deletions spec/engine/piston/execution_strategies/contextify_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var contextify_vm = require("../../../../lib/engine/piston/execution_strategies/contextify_vm").contextify_vm;

describe("Code Runners", function(){
describe("Contextify", function(){
beforeEach(function(){
this.strategy = contextify_vm.make();
this.sandbox = {
add: function(a,b){ return a+b;}
};
});

it("evalutes code against a sandbox", function(){
var last_eval = this.strategy.execute("add(1,1)", this.sandbox);
expect(last_eval).toBe(2);
});

it("throws a SyntaxError when the user-code has bad syntax", function(){
var last_eval = this.strategy.execute("add(1,1", this.sandbox);
expect(last_eval).toContain("SyntaxError");
});

it("throws a ReferenceError when the user-code calls an unknown sandbox function", function(){
var last_eval = this.strategy.execute("subtract(1,1)", this.sandbox);
expect(last_eval).toContain("ReferenceError");
});
});

});

0 comments on commit d806a8d

Please sign in to comment.