Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Script class with eval-function-family in binding('evals') plus tests.

  • Loading branch information...
commit c2a06725d6ebc0b23bfb20a8ce478b1ebf042c4e 1 parent 39464b4
@herby herby authored ry committed
View
18 src/node.cc
@@ -38,6 +38,7 @@
#ifdef HAVE_OPENSSL
#include <node_crypto.h>
#endif
+#include <node_script.h>
#include <v8-debug.h>
@@ -520,7 +521,7 @@ Local<Value> ExecuteString(Local<String> source, Local<Value> filename) {
HandleScope scope;
TryCatch try_catch;
- Local<Script> script = Script::Compile(source, filename);
+ Local<v8::Script> script = v8::Script::Compile(source, filename);
if (script.IsEmpty()) {
ReportException(try_catch);
exit(1);
@@ -1020,7 +1021,7 @@ Handle<Value> EvalCX(const Arguments& args) {
// Catch errors
TryCatch try_catch;
- Local<Script> script = Script::Compile(code, filename);
+ Local<v8::Script> script = v8::Script::Compile(code, filename);
Handle<Value> result;
if (script.IsEmpty()) {
@@ -1061,7 +1062,7 @@ Handle<Value> Compile(const Arguments& args) {
TryCatch try_catch;
- Local<Script> script = Script::Compile(source, filename);
+ Local<v8::Script> script = v8::Script::Compile(source, filename);
if (try_catch.HasCaught()) {
// Hack because I can't get a proper stacktrace on SyntaxError
ReportException(try_catch, true);
@@ -1323,6 +1324,15 @@ static Handle<Value> Binding(const Arguments& args) {
binding_cache->Set(module, exports);
}
#endif
+ } else if (!strcmp(*module_v, "evals")) {
+ if (binding_cache->Has(module)) {
+ exports = binding_cache->Get(module)->ToObject();
+ } else {
+ exports = Object::New();
+ node::Script::Initialize(exports);
+ binding_cache->Set(module, exports);
+ }
+
} else if (!strcmp(*module_v, "natives")) {
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
@@ -1422,7 +1432,7 @@ static void Load(int argc, char *argv[]) {
// define various internal methods
NODE_SET_METHOD(process, "loop", Loop);
NODE_SET_METHOD(process, "unloop", Unloop);
- NODE_SET_METHOD(process, "evalcx", EvalCX);
+// NODE_SET_METHOD(process, "evalcx", EvalCX);
NODE_SET_METHOD(process, "compile", Compile);
NODE_SET_METHOD(process, "_byteLength", ByteLength);
NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback);
View
1  src/node.js
@@ -100,6 +100,7 @@ process.assert = function (x, msg) {
if (!(x)) throw new Error(msg || "assertion error");
};
+process.evalcx = process.binding('evals').Script.runInNewContext;
// Event
View
237 src/node_script.cc
@@ -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);
+}
View
38 src/node_script.h
@@ -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
View
53 test/simple/test-script-new.js
@@ -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";');
+});
+
+
View
39 test/simple/test-script-static-new.js
@@ -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);
+
View
33 test/simple/test-script-static-this.js
@@ -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);
View
38 test/simple/test-script-this.js
@@ -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);
View
1  wscript
@@ -392,6 +392,7 @@ def build(bld):
src/node_stat_watcher.cc
src/node_stdio.cc
src/node_timer.cc
+ src/node_script.cc
"""
if bld.env["USE_OPENSSL"]:
node.source += "src/node_crypto.cc"

0 comments on commit c2a0672

Please sign in to comment.
Something went wrong with that request. Please try again.