Browse Source

Added new API to Script, and implemented it in the REPL

v0.7.4-release
Ruben Rodriguez 15 years ago
committed by Ryan Dahl
parent
commit
242161bef2
  1. 41
      lib/repl.js
  2. 7
      src/node.cc
  3. 8
      src/node_cares.cc
  4. 2
      src/node_child_process.cc
  5. 2
      src/node_file.cc
  6. 125
      src/node_script.cc
  7. 23
      src/node_script.h
  8. 2
      test/simple/test-repl.js
  9. 20
      test/simple/test-script-context.js
  10. 18
      test/simple/test-script-static-context.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() {};

7
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);

8
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);

2
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);

2
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);

125
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);
@ -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);

23
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);

2
test/simple/test-repl.js

@ -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

@ -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

@ -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…
Cancel
Save