Skip to content

Instantly share code, notes, and snippets.

@cha0s
Created June 23, 2010 12:04
Show Gist options
  • Save cha0s/449842 to your computer and use it in GitHub Desktop.
Save cha0s/449842 to your computer and use it in GitHub Desktop.
From 7e9c16806638ca413025defc91185c8f2ea471a6 Mon Sep 17 00:00:00 2001
From: Ruben Rodriguez <cha0s@therealcha0s.net>
Date: Wed, 23 Jun 2010 07:00:06 -0500
Subject: [PATCH] Added Script.createContext() and Script.runInContext() and implemented it in the REPL.
---
lib/repl.js | 40 ++++++++--------
src/node.cc | 7 ++-
src/node_cares.cc | 8 ++--
src/node_child_process.cc | 2 +-
src/node_file.cc | 2 +-
src/node_script.cc | 109 ++++++++++++++++++++++++++++++++++++++++++++-
src/node_script.h | 23 +++++++++-
7 files changed, 159 insertions(+), 32 deletions(-)
diff --git a/lib/repl.js b/lib/repl.js
index d0fefde..dec6684 100644
--- a/lib/repl.js
+++ b/lib/repl.js
@@ -10,13 +10,14 @@
// repl.start("node via TCP socket> ", socket);
// }).listen(5001);
-// repl.start("node > ").scope.foo = "stdin is fun"; // expose foo to repl scope
+// repl.start("node > ").context.foo = "stdin is fun"; // expose foo to repl context
var sys = require('sys');
-var evalcx = process.binding('evals').Script.runInNewContext;
+var Script = process.binding('evals').Script;
+var evalcx = Script.runInContext;
var path = require("path");
var rl = require('readline');
-var scope;
+var context;
function cwdRequire (id) {
if (id.match(/^\.\.\//) || id.match(/^\.\//)) {
@@ -28,11 +29,11 @@ Object.keys(require).forEach(function (k) {
cwdRequire[k] = require[k];
});
-function setScope (self) {
- scope = {};
- for (var i in global) scope[i] = global[i];
- scope.module = module;
- scope.require = cwdRequire;
+function resetContext() {
+ context = Script.createContext();
+ for (var i in global) context[i] = global[i];
+ context.module = module;
+ context.require = cwdRequire;
}
@@ -41,8 +42,7 @@ exports.writer = sys.inspect;
function REPLServer(prompt, stream) {
var self = this;
- if (!scope) setScope();
- self.scope = scope;
+ if (!context) resetContext();
self.buffered_cmd = '';
self.stream = stream || process.openStdin();
@@ -70,10 +70,10 @@ function REPLServer(prompt, stream) {
// This try is for determining if the command is complete, or should
// continue onto the next line.
try {
- // Use evalcx to supply the global scope
- var ret = evalcx(self.buffered_cmd, scope, "repl");
+ // Use evalcx to supply the global context
+ var ret = evalcx(self.buffered_cmd, context, "repl");
if (ret !== undefined) {
- scope._ = ret;
+ context._ = ret;
flushed = self.stream.write(exports.writer(ret) + "\n");
}
@@ -150,9 +150,9 @@ REPLServer.prototype.parseREPLKeyword = function (cmd) {
self.displayPrompt();
return true;
case ".clear":
- self.stream.write("Clearing Scope...\n");
+ self.stream.write("Clearing context...\n");
self.buffered_cmd = '';
- setScope();
+ resetContext();
self.displayPrompt();
return true;
case ".exit":
@@ -160,7 +160,7 @@ REPLServer.prototype.parseREPLKeyword = function (cmd) {
return true;
case ".help":
self.stream.write(".break\tSometimes you get stuck in a place you can't get out... This will get you out.\n");
- self.stream.write(".clear\tBreak, and also clear the local scope.\n");
+ self.stream.write(".clear\tBreak, and also clear the local context.\n");
self.stream.write(".exit\tExit the prompt\n");
self.stream.write(".help\tShow repl options\n");
self.displayPrompt();
@@ -180,21 +180,21 @@ function trimWhitespace (cmd) {
/**
* Converts commands that use var and function <name>() to use the
- * local exports.scope when evaled. This provides a local scope
+ * local exports.context when evaled. This provides a local context
* on the REPL.
*
* @param {String} cmd The cmd to convert
* @returns {String} The converted command
*/
-REPLServer.prototype.convertToScope = function (cmd) {
+REPLServer.prototype.convertToContext = function (cmd) {
var self = this, matches,
scopeVar = /^\s*var\s*([_\w\$]+)(.*)$/m,
scopeFunc = /^\s*function\s*([_\w\$]+)/;
- // Replaces: var foo = "bar"; with: self.scope.foo = bar;
+ // Replaces: var foo = "bar"; with: self.context.foo = bar;
matches = scopeVar.exec(cmd);
if (matches && matches.length === 3) {
- return "self.scope." + matches[1] + matches[2];
+ return "self.context." + matches[1] + matches[2];
}
// Replaces: function foo() {}; with: foo = function foo() {};
diff --git a/src/node.cc b/src/node.cc
index 3fc90cf..ea7de5f 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -1631,6 +1631,7 @@ static Handle<Value> Binding(const Arguments& args) {
exports = binding_cache->Get(module)->ToObject();
} else {
exports = Object::New();
+ node::Context::Initialize(exports);
node::Script::Initialize(exports);
binding_cache->Set(module, exports);
}
@@ -1685,7 +1686,7 @@ static void Load(int argc, char *argv[]) {
process = Persistent<Object>::New(process_template->GetFunction()->NewInstance());
// Add a reference to the global object
- Local<Object> global = Context::GetCurrent()->Global();
+ Local<Object> global = v8::Context::GetCurrent()->Global();
process->Set(String::NewSymbol("global"), global);
// process.version
@@ -1987,8 +1988,8 @@ int main(int argc, char *argv[]) {
}
// Create the one and only Context.
- Persistent<Context> context = Context::New();
- Context::Scope context_scope(context);
+ Persistent<v8::Context> context = v8::Context::New();
+ v8::Context::Scope context_scope(context);
atexit(node::AtExit);
diff --git a/src/node_cares.cc b/src/node_cares.cc
index 892f648..4b50776 100644
--- a/src/node_cares.cc
+++ b/src/node_cares.cc
@@ -223,7 +223,7 @@ static void ResolveError(Persistent<Function> &cb, int status) {
TryCatch try_catch;
- cb->Call(Context::GetCurrent()->Global(), 1, &e);
+ cb->Call(v8::Context::GetCurrent()->Global(), 1, &e);
if (try_catch.HasCaught()) {
FatalException(try_catch);
@@ -251,7 +251,7 @@ static void HostByNameCb(void *data,
Local<Value> argv[2] = { Local<Value>::New(Null()), addresses};
- (*cb)->Call(Context::GetCurrent()->Global(), 2, argv);
+ (*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
@@ -281,7 +281,7 @@ static void HostByAddrCb(void *data,
Local<Value> argv[2] = { Local<Value>::New(Null()), names };
- (*cb)->Call(Context::GetCurrent()->Global(), 2, argv);
+ (*cb)->Call(v8::Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
@@ -294,7 +294,7 @@ static void HostByAddrCb(void *data,
static void cb_call(Persistent<Function> &cb, int argc, Local<Value> *argv) {
TryCatch try_catch;
- cb->Call(Context::GetCurrent()->Global(), argc, argv);
+ cb->Call(v8::Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
diff --git a/src/node_child_process.cc b/src/node_child_process.cc
index 72787f0..8a0c32c 100644
--- a/src/node_child_process.cc
+++ b/src/node_child_process.cc
@@ -156,7 +156,7 @@ Handle<Value> ChildProcess::Kill(const Arguments& args) {
sig = args[0]->Int32Value();
} else if (args[0]->IsString()) {
Local<String> signame = args[0]->ToString();
- Local<Object> process = Context::GetCurrent()->Global();
+ Local<Object> process = v8::Context::GetCurrent()->Global();
Local<Object> node_obj = process->Get(String::NewSymbol("process"))->ToObject();
Local<Value> sig_v = node_obj->Get(signame);
diff --git a/src/node_file.cc b/src/node_file.cc
index dde2740..a2ec398 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -145,7 +145,7 @@ static int After(eio_req *req) {
TryCatch try_catch;
- (*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
+ (*callback)->Call(v8::Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
diff --git a/src/node_script.cc b/src/node_script.cc
index 78136dd..bf42502 100644
--- a/src/node_script.cc
+++ b/src/node_script.cc
@@ -7,6 +7,52 @@
using namespace v8;
using namespace node;
+Persistent<FunctionTemplate> node::Context::constructor_template;
+
+void
+node::Context::Initialize (Handle<Object> target)
+{
+ HandleScope scope;
+
+ Local<FunctionTemplate> t = FunctionTemplate::New(node::Context::New);
+ constructor_template = Persistent<FunctionTemplate>::New(t);
+ constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
+ constructor_template->SetClassName(String::NewSymbol("Context"));
+
+ target->Set(String::NewSymbol("Context"), constructor_template->GetFunction());
+}
+
+Handle<Value>
+node::Context::New (const Arguments& args)
+{
+ HandleScope scope;
+
+ node::Context *t = new node::Context();
+ t->Wrap(args.This());
+
+ return args.This();
+}
+
+node::Context::~Context() {
+ _context.Dispose();
+}
+
+Local<Object>
+node::Context::NewInstance()
+{
+ Local<Object> context = constructor_template->GetFunction()->NewInstance();
+ node::Context *nContext = ObjectWrap::Unwrap<node::Context>(context);
+ nContext->_context = v8::Context::New();
+ return context;
+}
+
+v8::Persistent<v8::Context>
+node::Context::GetV8Context()
+{
+ return _context;
+}
+
+
Persistent<FunctionTemplate> node::Script::constructor_template;
void
@@ -19,8 +65,12 @@ node::Script::Initialize (Handle<Object> target)
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
constructor_template->SetClassName(String::NewSymbol("Script"));
+ NODE_SET_PROTOTYPE_METHOD(constructor_template, "createContext", node::Script::CreateContext);
+ NODE_SET_PROTOTYPE_METHOD(constructor_template, "runInContext", node::Script::RunInContext);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "runInThisContext", node::Script::RunInThisContext);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "runInNewContext", node::Script::RunInNewContext);
+ NODE_SET_METHOD(constructor_template, "createContext", node::Script::CreateContext);
+ NODE_SET_METHOD(constructor_template, "runInContext", node::Script::CompileRunInContext);
NODE_SET_METHOD(constructor_template, "runInThisContext", node::Script::CompileRunInThisContext);
NODE_SET_METHOD(constructor_template, "runInNewContext", node::Script::CompileRunInNewContext);
@@ -45,6 +95,37 @@ node::Script::~Script() {
Handle<Value>
+node::Script::CreateContext (const Arguments& args)
+{
+ HandleScope scope;
+
+ Local<v8::Object> context = node::Context::NewInstance();
+
+ if (args.Length() > 0) {
+
+ Local<Object> sandbox = args[0]->ToObject();
+ Local<Array> keys = sandbox->GetPropertyNames();
+
+ for (int i = 0; i < keys->Length(); i++) {
+ Handle<String> key = keys->Get(Integer::New(i))->ToString();
+ Handle<Value> value = sandbox->Get(key);
+ context->Set(key, value);
+ }
+ }
+
+
+ return scope.Close(context);
+}
+
+Handle<Value>
+node::Script::RunInContext (const Arguments& args)
+{
+ return
+ node::Script::EvalMachine<unwrapExternal, userContext, returnResult>(args);
+}
+
+
+Handle<Value>
node::Script::RunInThisContext (const Arguments& args)
{
return
@@ -60,6 +141,14 @@ node::Script::RunInNewContext(const Arguments& args) {
Handle<Value>
+node::Script::CompileRunInContext (const Arguments& args)
+{
+ return
+ node::Script::EvalMachine<compileCode, userContext, returnResult>(args);
+}
+
+
+Handle<Value>
node::Script::CompileRunInThisContext (const Arguments& args)
{
return
@@ -90,6 +179,12 @@ Handle<Value> node::Script::EvalMachine(const Arguments& args) {
String::New("needs at least 'code' argument.")
));
}
+ if (cFlag == userContext && args.Length() < 2) {
+ return ThrowException(Exception::TypeError(
+ String::New("needs a 'context' argument.")
+ ));
+ }
+
Local<String> code;
if (iFlag == compileCode) { code = args[0]->ToString(); }
@@ -103,12 +198,12 @@ Handle<Value> node::Script::EvalMachine(const Arguments& args) {
Local<String> filename = args.Length() > fnIndex ? args[fnIndex]->ToString()
: String::New("evalmachine.<anonymous>");
- Persistent<Context> context;
+ Persistent<v8::Context> context;
Local<Array> keys;
unsigned int i;
if (cFlag == newContext) {
// Create the new context
- context = Context::New();
+ context = v8::Context::New();
// Enter and compile script
context->Enter();
@@ -121,6 +216,13 @@ Handle<Value> node::Script::EvalMachine(const Arguments& args) {
Handle<Value> value = sandbox->Get(key);
context->Global()->Set(key, value);
}
+ } else if (cFlag == userContext) {
+ // Use the passed in context
+ node::Context *nContext = ObjectWrap::Unwrap<node::Context>(args[1]->ToObject());
+ context = nContext->GetV8Context();
+
+ // Enter the context
+ context->Enter();
}
// Catch errors
@@ -183,6 +285,9 @@ Handle<Value> node::Script::EvalMachine(const Arguments& args) {
context->DetachGlobal();
context->Exit();
context.Dispose();
+ } else if (cFlag == userContext) {
+ // Exit the passed in context.
+ context->Exit();
}
return result == args.This() ? result : scope.Close(result);
diff --git a/src/node_script.h b/src/node_script.h
index edf51c3..8cac141 100644
--- a/src/node_script.h
+++ b/src/node_script.h
@@ -8,12 +8,30 @@
namespace node {
+class Context : ObjectWrap {
+ public:
+ static void Initialize (v8::Handle<v8::Object> target);
+ static v8::Handle<v8::Value> New (const v8::Arguments& args);
+
+ v8::Persistent<v8::Context> GetV8Context();
+ static v8::Local<v8::Object> NewInstance();
+
+ protected:
+
+ static v8::Persistent<v8::FunctionTemplate> constructor_template;
+
+ Context () : ObjectWrap () {}
+ ~Context();
+
+ v8::Persistent<v8::Context> _context;
+};
+
class Script : ObjectWrap {
public:
static void Initialize (v8::Handle<v8::Object> target);
enum EvalInputFlags { compileCode, unwrapExternal };
- enum EvalContextFlags { thisContext, newContext };
+ enum EvalContextFlags { thisContext, newContext, userContext };
enum EvalOutputFlags { returnResult, wrapExternal };
template <EvalInputFlags iFlag, EvalContextFlags cFlag, EvalOutputFlags oFlag>
@@ -26,8 +44,11 @@ class Script : ObjectWrap {
~Script();
static v8::Handle<v8::Value> New (const v8::Arguments& args);
+ static v8::Handle<v8::Value> CreateContext (const v8::Arguments& arg);
+ static v8::Handle<v8::Value> RunInContext (const v8::Arguments& args);
static v8::Handle<v8::Value> RunInThisContext (const v8::Arguments& args);
static v8::Handle<v8::Value> RunInNewContext (const v8::Arguments& args);
+ static v8::Handle<v8::Value> CompileRunInContext (const v8::Arguments& args);
static v8::Handle<v8::Value> CompileRunInThisContext (const v8::Arguments& args);
static v8::Handle<v8::Value> CompileRunInNewContext (const v8::Arguments& args);
--
1.7.0.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment