#include #include #include namespace node { using v8::Context; using v8::Script; using v8::Value; using v8::Handle; using v8::HandleScope; using v8::Object; using v8::Arguments; using v8::ThrowException; using v8::TryCatch; using v8::String; using v8::Exception; using v8::Local; using v8::Array; using v8::Persistent; using v8::Integer; using v8::FunctionTemplate; class WrappedContext : ObjectWrap { public: static void Initialize(Handle target); static Handle New(const Arguments& args); Persistent GetV8Context(); static Local NewInstance(); protected: static Persistent constructor_template; WrappedContext(); ~WrappedContext(); Persistent context_; }; Persistent WrappedContext::constructor_template; class WrappedScript : ObjectWrap { public: static void Initialize(Handle target); enum EvalInputFlags { compileCode, unwrapExternal }; enum EvalContextFlags { thisContext, newContext, userContext }; enum EvalOutputFlags { returnResult, wrapExternal }; template static Handle EvalMachine(const Arguments& args); protected: static Persistent constructor_template; WrappedScript() : ObjectWrap() {} ~WrappedScript(); static Handle New(const Arguments& args); static Handle CreateContext(const Arguments& arg); static Handle RunInContext(const Arguments& args); static Handle RunInThisContext(const Arguments& args); static Handle RunInNewContext(const Arguments& args); static Handle CompileRunInContext(const Arguments& args); static Handle CompileRunInThisContext(const Arguments& args); static Handle CompileRunInNewContext(const Arguments& args); Persistent script_; }; void WrappedContext::Initialize(Handle target) { HandleScope scope; Local t = FunctionTemplate::New(WrappedContext::New); constructor_template = Persistent::New(t); constructor_template->InstanceTemplate()->SetInternalFieldCount(1); constructor_template->SetClassName(String::NewSymbol("Context")); target->Set(String::NewSymbol("Context"), constructor_template->GetFunction()); } Handle WrappedContext::New(const Arguments& args) { HandleScope scope; WrappedContext *t = new WrappedContext(); t->Wrap(args.This()); return args.This(); } WrappedContext::WrappedContext() : ObjectWrap() { context_ = Context::New(); } WrappedContext::~WrappedContext() { context_.Dispose(); } Local WrappedContext::NewInstance() { Local context = constructor_template->GetFunction()->NewInstance(); return context; } Persistent WrappedContext::GetV8Context() { return context_; } Persistent WrappedScript::constructor_template; void WrappedScript::Initialize(Handle target) { HandleScope scope; Local t = FunctionTemplate::New(WrappedScript::New); constructor_template = Persistent::New(t); constructor_template->InstanceTemplate()->SetInternalFieldCount(1); constructor_template->SetClassName(String::NewSymbol("Script")); NODE_SET_PROTOTYPE_METHOD(constructor_template, "createContext", WrappedScript::CreateContext); NODE_SET_PROTOTYPE_METHOD(constructor_template, "runInContext", WrappedScript::RunInContext); NODE_SET_PROTOTYPE_METHOD(constructor_template, "runInThisContext", WrappedScript::RunInThisContext); NODE_SET_PROTOTYPE_METHOD(constructor_template, "runInNewContext", WrappedScript::RunInNewContext); NODE_SET_METHOD(constructor_template, "createContext", WrappedScript::CreateContext); NODE_SET_METHOD(constructor_template, "runInContext", WrappedScript::CompileRunInContext); NODE_SET_METHOD(constructor_template, "runInThisContext", WrappedScript::CompileRunInThisContext); NODE_SET_METHOD(constructor_template, "runInNewContext", WrappedScript::CompileRunInNewContext); target->Set(String::NewSymbol("Script"), constructor_template->GetFunction()); } Handle WrappedScript::New(const Arguments& args) { if (!args.IsConstructCall()) { return FromConstructorTemplate(constructor_template, args); } HandleScope scope; WrappedScript *t = new WrappedScript(); t->Wrap(args.Holder()); return WrappedScript::EvalMachine(args); } WrappedScript::~WrappedScript() { script_.Dispose(); } Handle WrappedScript::CreateContext(const Arguments& args) { HandleScope scope; Local context = WrappedContext::NewInstance(); if (args.Length() > 0) { Local sandbox = args[0]->ToObject(); Local keys = sandbox->GetPropertyNames(); for (uint32_t i = 0; i < keys->Length(); i++) { Handle key = keys->Get(Integer::New(i))->ToString(); Handle value = sandbox->Get(key); context->Set(key, value); } } return scope.Close(context); } Handle WrappedScript::RunInContext(const Arguments& args) { return WrappedScript::EvalMachine(args); } Handle WrappedScript::RunInThisContext(const Arguments& args) { return WrappedScript::EvalMachine(args); } Handle WrappedScript::RunInNewContext(const Arguments& args) { return WrappedScript::EvalMachine(args); } Handle WrappedScript::CompileRunInContext(const Arguments& args) { return WrappedScript::EvalMachine(args); } Handle WrappedScript::CompileRunInThisContext(const Arguments& args) { return WrappedScript::EvalMachine(args); } Handle WrappedScript::CompileRunInNewContext(const Arguments& args) { return WrappedScript::EvalMachine(args); } template Handle WrappedScript::EvalMachine(const Arguments& args) { HandleScope scope; if (input_flag == compileCode && args.Length() < 1) { return ThrowException(Exception::TypeError( String::New("needs at least 'code' argument."))); } const int sandbox_index = input_flag == compileCode ? 1 : 0; if (context_flag == userContext && args.Length() < (sandbox_index + 1)) { return ThrowException(Exception::TypeError( String::New("needs a 'context' argument."))); } Local code; if (input_flag == compileCode) code = args[0]->ToString(); Local sandbox; if (context_flag == newContext) { sandbox = args[sandbox_index]->IsObject() ? args[sandbox_index]->ToObject() : Object::New(); } else if (context_flag == userContext) { sandbox = args[sandbox_index]->ToObject(); } const int filename_index = sandbox_index + (context_flag == newContext ? 1 : 0); Local filename = args.Length() > filename_index ? args[filename_index]->ToString() : String::New("evalmachine."); const int display_error_index = args.Length() - 1; bool display_error = false; if (args.Length() > display_error_index && args[display_error_index]->IsBoolean() && args[display_error_index]->BooleanValue() == true) { display_error = true; } Persistent context; Local keys; unsigned int i; if (context_flag == newContext) { // Create the new context context = Context::New(); } else if (context_flag == userContext) { // Use the passed in context Local contextArg = args[sandbox_index]->ToObject(); WrappedContext *nContext = ObjectWrap::Unwrap(sandbox); context = nContext->GetV8Context(); } // New and user context share code. DRY it up. if (context_flag == userContext || context_flag == newContext) { // Enter the context context->Enter(); // 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++) { Handle key = keys->Get(Integer::New(i))->ToString(); Handle value = sandbox->Get(key); if (value == sandbox) { value = context->Global(); } context->Global()->Set(key, value); } } // Catch errors TryCatch try_catch; Handle result; Handle script; if (input_flag == compileCode) { // well, here WrappedScript::New would suffice in all cases, but maybe // Compile has a little better performance where possible script = output_flag == returnResult ? Script::Compile(code, filename) : Script::New(code, filename); if (script.IsEmpty()) { // FIXME UGLY HACK TO DISPLAY SYNTAX ERRORS. if (display_error) DisplayExceptionLine(try_catch); // Hack because I can't get a proper stacktrace on SyntaxError return try_catch.ReThrow(); } } else { WrappedScript *n_script = ObjectWrap::Unwrap(args.Holder()); if (!n_script) { return ThrowException(Exception::Error( String::New("Must be called as a method of Script."))); } else if (n_script->script_.IsEmpty()) { return ThrowException(Exception::Error( String::New("'this' must be a result of previous " "new Script(code) call."))); } script = n_script->script_; } if (output_flag == returnResult) { result = script->Run(); if (result.IsEmpty()) return try_catch.ReThrow(); } else { WrappedScript *n_script = ObjectWrap::Unwrap(args.Holder()); if (!n_script) { return ThrowException(Exception::Error( String::New("Must be called as a method of Script."))); } n_script->script_ = Persistent::New(script); result = args.This(); } if (context_flag == userContext || context_flag == newContext) { // success! copy changes back onto the sandbox object. keys = context->Global()->GetPropertyNames(); for (i = 0; i < keys->Length(); i++) { Handle key = keys->Get(Integer::New(i))->ToString(); Handle value = context->Global()->Get(key); if (value == context->Global()) { value = sandbox; } sandbox->Set(key, value); } } if (context_flag == newContext) { // Clean up, clean up, everybody everywhere! context->DetachGlobal(); context->Exit(); context.Dispose(); } else if (context_flag == userContext) { // Exit the passed in context. context->Exit(); } return result == args.This() ? result : scope.Close(result); } void InitEvals(Handle target) { HandleScope scope; WrappedContext::Initialize(target); WrappedScript::Initialize(target); } } // namespace node NODE_MODULE(node_evals, node::InitEvals);