Skip to content

Commit

Permalink
Added new API to Script, and implemented it in the REPL
Browse files Browse the repository at this point in the history
  • Loading branch information
cha0s authored and ry committed Jun 28, 2010
1 parent 5a25338 commit 242161b
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 37 deletions.
41 changes: 21 additions & 20 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(/^\.\//)) {
Expand All @@ -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;
}


Expand All @@ -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();
Expand Down Expand Up @@ -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");
}

Expand Down Expand Up @@ -150,17 +151,17 @@ 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":
self.stream.destroy();
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();
Expand All @@ -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() {};
Expand Down
7 changes: 4 additions & 3 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down
8 changes: 4 additions & 4 deletions src/node_cares.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/node_child_process.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
125 changes: 119 additions & 6 deletions src/node_script.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);

Expand All @@ -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)
{
Expand All @@ -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)
{
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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++) {
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 242161b

Please sign in to comment.