Browse Source

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

v0.7.4-release
Herbert Vojcik 15 years ago
committed by Ryan Dahl
parent
commit
c2a06725d6
  1. 18
      src/node.cc
  2. 1
      src/node.js
  3. 237
      src/node_script.cc
  4. 38
      src/node_script.h
  5. 53
      test/simple/test-script-new.js
  6. 39
      test/simple/test-script-static-new.js
  7. 33
      test/simple/test-script-static-this.js
  8. 38
      test/simple/test-script-this.js
  9. 1
      wscript

18
src/node.cc

@ -38,6 +38,7 @@
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
#include <node_crypto.h> #include <node_crypto.h>
#endif #endif
#include <node_script.h>
#include <v8-debug.h> #include <v8-debug.h>
@ -520,7 +521,7 @@ Local<Value> ExecuteString(Local<String> source, Local<Value> filename) {
HandleScope scope; HandleScope scope;
TryCatch try_catch; TryCatch try_catch;
Local<Script> script = Script::Compile(source, filename); Local<v8::Script> script = v8::Script::Compile(source, filename);
if (script.IsEmpty()) { if (script.IsEmpty()) {
ReportException(try_catch); ReportException(try_catch);
exit(1); exit(1);
@ -1020,7 +1021,7 @@ Handle<Value> EvalCX(const Arguments& args) {
// Catch errors // Catch errors
TryCatch try_catch; TryCatch try_catch;
Local<Script> script = Script::Compile(code, filename); Local<v8::Script> script = v8::Script::Compile(code, filename);
Handle<Value> result; Handle<Value> result;
if (script.IsEmpty()) { if (script.IsEmpty()) {
@ -1061,7 +1062,7 @@ Handle<Value> Compile(const Arguments& args) {
TryCatch try_catch; TryCatch try_catch;
Local<Script> script = Script::Compile(source, filename); Local<v8::Script> script = v8::Script::Compile(source, filename);
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {
// Hack because I can't get a proper stacktrace on SyntaxError // Hack because I can't get a proper stacktrace on SyntaxError
ReportException(try_catch, true); ReportException(try_catch, true);
@ -1323,6 +1324,15 @@ static Handle<Value> Binding(const Arguments& args) {
binding_cache->Set(module, exports); binding_cache->Set(module, exports);
} }
#endif #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")) { } else if (!strcmp(*module_v, "natives")) {
if (binding_cache->Has(module)) { if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject(); exports = binding_cache->Get(module)->ToObject();
@ -1422,7 +1432,7 @@ static void Load(int argc, char *argv[]) {
// define various internal methods // define various internal methods
NODE_SET_METHOD(process, "loop", Loop); NODE_SET_METHOD(process, "loop", Loop);
NODE_SET_METHOD(process, "unloop", Unloop); 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, "compile", Compile);
NODE_SET_METHOD(process, "_byteLength", ByteLength); NODE_SET_METHOD(process, "_byteLength", ByteLength);
NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback); NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback);

1
src/node.js

@ -100,6 +100,7 @@ process.assert = function (x, msg) {
if (!(x)) throw new Error(msg || "assertion error"); if (!(x)) throw new Error(msg || "assertion error");
}; };
process.evalcx = process.binding('evals').Script.runInNewContext;
// Event // Event

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

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

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";');
});

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

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

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

1
wscript

@ -392,6 +392,7 @@ def build(bld):
src/node_stat_watcher.cc src/node_stat_watcher.cc
src/node_stdio.cc src/node_stdio.cc
src/node_timer.cc src/node_timer.cc
src/node_script.cc
""" """
if bld.env["USE_OPENSSL"]: if bld.env["USE_OPENSSL"]:
node.source += "src/node_crypto.cc" node.source += "src/node_crypto.cc"

Loading…
Cancel
Save