mirror of https://github.com/lukechilds/node.git
Herbert Vojcik
15 years ago
committed by
Ryan Dahl
9 changed files with 454 additions and 4 deletions
@ -0,0 +1,237 @@ |
|||||
|
#include <node.h> |
||||
|
#include <node_script.h> |
||||
|
#include <assert.h> |
||||
|
|
||||
|
#include <v8-debug.h> |
||||
|
|
||||
|
using namespace v8; |
||||
|
using namespace node; |
||||
|
|
||||
|
Persistent<FunctionTemplate> node::Script::constructor_template; |
||||
|
|
||||
|
void |
||||
|
node::Script::Initialize (Handle<Object> target) |
||||
|
{ |
||||
|
HandleScope scope; |
||||
|
|
||||
|
Local<FunctionTemplate> t = FunctionTemplate::New(node::Script::New); |
||||
|
constructor_template = Persistent<FunctionTemplate>::New(t); |
||||
|
constructor_template->InstanceTemplate()->SetInternalFieldCount(1); |
||||
|
constructor_template->SetClassName(String::NewSymbol("Script")); |
||||
|
|
||||
|
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, "runInThisContext", node::Script::CompileRunInThisContext); |
||||
|
NODE_SET_METHOD(constructor_template, "runInNewContext", node::Script::CompileRunInNewContext); |
||||
|
|
||||
|
target->Set(String::NewSymbol("Script"), constructor_template->GetFunction()); |
||||
|
} |
||||
|
|
||||
|
Handle<Value> |
||||
|
node::Script::New (const Arguments& args) |
||||
|
{ |
||||
|
HandleScope scope; |
||||
|
|
||||
|
node::Script *t = new node::Script(); |
||||
|
t->Wrap(args.Holder()); |
||||
|
|
||||
|
return |
||||
|
node::Script::EvalMachine<compileCode, thisContext, wrapExternal>(args); |
||||
|
} |
||||
|
|
||||
|
node::Script::~Script() { |
||||
|
_script.Dispose(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Handle<Value> |
||||
|
node::Script::RunInThisContext (const Arguments& args) |
||||
|
{ |
||||
|
return |
||||
|
node::Script::EvalMachine<unwrapExternal, thisContext, returnResult>(args); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Handle<Value> |
||||
|
node::Script::RunInNewContext(const Arguments& args) { |
||||
|
return |
||||
|
node::Script::EvalMachine<unwrapExternal, newContext, returnResult>(args); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Handle<Value> |
||||
|
node::Script::CompileRunInThisContext (const Arguments& args) |
||||
|
{ |
||||
|
return |
||||
|
node::Script::EvalMachine<compileCode, thisContext, returnResult>(args); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Handle<Value> |
||||
|
node::Script::CompileRunInNewContext(const Arguments& args) { |
||||
|
return |
||||
|
node::Script::EvalMachine<compileCode, newContext, returnResult>(args); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Extracts a C str from a V8 Utf8Value.
|
||||
|
const char* ToCString(const v8::String::Utf8Value& value) { |
||||
|
return *value ? *value : "<str conversion failed>"; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void ReportException(TryCatch &try_catch, bool show_line = false, bool show_rest = true) { |
||||
|
Handle<Message> message = try_catch.Message(); |
||||
|
|
||||
|
Handle<Value> error = try_catch.Exception(); |
||||
|
Handle<String> stack; |
||||
|
|
||||
|
if (error->IsObject()) { |
||||
|
Handle<Object> obj = Handle<Object>::Cast(error); |
||||
|
Handle<Value> raw_stack = obj->Get(String::New("stack")); |
||||
|
if (raw_stack->IsString()) stack = Handle<String>::Cast(raw_stack); |
||||
|
} |
||||
|
|
||||
|
if (show_line && !message.IsEmpty()) { |
||||
|
// Print (filename):(line number): (message).
|
||||
|
String::Utf8Value filename(message->GetScriptResourceName()); |
||||
|
const char* filename_string = ToCString(filename); |
||||
|
int linenum = message->GetLineNumber(); |
||||
|
fprintf(stderr, "%s:%i\n", filename_string, linenum); |
||||
|
// Print line of source code.
|
||||
|
String::Utf8Value sourceline(message->GetSourceLine()); |
||||
|
const char* sourceline_string = ToCString(sourceline); |
||||
|
fprintf(stderr, "%s\n", sourceline_string); |
||||
|
// Print wavy underline (GetUnderline is deprecated).
|
||||
|
int start = message->GetStartColumn(); |
||||
|
for (int i = 0; i < start; i++) { |
||||
|
fprintf(stderr, " "); |
||||
|
} |
||||
|
int end = message->GetEndColumn(); |
||||
|
for (int i = start; i < end; i++) { |
||||
|
fprintf(stderr, "^"); |
||||
|
} |
||||
|
fprintf(stderr, "\n"); |
||||
|
} |
||||
|
|
||||
|
if (show_rest) { |
||||
|
if (stack.IsEmpty()) { |
||||
|
message->PrintCurrentStackTrace(stderr); |
||||
|
} else { |
||||
|
String::Utf8Value trace(stack); |
||||
|
fprintf(stderr, "%s\n", *trace); |
||||
|
} |
||||
|
} |
||||
|
fflush(stderr); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
template <node::Script::EvalInputFlags iFlag, |
||||
|
node::Script::EvalContextFlags cFlag, |
||||
|
node::Script::EvalOutputFlags oFlag> |
||||
|
Handle<Value> node::Script::EvalMachine(const Arguments& args) { |
||||
|
HandleScope scope; |
||||
|
|
||||
|
if (iFlag == compileCode && args.Length() < 1) { |
||||
|
return ThrowException(Exception::TypeError( |
||||
|
String::New("needs at least 'code' 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(); |
||||
|
} |
||||
|
const int fnIndex = sbIndex + (cFlag == newContext ? 1 : 0); |
||||
|
Local<String> filename = args.Length() > fnIndex ? args[fnIndex]->ToString() |
||||
|
: String::New("evalmachine.<anonymous>"); |
||||
|
|
||||
|
Persistent<Context> context; |
||||
|
Local<Array> keys; |
||||
|
unsigned int i; |
||||
|
if (cFlag == newContext) { |
||||
|
// Create the new context
|
||||
|
context = Context::New(); |
||||
|
|
||||
|
// Enter and compile script
|
||||
|
context->Enter(); |
||||
|
|
||||
|
// Copy objects from global context, to our brand new context
|
||||
|
keys = sandbox->GetPropertyNames(); |
||||
|
|
||||
|
for (i = 0; i < keys->Length(); i++) { |
||||
|
Handle<String> key = keys->Get(Integer::New(i))->ToString(); |
||||
|
Handle<Value> value = sandbox->Get(key); |
||||
|
context->Global()->Set(key, value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Catch errors
|
||||
|
TryCatch try_catch; |
||||
|
|
||||
|
Handle<Value> result; |
||||
|
Handle<v8::Script> script; |
||||
|
|
||||
|
if (iFlag == compileCode) { |
||||
|
// well, here node::Script::New would suffice in all cases, but maybe Compile has a little better performance where possible
|
||||
|
script = oFlag == returnResult ? v8::Script::Compile(code, filename) : v8::Script::New(code, filename); |
||||
|
if (script.IsEmpty()) { |
||||
|
// Hack because I can't get a proper stacktrace on SyntaxError
|
||||
|
ReportException(try_catch, true, false); |
||||
|
result = ThrowException(try_catch.Exception()); |
||||
|
} |
||||
|
} else { |
||||
|
node::Script *nScript = ObjectWrap::Unwrap<node::Script>(args.Holder()); |
||||
|
if (!nScript) { |
||||
|
Local<Value> exception = |
||||
|
Exception::Error(String::New("Must be called as a method of Script.")); |
||||
|
result = ThrowException(exception); |
||||
|
} else if (nScript->_script.IsEmpty()) { |
||||
|
Local<Value> exception = |
||||
|
Exception::Error(String::New("'this' must be a result of previous new Script(code) call.")); |
||||
|
result = ThrowException(exception); |
||||
|
} else { |
||||
|
script = nScript->_script; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (result.IsEmpty()) { |
||||
|
if (oFlag == returnResult) { |
||||
|
result = script->Run(); |
||||
|
} else { |
||||
|
node::Script *nScript = ObjectWrap::Unwrap<node::Script>(args.Holder()); |
||||
|
if (!nScript) { |
||||
|
Local<Value> exception = |
||||
|
Exception::Error(String::New("Must be called as a method of Script.")); |
||||
|
result = ThrowException(exception); |
||||
|
} else { |
||||
|
nScript->_script = Persistent<v8::Script>::New(script); |
||||
|
result = args.This(); |
||||
|
} |
||||
|
} |
||||
|
if (result.IsEmpty()) { |
||||
|
result = ThrowException(try_catch.Exception()); |
||||
|
} else if (cFlag == newContext) { |
||||
|
// success! copy changes back onto the sandbox object.
|
||||
|
keys = context->Global()->GetPropertyNames(); |
||||
|
for (i = 0; i < keys->Length(); i++) { |
||||
|
Handle<String> key = keys->Get(Integer::New(i))->ToString(); |
||||
|
Handle<Value> value = context->Global()->Get(key); |
||||
|
sandbox->Set(key, value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (cFlag == newContext) { |
||||
|
// Clean up, clean up, everybody everywhere!
|
||||
|
context->DetachGlobal(); |
||||
|
context->Exit(); |
||||
|
context.Dispose(); |
||||
|
} |
||||
|
|
||||
|
return result == args.This() ? result : scope.Close(result); |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
#ifndef node_script_h |
||||
|
#define node_script_h |
||||
|
|
||||
|
#include <node.h> |
||||
|
#include <node_object_wrap.h> |
||||
|
#include <v8.h> |
||||
|
#include <ev.h> |
||||
|
|
||||
|
namespace node { |
||||
|
|
||||
|
class Script : ObjectWrap { |
||||
|
public: |
||||
|
static void Initialize (v8::Handle<v8::Object> target); |
||||
|
|
||||
|
enum EvalInputFlags { compileCode, unwrapExternal }; |
||||
|
enum EvalContextFlags { thisContext, newContext }; |
||||
|
enum EvalOutputFlags { returnResult, wrapExternal }; |
||||
|
|
||||
|
template <EvalInputFlags iFlag, EvalContextFlags cFlag, EvalOutputFlags oFlag> |
||||
|
static v8::Handle<v8::Value> EvalMachine(const v8::Arguments& args); |
||||
|
|
||||
|
protected: |
||||
|
static v8::Persistent<v8::FunctionTemplate> constructor_template; |
||||
|
|
||||
|
Script () : ObjectWrap () {} |
||||
|
~Script(); |
||||
|
|
||||
|
static v8::Handle<v8::Value> New (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> CompileRunInThisContext (const v8::Arguments& args); |
||||
|
static v8::Handle<v8::Value> CompileRunInNewContext (const v8::Arguments& args); |
||||
|
|
||||
|
v8::Persistent<v8::Script> _script; |
||||
|
}; |
||||
|
|
||||
|
} // namespace node
|
||||
|
#endif // node_script_h
|
@ -0,0 +1,53 @@ |
|||||
|
require("../common"); |
||||
|
|
||||
|
var Script = process.binding('evals').Script; |
||||
|
debug('run a string'); |
||||
|
var script = new Script('"passed";'); |
||||
|
debug('script created'); |
||||
|
var result1 = script.runInNewContext(); |
||||
|
var result2 = script.runInNewContext(); |
||||
|
assert.equal('passed', result1); |
||||
|
assert.equal('passed', result2); |
||||
|
|
||||
|
debug('thrown error'); |
||||
|
script = new Script('throw new Error("test");'); |
||||
|
assert.throws(function() { |
||||
|
script.runInNewContext(); |
||||
|
}); |
||||
|
|
||||
|
hello = 5; |
||||
|
script = new Script('hello = 2'); |
||||
|
script.runInNewContext(); |
||||
|
assert.equal(5, hello); |
||||
|
|
||||
|
|
||||
|
debug("pass values in and out"); |
||||
|
code = "foo = 1;" |
||||
|
+ "bar = 2;" |
||||
|
+ "if (baz !== 3) throw new Error('test fail');"; |
||||
|
foo = 2; |
||||
|
obj = { foo : 0, baz : 3 }; |
||||
|
script = new Script(code); |
||||
|
var baz = script.runInNewContext(obj); |
||||
|
assert.equal(1, obj.foo); |
||||
|
assert.equal(2, obj.bar); |
||||
|
assert.equal(2, foo); |
||||
|
|
||||
|
debug("call a function by reference"); |
||||
|
script = new Script("f()"); |
||||
|
function changeFoo () { foo = 100 } |
||||
|
script.runInNewContext({ f : changeFoo }); |
||||
|
assert.equal(foo, 100); |
||||
|
|
||||
|
debug("modify an object by reference"); |
||||
|
script = new Script("f.a = 2"); |
||||
|
var f = { a : 1 }; |
||||
|
script.runInNewContext({ f : f }); |
||||
|
assert.equal(f.a, 2); |
||||
|
|
||||
|
debug("invalid this"); |
||||
|
assert.throws(function() { |
||||
|
script.runInNewContext.call('"hello";'); |
||||
|
}); |
||||
|
|
||||
|
|
@ -0,0 +1,39 @@ |
|||||
|
require("../common"); |
||||
|
|
||||
|
var Script = process.binding('evals').Script; |
||||
|
|
||||
|
debug('run a string'); |
||||
|
var result = Script.runInNewContext('"passed";'); |
||||
|
assert.equal('passed', result); |
||||
|
|
||||
|
debug('thrown error'); |
||||
|
assert.throws(function() { |
||||
|
Script.runInNewContext('throw new Error("test");'); |
||||
|
}); |
||||
|
|
||||
|
hello = 5; |
||||
|
Script.runInNewContext('hello = 2'); |
||||
|
assert.equal(5, hello); |
||||
|
|
||||
|
|
||||
|
debug("pass values in and out"); |
||||
|
code = "foo = 1;" |
||||
|
+ "bar = 2;" |
||||
|
+ "if (baz !== 3) throw new Error('test fail');"; |
||||
|
foo = 2; |
||||
|
obj = { foo : 0, baz : 3 }; |
||||
|
var baz = Script.runInNewContext(code, obj); |
||||
|
assert.equal(1, obj.foo); |
||||
|
assert.equal(2, obj.bar); |
||||
|
assert.equal(2, foo); |
||||
|
|
||||
|
debug("call a function by reference"); |
||||
|
function changeFoo () { foo = 100 } |
||||
|
Script.runInNewContext("f()", { f : changeFoo }); |
||||
|
assert.equal(foo, 100); |
||||
|
|
||||
|
debug("modify an object by reference"); |
||||
|
var f = { a : 1 }; |
||||
|
Script.runInNewContext("f.a = 2", { f : f }); |
||||
|
assert.equal(f.a, 2); |
||||
|
|
@ -0,0 +1,33 @@ |
|||||
|
require("../common"); |
||||
|
|
||||
|
var Script = process.binding('evals').Script; |
||||
|
|
||||
|
debug('run a string'); |
||||
|
var result = Script.runInThisContext('"passed";'); |
||||
|
assert.equal('passed', result); |
||||
|
|
||||
|
debug('thrown error'); |
||||
|
assert.throws(function() { |
||||
|
Script.runInThisContext('throw new Error("test");'); |
||||
|
}); |
||||
|
|
||||
|
hello = 5; |
||||
|
Script.runInThisContext('hello = 2'); |
||||
|
assert.equal(2, hello); |
||||
|
|
||||
|
|
||||
|
debug("pass values"); |
||||
|
code = "foo = 1;" |
||||
|
+ "bar = 2;" |
||||
|
+ "if (typeof baz !== 'undefined') throw new Error('test fail');"; |
||||
|
foo = 2; |
||||
|
obj = { foo : 0, baz : 3 }; |
||||
|
var baz = Script.runInThisContext(code); |
||||
|
assert.equal(0, obj.foo); |
||||
|
assert.equal(2, bar); |
||||
|
assert.equal(1, foo); |
||||
|
|
||||
|
debug("call a function"); |
||||
|
f = function () { foo = 100 }; |
||||
|
Script.runInThisContext("f()"); |
||||
|
assert.equal(100, foo); |
@ -0,0 +1,38 @@ |
|||||
|
require("../common"); |
||||
|
|
||||
|
var Script = process.binding('evals').Script; |
||||
|
|
||||
|
debug('run a string'); |
||||
|
var script = new Script('"passed";'); |
||||
|
var result = script.runInThisContext(script); |
||||
|
assert.equal('passed', result); |
||||
|
|
||||
|
debug('thrown error'); |
||||
|
script = new Script('throw new Error("test");'); |
||||
|
assert.throws(function() { |
||||
|
script.runInThisContext(script); |
||||
|
}); |
||||
|
|
||||
|
hello = 5; |
||||
|
script = new Script('hello = 2'); |
||||
|
script.runInThisContext(script); |
||||
|
assert.equal(2, hello); |
||||
|
|
||||
|
|
||||
|
debug("pass values"); |
||||
|
code = "foo = 1;" |
||||
|
+ "bar = 2;" |
||||
|
+ "if (typeof baz !== 'undefined') throw new Error('test fail');"; |
||||
|
foo = 2; |
||||
|
obj = { foo : 0, baz : 3 }; |
||||
|
script = new Script(code); |
||||
|
script.runInThisContext(script); |
||||
|
assert.equal(0, obj.foo); |
||||
|
assert.equal(2, bar); |
||||
|
assert.equal(1, foo); |
||||
|
|
||||
|
debug("call a function"); |
||||
|
f = function () { foo = 100 }; |
||||
|
script = new Script("f()"); |
||||
|
script.runInThisContext(script); |
||||
|
assert.equal(100, foo); |
Loading…
Reference in new issue