// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node.h" #include "node_internals.h" #include "node_watchdog.h" namespace node { using v8::AccessType; using v8::Array; using v8::Boolean; using v8::Context; using v8::External; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; using v8::None; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::PropertyCallbackInfo; using v8::Script; using v8::String; using v8::TryCatch; using v8::V8; using v8::Value; class ContextifyContext { private: Persistent sandbox_; Persistent proxy_global_; Persistent context_; static Persistent data_wrapper_ctor; public: explicit ContextifyContext(Local sandbox) : sandbox_(node_isolate, sandbox) { HandleScope scope(node_isolate); Local v8_context = CreateV8Context(); context_.Reset(node_isolate, v8_context); proxy_global_.Reset(node_isolate, v8_context->Global()); sandbox_.MakeWeak(this, SandboxFreeCallback); sandbox_.MarkIndependent(); } ~ContextifyContext() { context_.Dispose(); proxy_global_.Dispose(); sandbox_.Dispose(); } // This is an object that just keeps an internal pointer to this // ContextifyContext. It's passed to the NamedPropertyHandler. If we // pass the main JavaScript context object we're embedded in, then the // NamedPropertyHandler will store a reference to it forever and keep it // from getting gc'd. Local CreateDataWrapper() { HandleScope scope(node_isolate); Local ctor = PersistentToLocal(node_isolate, data_wrapper_ctor); Local wrapper = ctor->NewInstance(); NODE_WRAP(wrapper, this); return scope.Close(wrapper); } Local CreateV8Context() { HandleScope scope(node_isolate); Local function_template = FunctionTemplate::New(); function_template->SetHiddenPrototype(true); Local sandbox = PersistentToLocal(node_isolate, sandbox_); function_template->SetClassName(sandbox->GetConstructorName()); Local object_template = function_template->InstanceTemplate(); object_template->SetNamedPropertyHandler(GlobalPropertyGetterCallback, GlobalPropertySetterCallback, GlobalPropertyQueryCallback, GlobalPropertyDeleterCallback, GlobalPropertyEnumeratorCallback, CreateDataWrapper()); object_template->SetAccessCheckCallbacks(GlobalPropertyNamedAccessCheck, GlobalPropertyIndexedAccessCheck); return scope.Close(Context::New(node_isolate, NULL, object_template)); } static void Init(Local target) { HandleScope scope(node_isolate); Local function_template = FunctionTemplate::New(); function_template->InstanceTemplate()->SetInternalFieldCount(1); data_wrapper_ctor.Reset(node_isolate, function_template->GetFunction()); NODE_SET_METHOD(target, "makeContext", MakeContext); } static void MakeContext(const FunctionCallbackInfo& args) { HandleScope scope(node_isolate); if (!args[0]->IsObject()) { return ThrowTypeError("sandbox argument must be an object."); } Local sandbox = args[0].As(); ContextifyContext* context = new ContextifyContext(sandbox); Local hidden_context = External::New(context); Local hidden_name = FIXED_ONE_BYTE_STRING(node_isolate, "_contextifyHidden"); sandbox->SetHiddenValue(hidden_name, hidden_context); } static void SandboxFreeCallback(Isolate* isolate, Persistent* target, ContextifyContext* context) { delete context; } static ContextifyContext* ContextFromContextifiedSandbox( const Local& sandbox) { Local hidden_name = FIXED_ONE_BYTE_STRING(node_isolate, "_contextifyHidden"); Local context_external_v = sandbox->GetHiddenValue(hidden_name); if (context_external_v.IsEmpty() || !context_external_v->IsExternal()) { return NULL; } Local context_external = context_external_v.As(); return static_cast(context_external->Value()); } static Local V8ContextFromContextifiedSandbox( const Local& sandbox) { ContextifyContext* contextify_context = ContextFromContextifiedSandbox(sandbox); if (contextify_context == NULL) { ThrowTypeError("sandbox argument must have been converted to a context."); return Local(); } return PersistentToLocal(node_isolate, contextify_context->context_); } static bool GlobalPropertyNamedAccessCheck(Local host, Local key, AccessType type, Local data) { return true; } static bool GlobalPropertyIndexedAccessCheck(Local host, uint32_t key, AccessType type, Local data) { return true; } static void GlobalPropertyGetterCallback( Local property, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); Local data = args.Data()->ToObject(); ContextifyContext* ctx = ObjectWrap::Unwrap(data); Local sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); Local rv = sandbox->GetRealNamedProperty(property); if (rv.IsEmpty()) { Local proxy_global = PersistentToLocal(node_isolate, ctx->proxy_global_); rv = proxy_global->GetRealNamedProperty(property); } if (!rv.IsEmpty() && rv == ctx->sandbox_) { rv = PersistentToLocal(node_isolate, ctx->proxy_global_); } args.GetReturnValue().Set(rv); } static void GlobalPropertySetterCallback( Local property, Local value, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); Local data = args.Data()->ToObject(); ContextifyContext* ctx = ObjectWrap::Unwrap(data); PersistentToLocal(node_isolate, ctx->sandbox_)->Set(property, value); } static void GlobalPropertyQueryCallback( Local property, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); Local data = args.Data()->ToObject(); ContextifyContext* ctx = ObjectWrap::Unwrap(data); Local sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); Local proxy_global = PersistentToLocal(node_isolate, ctx->proxy_global_); bool in_sandbox = sandbox->GetRealNamedProperty(property).IsEmpty(); bool in_proxy_global = proxy_global->GetRealNamedProperty(property).IsEmpty(); if (!in_sandbox || !in_proxy_global) { args.GetReturnValue().Set(None); } } static void GlobalPropertyDeleterCallback( Local property, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); Local data = args.Data()->ToObject(); ContextifyContext* ctx = ObjectWrap::Unwrap(data); bool success = PersistentToLocal(node_isolate, ctx->sandbox_)->Delete(property); if (!success) { success = PersistentToLocal(node_isolate, ctx->proxy_global_)->Delete(property); } args.GetReturnValue().Set(success); } static void GlobalPropertyEnumeratorCallback( const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); Local data = args.Data()->ToObject(); ContextifyContext* ctx = ObjectWrap::Unwrap(data); Local sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); args.GetReturnValue().Set(sandbox->GetPropertyNames()); } }; class ContextifyScript : ObjectWrap { private: Persistent