Added new API to Script, and implemented it in the REPL
This commit is contained in:
parent
5a25338ac0
commit
242161bef2
41
lib/repl.js
41
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,8 @@ exports.writer = sys.inspect;
|
||||
|
||||
function REPLServer(prompt, stream) {
|
||||
var self = this;
|
||||
if (!scope) setScope();
|
||||
self.scope = scope;
|
||||
if (!context) resetContext();
|
||||
self.context = context;
|
||||
self.buffered_cmd = '';
|
||||
|
||||
self.stream = stream || process.openStdin();
|
||||
@ -70,10 +71,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 +151,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 +161,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 +181,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() {};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
@ -44,6 +94,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)
|
||||
{
|
||||
@ -59,6 +140,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)
|
||||
{
|
||||
@ -91,29 +180,50 @@ Handle<Value> node::Script::EvalMachine(const Arguments& args) {
|
||||
));
|
||||
}
|
||||
|
||||
const int sbIndex = iFlag == compileCode ? 1 : 0;
|
||||
if (cFlag == userContext && args.Length() < (sbIndex + 1)) {
|
||||
return ThrowException(Exception::TypeError(
|
||||
String::New("needs a 'context' argument.")
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Local<String> code;
|
||||
if (iFlag == compileCode) { code = args[0]->ToString(); }
|
||||
|
||||
Local<Object> sandbox;
|
||||
const int sbIndex = iFlag == compileCode ? 1 : 0;
|
||||
if (cFlag == newContext) {
|
||||
sandbox = args.Length() > sbIndex ? args[sbIndex]->ToObject() : Object::New();
|
||||
}
|
||||
else if (cFlag == userContext) {
|
||||
sandbox = args[sbIndex]->ToObject();
|
||||
}
|
||||
const int fnIndex = sbIndex + (cFlag == newContext ? 1 : 0);
|
||||
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
|
||||
} else if (cFlag == userContext) {
|
||||
// Use the passed in context
|
||||
Local<Object> contextArg = args[sbIndex]->ToObject();
|
||||
node::Context *nContext = ObjectWrap::Unwrap<node::Context>(sandbox);
|
||||
context = nContext->GetV8Context();
|
||||
}
|
||||
|
||||
// New and user context share code. DRY it up.
|
||||
if (cFlag == userContext || cFlag == newContext) {
|
||||
|
||||
// Enter the context
|
||||
context->Enter();
|
||||
|
||||
// Copy objects from global context, to our brand new context
|
||||
// Copy everything from the passed in sandbox (either the persistent
|
||||
// context for runInContext(), or the sandbox arg to runInNewContext()).
|
||||
keys = sandbox->GetPropertyNames();
|
||||
|
||||
for (i = 0; i < keys->Length(); i++) {
|
||||
@ -167,7 +277,7 @@ Handle<Value> node::Script::EvalMachine(const Arguments& args) {
|
||||
}
|
||||
if (result.IsEmpty()) {
|
||||
return try_catch.ReThrow();
|
||||
} else if (cFlag == newContext) {
|
||||
} else if (cFlag == userContext || cFlag == newContext) {
|
||||
// success! copy changes back onto the sandbox object.
|
||||
keys = context->Global()->GetPropertyNames();
|
||||
for (i = 0; i < keys->Length(); i++) {
|
||||
@ -183,6 +293,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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -99,7 +99,7 @@ function unix_test() {
|
||||
socket.end();
|
||||
});
|
||||
|
||||
repl.start(prompt_unix, socket).scope.message = message;
|
||||
repl.start(prompt_unix, socket).context.message = message;
|
||||
});
|
||||
|
||||
server_unix.addListener('listening', function () {
|
||||
|
20
test/simple/test-script-context.js
Normal file
20
test/simple/test-script-context.js
Normal file
@ -0,0 +1,20 @@
|
||||
require("../common");
|
||||
|
||||
var Script = process.binding('evals').Script;
|
||||
var script = new Script('"passed";');
|
||||
|
||||
debug('run in a new empty context');
|
||||
var context = script.createContext();
|
||||
var result = script.runInContext(context);
|
||||
assert.equal('passed', result);
|
||||
|
||||
debug('create a new pre-populated context');
|
||||
context = script.createContext({'foo': 'bar', 'thing': 'lala'});
|
||||
assert.equal('bar', context.foo);
|
||||
assert.equal('lala', context.thing);
|
||||
|
||||
debug('test updating context');
|
||||
script = new Script('foo = 3;');
|
||||
result = script.runInContext(context);
|
||||
assert.equal(3, context.foo);
|
||||
assert.equal('lala', context.thing);
|
18
test/simple/test-script-static-context.js
Normal file
18
test/simple/test-script-static-context.js
Normal file
@ -0,0 +1,18 @@
|
||||
require("../common");
|
||||
|
||||
var Script = process.binding('evals').Script;
|
||||
|
||||
debug('run in a new empty context');
|
||||
var context = Script.createContext();
|
||||
var result = Script.runInContext('"passed";', context);
|
||||
assert.equal('passed', result);
|
||||
|
||||
debug('create a new pre-populated context');
|
||||
context = Script.createContext({'foo': 'bar', 'thing': 'lala'});
|
||||
assert.equal('bar', context.foo);
|
||||
assert.equal('lala', context.thing);
|
||||
|
||||
debug('test updating context');
|
||||
result = Script.runInContext('var foo = 3;', context);
|
||||
assert.equal(3, context.foo);
|
||||
assert.equal('lala', context.thing);
|
Loading…
x
Reference in New Issue
Block a user