Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add Contextify.createContext and Contextify.createScript

Similar to the `vm` API in node, this allows you to create a Contextify context like this:

  var sandbox = {};
  var context = Contextify.createContext(sandbox);

Unlike `Contextify()`, createContext does not augment the sandbox with `run`, `getGlobal` and `dispose`.

This also adds `Contextify.createScript`, similar to `vm.createScript`, which will pre-compile code and let you run it in a Contextify context later. This can have huge performance benefits, e.g. a web server that runs each request in a separate context.

  var sandbox = {};
  var context = Contextify.createContext(sandbox);
  var script = Contextify.createScript('var x = 1');
  script.runInContext(context);
  // sandbox.x === 1
  • Loading branch information...
commit 37e75282ef6e9c6102e1458feb668d67dea37aa3 1 parent 5cb61d4
@mroch mroch authored
Showing with 203 additions and 4 deletions.
  1. +20 −2 lib/contextify.js
  2. +89 −0 src/contextify.cc
  3. +94 −2 test/contextify.js
View
22 lib/contextify.js
@@ -1,6 +1,8 @@
-var ContextifyContext = require('bindings')('contextify').ContextifyContext;
+var binding = require('bindings')('contextify');
+var ContextifyContext = binding.ContextifyContext;
+var ContextifyScript = binding.ContextifyScript;
-module.exports = function Contextify (sandbox) {
+function Contextify (sandbox) {
if (typeof sandbox != 'object') {
sandbox = {};
}
@@ -28,3 +30,19 @@ module.exports = function Contextify (sandbox) {
}
return sandbox;
}
+
+Contextify.createContext = function (sandbox) {
+ if (typeof sandbox != 'object') {
+ sandbox = {};
+ }
+ return new ContextifyContext(sandbox);
+};
+
+Contextify.createScript = function (code, filename) {
+ if (typeof code != 'string') {
+ throw new TypeError('Code argument is required');
+ }
+ return new ContextifyScript(code, filename);
+};
+
+module.exports = Contextify;
View
89 src/contextify.cc
@@ -134,6 +134,10 @@ class ContextifyContext : ObjectWrap {
return scope.Close(result);
}
+ static bool InstanceOf(Handle<Value> value) {
+ return !value.IsEmpty() && jsTmpl->HasInstance(value);
+ }
+
static Handle<Value> GetGlobal(const Arguments& args) {
HandleScope scope;
ContextifyContext* ctx = ObjectWrap::Unwrap<ContextifyContext>(args.This());
@@ -208,11 +212,96 @@ class ContextifyContext : ObjectWrap {
}
};
+class ContextifyScript : ObjectWrap {
+public:
+ static Persistent<FunctionTemplate> scriptTmpl;
+ Persistent<Script> script;
+
+ static void Init(Handle<Object> target) {
+ HandleScope scope;
+ scriptTmpl = Persistent<FunctionTemplate>::New(FunctionTemplate::New(New));
+ scriptTmpl->InstanceTemplate()->SetInternalFieldCount(1);
+ scriptTmpl->SetClassName(String::NewSymbol("ContextifyScript"));
+
+ NODE_SET_PROTOTYPE_METHOD(scriptTmpl, "runInContext", RunInContext);
+
+ target->Set(String::NewSymbol("ContextifyScript"),
+ scriptTmpl->GetFunction());
+ }
+
+ static Handle<Value> New(const Arguments& args) {
+ HandleScope scope;
+
+ ContextifyScript *contextify_script = new ContextifyScript();
+ contextify_script->Wrap(args.Holder());
+
+ if (args.Length() < 1) {
+ return ThrowException(Exception::TypeError(
+ String::New("needs at least 'code' argument.")));
+ }
+
+ Local<String> code = args[0]->ToString();
+ Local<String> filename = args.Length() > 1
+ ? args[1]->ToString()
+ : String::New("ContextifyScript.<anonymous>");
+
+ Handle<Context> context = Context::GetCurrent();
+ Context::Scope context_scope(context);
+
+ // Catch errors
+ TryCatch trycatch;
+
+ Handle<Script> v8_script = Script::New(code, filename);
+
+ if (v8_script.IsEmpty()) {
+ return trycatch.ReThrow();
+ }
+
+ contextify_script->script = Persistent<Script>::New(v8_script);
+
+ return args.This();
+ }
+
+ static Handle<Value> RunInContext(const Arguments& args) {
+ HandleScope scope;
+ if (args.Length() == 0) {
+ Local<String> msg = String::New("Must supply at least 1 argument to runInContext");
+ return ThrowException(Exception::Error(msg));
+ }
+ if (!ContextifyContext::InstanceOf(args[0]->ToObject())) {
+ Local<String> msg = String::New("First argument must be a ContextifyContext.");
+ return ThrowException(Exception::TypeError(msg));
+ }
+ ContextifyContext* ctx = ObjectWrap::Unwrap<ContextifyContext>(args[0]->ToObject());
+ Persistent<Context> context = ctx->context;
+ context->Enter();
+ ContextifyScript* wrapped_script = ObjectWrap::Unwrap<ContextifyScript>(args.This());
+ Handle<Script> script = wrapped_script->script;
+ TryCatch trycatch;
+ if (script.IsEmpty()) {
+ context->Exit();
+ return trycatch.ReThrow();
+ }
+ Handle<Value> result = script->Run();
+ context->Exit();
+ if (result.IsEmpty()) {
+ return trycatch.ReThrow();
+ }
+ return scope.Close(result);
+ }
+
+ ~ContextifyScript() {
+ script.Dispose();
+ }
+};
+
Persistent<FunctionTemplate> ContextifyContext::jsTmpl;
+Persistent<FunctionTemplate> ContextifyScript::scriptTmpl;
extern "C" {
static void init(Handle<Object> target) {
ContextifyContext::Init(target);
+ ContextifyScript::Init(target);
}
NODE_MODULE(contextify, init);
};
View
96 test/contextify.js
@@ -22,6 +22,17 @@ exports['basic tests'] = {
test.done();
},
+ 'basic createContext' : function (test) {
+ var sandbox = {
+ prop1: 'prop1',
+ prop2: 'prop2'
+ };
+ var context = Contextify.createContext(sandbox);
+ test.equal(sandbox.prop1, 'prop1');
+ test.equal(sandbox.prop2, 'prop2');
+ test.done();
+ },
+
// Ensure that the correct properties exist on a wrapped sandbox.
'test contextified object extra properties' : function (test) {
var sandbox = Contextify({});
@@ -31,6 +42,15 @@ exports['basic tests'] = {
test.done();
},
+ 'createContext should not modify the sandbox' : function (test) {
+ var sandbox = {};
+ Contextify.createContext(sandbox);
+ test.equal(sandbox.run, undefined);
+ test.equal(sandbox.getGlobal, undefined);
+ test.equal(sandbox.dispose, undefined);
+ test.done();
+ },
+
// Passing undefined should create an empty context.
'test undefined sandbox' : function (test) {
// Should return an empty object.
@@ -65,6 +85,14 @@ exports['basic tests'] = {
test.done();
},
+ 'test for "undefined" properties with createContext' : function (test) {
+ var sandbox = { x: undefined };
+ var context = Contextify.createContext(sandbox);
+ context.run("_x = x");
+ test.equal(sandbox._x, undefined);
+ test.done();
+ },
+
'test for "undefined" variables' : function (test) {
var sandbox = { };
Contextify(sandbox);
@@ -88,6 +116,15 @@ exports['basic tests'] = {
test.done();
},
+ // Make sure run can be called on a context
+ 'test run with createContext' : function (test) {
+ var sandbox = {};
+ var context = Contextify.createContext(sandbox);
+ context.run('var x = 3', "test.js");
+ test.equal(sandbox.x, 3);
+ test.done();
+ },
+
// Make sure getters/setters on the sandbox object are used.
'test accessors on sandbox' : function (test) {
var sandbox = {};
@@ -192,6 +229,28 @@ exports['asynchronous script tests'] = {
test.ok(sandbox.test2);
test.done();
}, 0);
+ },
+
+ // Asynchronous context script execution:
+ // Ensure that sandbox properties can be accessed as global variables.
+ 'createContext: sandbox properties should be globals' : function (test) {
+ var sandbox = {
+ setTimeout : setTimeout,
+ prop1 : 'prop1',
+ prop2 : 'prop2'
+ };
+ var context = Contextify.createContext(sandbox);
+ context.run("setTimeout(function () {" +
+ "test1 = (prop1 == 'prop1');" +
+ "test2 = (prop2 == 'prop2');" +
+ "}, 0)");
+ test.equal(sandbox.test1, undefined);
+ test.equal(sandbox.test2, undefined);
+ setTimeout(function () {
+ test.ok(sandbox.test1);
+ test.ok(sandbox.test2);
+ test.done();
+ }, 0);
}
};
@@ -301,7 +360,7 @@ exports['test global'] = {
test.equal(global.x, 5);
test.done();
},
-
+
// Make sure global can be a receiver for getGlobal().
'test global.getGlobal()' : function (test) {
var global = Contextify().getGlobal();
@@ -436,7 +495,7 @@ exports['test exceptions'] = {
}, 'Called dispose() twice.');
test.done();
},
-
+
'test run() after dispose()' : function (test) {
var sandbox = Contextify();
test.doesNotThrow(function () {
@@ -459,3 +518,36 @@ exports['test exceptions'] = {
test.done();
}
};
+
+exports['test scripts'] = {
+ 'test createScript()' : function (test) {
+ var script = Contextify.createScript('var x = 3', 'test.js');
+ test.equal(typeof script.runInContext, 'function');
+ test.done();
+ },
+
+ 'test createScript() without code' : function (test) {
+ test.throws(function () {
+ Contextify.createScript();
+ });
+ test.throws(function () {
+ Contextify.createScript(true);
+ });
+ test.throws(function () {
+ Contextify.createScript(null);
+ });
+ test.throws(function () {
+ Contextify.createScript(1);
+ });
+ test.done();
+ },
+
+ 'test runInContext' : function (test) {
+ var sandbox = {};
+ var script = Contextify.createScript('var x = 3', 'test.js');
+ var context = Contextify.createContext(sandbox);
+ script.runInContext(context);
+ test.equal(sandbox.x, 3);
+ test.done();
+ }
+};
Please sign in to comment.
Something went wrong with that request. Please try again.