diff options
-rw-r--r-- | node.gyp | 1 | ||||
-rw-r--r-- | src/node_contextify.cc | 907 | ||||
-rw-r--r-- | src/node_contextify.h | 100 |
3 files changed, 546 insertions, 462 deletions
@@ -344,6 +344,7 @@ 'src/node.h', 'src/node_buffer.h', 'src/node_constants.h', + 'src/node_contextify.h', 'src/node_debug_options.h', 'src/node_http2.h', 'src/node_http2_state.h', diff --git a/src/node_contextify.cc b/src/node_contextify.cc index d50c6023dc..6fe9276d82 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -23,8 +23,10 @@ #include "node_watchdog.h" #include "base_object-inl.h" #include "v8-debug.h" +#include "node_contextify.h" namespace node { +namespace contextify { using v8::Array; using v8::ArrayBuffer; @@ -92,550 +94,531 @@ Local<Name> Uint32ToName(Local<Context> context, uint32_t index) { .ToLocalChecked(); } -class ContextifyContext { - protected: - // V8 reserves the first field in context objects for the debugger. We use the - // second field to hold a reference to the sandbox object. - enum { kSandboxObjectIndex = 1 }; - - Environment* const env_; - Persistent<Context> context_; - - public: - ContextifyContext(Environment* env, - Local<Object> sandbox_obj, - Local<Object> options_obj) - : env_(env) { - Local<Context> v8_context = CreateV8Context(env, sandbox_obj, options_obj); - context_.Reset(env->isolate(), v8_context); - - // Allocation failure or maximum call stack size reached - if (context_.IsEmpty()) - return; - context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); - context_.MarkIndependent(); - } - - - ~ContextifyContext() { - context_.Reset(); - } - +} // anonymous namespace - inline Environment* env() const { - return env_; - } +ContextifyContext::ContextifyContext( + Environment* env, + Local<Object> sandbox_obj, Local<Object> options_obj) : env_(env) { + Local<Context> v8_context = CreateV8Context(env, sandbox_obj, options_obj); + context_.Reset(env->isolate(), v8_context); + + // Allocation failure or maximum call stack size reached + if (context_.IsEmpty()) + return; + context_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); + context_.MarkIndependent(); +} - inline Local<Context> context() const { - return PersistentToLocal(env()->isolate(), context_); - } +ContextifyContext::~ContextifyContext() { + context_.Reset(); +} - inline Local<Object> global_proxy() const { - return context()->Global(); - } +// 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<Value> ContextifyContext::CreateDataWrapper(Environment* env) { + EscapableHandleScope scope(env->isolate()); + Local<Object> wrapper = + env->script_data_constructor_function() + ->NewInstance(env->context()).FromMaybe(Local<Object>()); + if (wrapper.IsEmpty()) + return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>())); + + Wrap(wrapper, this); + return scope.Escape(wrapper); +} - inline Local<Object> sandbox() const { - return Local<Object>::Cast(context()->GetEmbedderData(kSandboxObjectIndex)); +Local<Context> ContextifyContext::CreateV8Context( + Environment* env, + Local<Object> sandbox_obj, + Local<Object> options_obj) { + EscapableHandleScope scope(env->isolate()); + Local<FunctionTemplate> function_template = + FunctionTemplate::New(env->isolate()); + + function_template->SetClassName(sandbox_obj->GetConstructorName()); + + Local<ObjectTemplate> object_template = + function_template->InstanceTemplate(); + + NamedPropertyHandlerConfiguration config(PropertyGetterCallback, + PropertySetterCallback, + PropertyDescriptorCallback, + PropertyDeleterCallback, + PropertyEnumeratorCallback, + PropertyDefinerCallback, + CreateDataWrapper(env)); + + IndexedPropertyHandlerConfiguration indexed_config( + IndexedPropertyGetterCallback, + IndexedPropertySetterCallback, + IndexedPropertyDescriptorCallback, + IndexedPropertyDeleterCallback, + PropertyEnumeratorCallback, + IndexedPropertyDefinerCallback, + CreateDataWrapper(env)); + + object_template->SetHandler(config); + object_template->SetHandler(indexed_config); + + Local<Context> ctx = NewContext(env->isolate(), object_template); + + if (ctx.IsEmpty()) { + env->ThrowError("Could not instantiate context"); + return Local<Context>(); } - // 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<Value> CreateDataWrapper(Environment* env) { - EscapableHandleScope scope(env->isolate()); - Local<Object> wrapper = - env->script_data_constructor_function() - ->NewInstance(env->context()).FromMaybe(Local<Object>()); - if (wrapper.IsEmpty()) - return scope.Escape(Local<Value>::New(env->isolate(), Local<Value>())); - - Wrap(wrapper, this); - return scope.Escape(wrapper); + ctx->SetSecurityToken(env->context()->GetSecurityToken()); + + // We need to tie the lifetime of the sandbox object with the lifetime of + // newly created context. We do this by making them hold references to each + // other. The context can directly hold a reference to the sandbox as an + // embedder data field. However, we cannot hold a reference to a v8::Context + // directly in an Object, we instead hold onto the new context's global + // object instead (which then has a reference to the context). + ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj); + sandbox_obj->SetPrivate(env->context(), + env->contextify_global_private_symbol(), + ctx->Global()); + + Local<Value> name = + options_obj->Get(env->context(), env->name_string()) + .ToLocalChecked(); + CHECK(name->IsString()); + Utf8Value name_val(env->isolate(), name); + + ContextInfo info(*name_val); + + Local<Value> origin = + options_obj->Get(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "origin")) + .ToLocalChecked(); + if (!origin->IsUndefined()) { + CHECK(origin->IsString()); + Utf8Value origin_val(env->isolate(), origin); + info.origin = *origin_val; } + env->AssignToContext(ctx, info); - Local<Context> CreateV8Context(Environment* env, - Local<Object> sandbox_obj, - Local<Object> options_obj) { - EscapableHandleScope scope(env->isolate()); - Local<FunctionTemplate> function_template = - FunctionTemplate::New(env->isolate()); - - function_template->SetClassName(sandbox_obj->GetConstructorName()); - - Local<ObjectTemplate> object_template = - function_template->InstanceTemplate(); - - NamedPropertyHandlerConfiguration config(PropertyGetterCallback, - PropertySetterCallback, - PropertyDescriptorCallback, - PropertyDeleterCallback, - PropertyEnumeratorCallback, - PropertyDefinerCallback, - CreateDataWrapper(env)); - - IndexedPropertyHandlerConfiguration indexed_config( - IndexedPropertyGetterCallback, - IndexedPropertySetterCallback, - IndexedPropertyDescriptorCallback, - IndexedPropertyDeleterCallback, - PropertyEnumeratorCallback, - IndexedPropertyDefinerCallback, - CreateDataWrapper(env)); - - object_template->SetHandler(config); - object_template->SetHandler(indexed_config); + return scope.Escape(ctx); +} - Local<Context> ctx = NewContext(env->isolate(), object_template); - if (ctx.IsEmpty()) { - env->ThrowError("Could not instantiate context"); - return Local<Context>(); - } +void ContextifyContext::Init(Environment* env, Local<Object> target) { + Local<FunctionTemplate> function_template = + FunctionTemplate::New(env->isolate()); + function_template->InstanceTemplate()->SetInternalFieldCount(1); + env->set_script_data_constructor_function(function_template->GetFunction()); - ctx->SetSecurityToken(env->context()->GetSecurityToken()); - - // We need to tie the lifetime of the sandbox object with the lifetime of - // newly created context. We do this by making them hold references to each - // other. The context can directly hold a reference to the sandbox as an - // embedder data field. However, we cannot hold a reference to a v8::Context - // directly in an Object, we instead hold onto the new context's global - // object instead (which then has a reference to the context). - ctx->SetEmbedderData(kSandboxObjectIndex, sandbox_obj); - sandbox_obj->SetPrivate(env->context(), - env->contextify_global_private_symbol(), - ctx->Global()); - - Local<Value> name = - options_obj->Get(env->context(), env->name_string()) - .ToLocalChecked(); - CHECK(name->IsString()); - Utf8Value name_val(env->isolate(), name); - - ContextInfo info(*name_val); - - Local<Value> origin = - options_obj->Get(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "origin")) - .ToLocalChecked(); - if (!origin->IsUndefined()) { - CHECK(origin->IsString()); - Utf8Value origin_val(env->isolate(), origin); - info.origin = *origin_val; - } + env->SetMethod(target, "runInDebugContext", RunInDebugContext); + env->SetMethod(target, "makeContext", MakeContext); + env->SetMethod(target, "isContext", IsContext); +} - env->AssignToContext(ctx, info); - return scope.Escape(ctx); +void ContextifyContext::RunInDebugContext( + const FunctionCallbackInfo<Value>& args) { + Local<String> script_source(args[0]->ToString(args.GetIsolate())); + if (script_source.IsEmpty()) + return; // Exception pending. + Local<Context> debug_context = Debug::GetDebugContext(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args); + if (debug_context.IsEmpty()) { + // Force-load the debug context. + auto dummy_event_listener = [] (const Debug::EventDetails&) {}; + Debug::SetDebugEventListener(args.GetIsolate(), dummy_event_listener); + debug_context = Debug::GetDebugContext(args.GetIsolate()); + CHECK(!debug_context.IsEmpty()); + // Ensure that the debug context has an Environment assigned in case + // a fatal error is raised. The fatal exception handler in node.cc + // is not equipped to deal with contexts that don't have one and + // can't easily be taught that due to a deficiency in the V8 API: + // there is no way for the embedder to tell if the data index is + // in use. + const int index = Environment::kContextEmbedderDataIndex; + debug_context->SetAlignedPointerInEmbedderData(index, env); } - - static void Init(Environment* env, Local<Object> target) { - Local<FunctionTemplate> function_template = - FunctionTemplate::New(env->isolate()); - function_template->InstanceTemplate()->SetInternalFieldCount(1); - env->set_script_data_constructor_function(function_template->GetFunction()); - - env->SetMethod(target, "runInDebugContext", RunInDebugContext); - env->SetMethod(target, "makeContext", MakeContext); - env->SetMethod(target, "isContext", IsContext); - } + Context::Scope context_scope(debug_context); + MaybeLocal<Script> script = Script::Compile(debug_context, script_source); + if (script.IsEmpty()) + return; // Exception pending. + args.GetReturnValue().Set(script.ToLocalChecked()->Run()); +} - static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) { - Local<String> script_source(args[0]->ToString(args.GetIsolate())); - if (script_source.IsEmpty()) - return; // Exception pending. - Local<Context> debug_context = Debug::GetDebugContext(args.GetIsolate()); - Environment* env = Environment::GetCurrent(args); - if (debug_context.IsEmpty()) { - // Force-load the debug context. - auto dummy_event_listener = [] (const Debug::EventDetails&) {}; - Debug::SetDebugEventListener(args.GetIsolate(), dummy_event_listener); - debug_context = Debug::GetDebugContext(args.GetIsolate()); - CHECK(!debug_context.IsEmpty()); - // Ensure that the debug context has an Environment assigned in case - // a fatal error is raised. The fatal exception handler in node.cc - // is not equipped to deal with contexts that don't have one and - // can't easily be taught that due to a deficiency in the V8 API: - // there is no way for the embedder to tell if the data index is - // in use. - const int index = Environment::kContextEmbedderDataIndex; - debug_context->SetAlignedPointerInEmbedderData(index, env); - } +void ContextifyContext::MakeContext(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); - Context::Scope context_scope(debug_context); - MaybeLocal<Script> script = Script::Compile(debug_context, script_source); - if (script.IsEmpty()) - return; // Exception pending. - args.GetReturnValue().Set(script.ToLocalChecked()->Run()); + if (!args[0]->IsObject()) { + return env->ThrowTypeError("sandbox argument must be an object."); } + Local<Object> sandbox = args[0].As<Object>(); + // Don't allow contextifying a sandbox multiple times. + CHECK( + !sandbox->HasPrivate( + env->context(), + env->contextify_context_private_symbol()).FromJust()); - static void MakeContext(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); + Local<Object> options = args[1].As<Object>(); + CHECK(options->IsObject()); - if (!args[0]->IsObject()) { - return env->ThrowTypeError("sandbox argument must be an object."); - } - Local<Object> sandbox = args[0].As<Object>(); + TryCatch try_catch(env->isolate()); + ContextifyContext* context = new ContextifyContext(env, sandbox, options); - // Don't allow contextifying a sandbox multiple times. - CHECK( - !sandbox->HasPrivate( - env->context(), - env->contextify_context_private_symbol()).FromJust()); + if (try_catch.HasCaught()) { + try_catch.ReThrow(); + return; + } - Local<Object> options = args[1].As<Object>(); - CHECK(options->IsObject()); + if (context->context().IsEmpty()) + return; - TryCatch try_catch(env->isolate()); - ContextifyContext* context = new ContextifyContext(env, sandbox, options); + sandbox->SetPrivate( + env->context(), + env->contextify_context_private_symbol(), + External::New(env->isolate(), context)); +} - if (try_catch.HasCaught()) { - try_catch.ReThrow(); - return; - } - if (context->context().IsEmpty()) - return; +void ContextifyContext::IsContext(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); - sandbox->SetPrivate( - env->context(), - env->contextify_context_private_symbol(), - External::New(env->isolate(), context)); + if (!args[0]->IsObject()) { + env->ThrowTypeError("sandbox must be an object"); + return; } + Local<Object> sandbox = args[0].As<Object>(); + Maybe<bool> result = + sandbox->HasPrivate(env->context(), + env->contextify_context_private_symbol()); + args.GetReturnValue().Set(result.FromJust()); +} - static void IsContext(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsObject()) { - env->ThrowTypeError("sandbox must be an object"); - return; - } - Local<Object> sandbox = args[0].As<Object>(); +void ContextifyContext::WeakCallback( + const WeakCallbackInfo<ContextifyContext>& data) { + ContextifyContext* context = data.GetParameter(); + delete context; +} - Maybe<bool> result = - sandbox->HasPrivate(env->context(), - env->contextify_context_private_symbol()); - args.GetReturnValue().Set(result.FromJust()); +// static +ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox( + Environment* env, + const Local<Object>& sandbox) { + MaybeLocal<Value> maybe_value = + sandbox->GetPrivate(env->context(), + env->contextify_context_private_symbol()); + Local<Value> context_external_v; + if (maybe_value.ToLocal(&context_external_v) && + context_external_v->IsExternal()) { + Local<External> context_external = context_external_v.As<External>(); + return static_cast<ContextifyContext*>(context_external->Value()); } + return nullptr; +} - - static void WeakCallback(const WeakCallbackInfo<ContextifyContext>& data) { - ContextifyContext* context = data.GetParameter(); - delete context; +// static +void ContextifyContext::PropertyGetterCallback( + Local<Name> property, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); + + // Still initializing + if (ctx->context_.IsEmpty()) + return; + + Local<Context> context = ctx->context(); + Local<Object> sandbox = ctx->sandbox(); + MaybeLocal<Value> maybe_rv = + sandbox->GetRealNamedProperty(context, property); + if (maybe_rv.IsEmpty()) { + maybe_rv = + ctx->global_proxy()->GetRealNamedProperty(context, property); } + Local<Value> rv; + if (maybe_rv.ToLocal(&rv)) { + if (rv == sandbox) + rv = ctx->global_proxy(); - static ContextifyContext* ContextFromContextifiedSandbox( - Environment* env, - const Local<Object>& sandbox) { - MaybeLocal<Value> maybe_value = - sandbox->GetPrivate(env->context(), - env->contextify_context_private_symbol()); - Local<Value> context_external_v; - if (maybe_value.ToLocal(&context_external_v) && - context_external_v->IsExternal()) { - Local<External> context_external = context_external_v.As<External>(); - return static_cast<ContextifyContext*>(context_external->Value()); - } - return nullptr; + args.GetReturnValue().Set(rv); } +} - - static void PropertyGetterCallback( - Local<Name> property, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - - // Still initializing - if (ctx->context_.IsEmpty()) - return; - - Local<Context> context = ctx->context(); - Local<Object> sandbox = ctx->sandbox(); - MaybeLocal<Value> maybe_rv = - sandbox->GetRealNamedProperty(context, property); - if (maybe_rv.IsEmpty()) { - maybe_rv = - ctx->global_proxy()->GetRealNamedProperty(context, property); - } - - Local<Value> rv; - if (maybe_rv.ToLocal(&rv)) { - if (rv == sandbox) - rv = ctx->global_proxy(); - - args.GetReturnValue().Set(rv); - } - } - - - static void PropertySetterCallback( - Local<Name> property, - Local<Value> value, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - - // Still initializing - if (ctx->context_.IsEmpty()) - return; - - auto attributes = PropertyAttribute::None; - bool is_declared_on_global_proxy = ctx->global_proxy() - ->GetRealNamedPropertyAttributes(ctx->context(), property) - .To(&attributes); - bool read_only = - static_cast<int>(attributes) & - static_cast<int>(PropertyAttribute::ReadOnly); - - bool is_declared_on_sandbox = ctx->sandbox() - ->GetRealNamedPropertyAttributes(ctx->context(), property) - .To(&attributes); - read_only = read_only || - (static_cast<int>(attributes) & - static_cast<int>(PropertyAttribute::ReadOnly)); - - if (read_only) - return; - - // true for x = 5 - // false for this.x = 5 - // false for Object.defineProperty(this, 'foo', ...) - // false for vmResult.x = 5 where vmResult = vm.runInContext(); - bool is_contextual_store = ctx->global_proxy() != args.This(); - - // Indicator to not return before setting (undeclared) function declarations - // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true. - // True for 'function f() {}', 'this.f = function() {}', - // 'var f = function()'. - // In effect only for 'function f() {}' because - // var f = function(), is_declared = true - // this.f = function() {}, is_contextual_store = false. - bool is_function = value->IsFunction(); - - bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox; - if (!is_declared && args.ShouldThrowOnError() && is_contextual_store && - !is_function) - return; - - if (!is_declared_on_global_proxy && is_declared_on_sandbox && - args.ShouldThrowOnError() && is_contextual_store && !is_function) { - // The property exists on the sandbox but not on the global - // proxy. Setting it would throw because we are in strict mode. - // Don't attempt to set it by signaling that the call was - // intercepted. Only change the value on the sandbox. - args.GetReturnValue().Set(false); - } - - ctx->sandbox()->Set(property, value); +// static +void ContextifyContext::PropertySetterCallback( + Local<Name> property, + Local<Value> value, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); + + // Still initializing + if (ctx->context_.IsEmpty()) + return; + + auto attributes = PropertyAttribute::None; + bool is_declared_on_global_proxy = ctx->global_proxy() + ->GetRealNamedPropertyAttributes(ctx->context(), property) + .To(&attributes); + bool read_only = + static_cast<int>(attributes) & + static_cast<int>(PropertyAttribute::ReadOnly); + + bool is_declared_on_sandbox = ctx->sandbox() + ->GetRealNamedPropertyAttributes(ctx->context(), property) + .To(&attributes); + read_only = read_only || + (static_cast<int>(attributes) & + static_cast<int>(PropertyAttribute::ReadOnly)); + + if (read_only) + return; + + // true for x = 5 + // false for this.x = 5 + // false for Object.defineProperty(this, 'foo', ...) + // false for vmResult.x = 5 where vmResult = vm.runInContext(); + bool is_contextual_store = ctx->global_proxy() != args.This(); + + // Indicator to not return before setting (undeclared) function declarations + // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true. + // True for 'function f() {}', 'this.f = function() {}', + // 'var f = function()'. + // In effect only for 'function f() {}' because + // var f = function(), is_declared = true + // this.f = function() {}, is_contextual_store = false. + bool is_function = value->IsFunction(); + + bool is_declared = is_declared_on_global_proxy || is_declared_on_sandbox; + if (!is_declared && args.ShouldThrowOnError() && is_contextual_store && + !is_function) + return; + + if (!is_declared_on_global_proxy && is_declared_on_sandbox && + args.ShouldThrowOnError() && is_contextual_store && !is_function) { + // The property exists on the sandbox but not on the global + // proxy. Setting it would throw because we are in strict mode. + // Don't attempt to set it by signaling that the call was + // intercepted. Only change the value on the sandbox. + args.GetReturnValue().Set(false); } + ctx->sandbox()->Set(property, value); +} - static void PropertyDescriptorCallback( - Local<Name> property, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); +// static +void ContextifyContext::PropertyDescriptorCallback( + Local<Name> property, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - // Still initializing - if (ctx->context_.IsEmpty()) - return; + // Still initializing + if (ctx->context_.IsEmpty()) + return; - Local<Context> context = ctx->context(); + Local<Context> context = ctx->context(); - Local<Object> sandbox = ctx->sandbox(); + Local<Object> sandbox = ctx->sandbox(); - if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) { - args.GetReturnValue().Set( - sandbox->GetOwnPropertyDescriptor(context, property) - .ToLocalChecked()); - } + if (sandbox->HasOwnProperty(context, property).FromMaybe(false)) { + args.GetReturnValue().Set( + sandbox->GetOwnPropertyDescriptor(context, property) + .ToLocalChecked()); } +} - - static void PropertyDefinerCallback( - Local<Name> property, - const PropertyDescriptor& desc, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - - // Still initializing - if (ctx->context_.IsEmpty()) - return; - - Local<Context> context = ctx->context(); - v8::Isolate* isolate = context->GetIsolate(); - - auto attributes = PropertyAttribute::None; - bool is_declared = - ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(), - property) - .To(&attributes); - bool read_only = - static_cast<int>(attributes) & - static_cast<int>(PropertyAttribute::ReadOnly); - - // If the property is set on the global as read_only, don't change it on - // the global or sandbox. - if (is_declared && read_only) - return; - - Local<Object> sandbox = ctx->sandbox(); - - auto define_prop_on_sandbox = - [&] (PropertyDescriptor* desc_for_sandbox) { - if (desc.has_enumerable()) { - desc_for_sandbox->set_enumerable(desc.enumerable()); - } - if (desc.has_configurable()) { - desc_for_sandbox->set_configurable(desc.configurable()); - } - // Set the property on the sandbox. - sandbox->DefineProperty(context, property, *desc_for_sandbox) - .FromJust(); - }; - - if (desc.has_get() || desc.has_set()) { - PropertyDescriptor desc_for_sandbox( - desc.has_get() ? desc.get() : v8::Undefined(isolate).As<Value>(), - desc.has_set() ? desc.set() : v8::Undefined(isolate).As<Value>()); - +// static +void ContextifyContext::PropertyDefinerCallback( + Local<Name> property, + const PropertyDescriptor& desc, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); + + // Still initializing + if (ctx->context_.IsEmpty()) + return; + + Local<Context> context = ctx->context(); + v8::Isolate* isolate = context->GetIsolate(); + + auto attributes = PropertyAttribute::None; + bool is_declared = + ctx->global_proxy()->GetRealNamedPropertyAttributes(ctx->context(), + property) + .To(&attributes); + bool read_only = + static_cast<int>(attributes) & + static_cast<int>(PropertyAttribute::ReadOnly); + + // If the property is set on the global as read_only, don't change it on + // the global or sandbox. + if (is_declared && read_only) + return; + + Local<Object> sandbox = ctx->sandbox(); + + auto define_prop_on_sandbox = + [&] (PropertyDescriptor* desc_for_sandbox) { + if (desc.has_enumerable()) { + desc_for_sandbox->set_enumerable(desc.enumerable()); + } + if (desc.has_configurable()) { + desc_for_sandbox->set_configurable(desc.configurable()); + } + // Set the property on the sandbox. + sandbox->DefineProperty(context, property, *desc_for_sandbox) + .FromJust(); + }; + + if (desc.has_get() || desc.has_set()) { + PropertyDescriptor desc_for_sandbox( + desc.has_get() ? desc.get() : v8::Undefined(isolate).As<Value>(), + desc.has_set() ? desc.set() : v8::Undefined(isolate).As<Value>()); + + define_prop_on_sandbox(&desc_for_sandbox); + } else { + Local<Value> value = + desc.has_value() ? desc.value() : v8::Undefined(isolate).As<Value>(); + + if (desc.has_writable()) { + PropertyDescriptor desc_for_sandbox(value, desc.writable()); define_prop_on_sandbox(&desc_for_sandbox); } else { - Local<Value> value = - desc.has_value() ? desc.value() : v8::Undefined(isolate).As<Value>(); - - if (desc.has_writable()) { - PropertyDescriptor desc_for_sandbox(value, desc.writable()); - define_prop_on_sandbox(&desc_for_sandbox); - } else { - PropertyDescriptor desc_for_sandbox(value); - define_prop_on_sandbox(&desc_for_sandbox); - } + PropertyDescriptor desc_for_sandbox(value); + define_prop_on_sandbox(&desc_for_sandbox); } } +} - static void PropertyDeleterCallback( - Local<Name> property, - const PropertyCallbackInfo<Boolean>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - - // Still initializing - if (ctx->context_.IsEmpty()) - return; +// static +void ContextifyContext::PropertyDeleterCallback( + Local<Name> property, + const PropertyCallbackInfo<Boolean>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property); + // Still initializing + if (ctx->context_.IsEmpty()) + return; - if (success.FromMaybe(false)) - return; + Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), property); - // Delete failed on the sandbox, intercept and do not delete on - // the global object. - args.GetReturnValue().Set(false); - } + if (success.FromMaybe(false)) + return; + // Delete failed on the sandbox, intercept and do not delete on + // the global object. + args.GetReturnValue().Set(false); +} - static void PropertyEnumeratorCallback( - const PropertyCallbackInfo<Array>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); +// static +void ContextifyContext::PropertyEnumeratorCallback( + const PropertyCallbackInfo<Array>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - // Still initializing - if (ctx->context_.IsEmpty()) - return; + // Still initializing + if (ctx->context_.IsEmpty()) + return; - args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames()); - } + args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames()); +} - static void IndexedPropertyGetterCallback( - uint32_t index, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); +// static +void ContextifyContext::IndexedPropertyGetterCallback( + uint32_t index, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - // Still initializing - if (ctx->context_.IsEmpty()) - return; - - PropertyGetterCallback(Uint32ToName(ctx->context(), index), args); - } + // Still initializing + if (ctx->context_.IsEmpty()) + return; + ContextifyContext::PropertyGetterCallback( + Uint32ToName(ctx->context(), index), args); +} - static void IndexedPropertySetterCallback( - uint32_t index, - Local<Value> value, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - // Still initializing - if (ctx->context_.IsEmpty()) - return; +void ContextifyContext::IndexedPropertySetterCallback( + uint32_t index, + Local<Value> value, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - PropertySetterCallback(Uint32ToName(ctx->context(), index), value, args); - } + // Still initializing + if (ctx->context_.IsEmpty()) + return; + ContextifyContext::PropertySetterCallback( + Uint32ToName(ctx->context(), index), value, args); +} - static void IndexedPropertyDescriptorCallback( - uint32_t index, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); +// static +void ContextifyContext::IndexedPropertyDescriptorCallback( + uint32_t index, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - // Still initializing - if (ctx->context_.IsEmpty()) - return; + // Still initializing + if (ctx->context_.IsEmpty()) + return; - PropertyDescriptorCallback(Uint32ToName(ctx->context(), index), args); - } + ContextifyContext::PropertyDescriptorCallback( + Uint32ToName(ctx->context(), index), args); +} - static void IndexedPropertyDefinerCallback( - uint32_t index, - const PropertyDescriptor& desc, - const PropertyCallbackInfo<Value>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); +void ContextifyContext::IndexedPropertyDefinerCallback( + uint32_t index, + const PropertyDescriptor& desc, + const PropertyCallbackInfo<Value>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - // Still initializing - if (ctx->context_.IsEmpty()) - return; + // Still initializing + if (ctx->context_.IsEmpty()) + return; - PropertyDefinerCallback(Uint32ToName(ctx->context(), index), desc, args); - } + ContextifyContext::PropertyDefinerCallback( + Uint32ToName(ctx->context(), index), desc, args); +} - static void IndexedPropertyDeleterCallback( - uint32_t index, - const PropertyCallbackInfo<Boolean>& args) { - ContextifyContext* ctx; - ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); +// static +void ContextifyContext::IndexedPropertyDeleterCallback( + uint32_t index, + const PropertyCallbackInfo<Boolean>& args) { + ContextifyContext* ctx; + ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Data().As<Object>()); - // Still initializing - if (ctx->context_.IsEmpty()) - return; + // Still initializing + if (ctx->context_.IsEmpty()) + return; - Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), index); + Maybe<bool> success = ctx->sandbox()->Delete(ctx->context(), index); - if (success.FromMaybe(false)) - return; + if (success.FromMaybe(false)) + return; - // Delete failed on the sandbox, intercept and do not delete on - // the global object. - args.GetReturnValue().Set(false); - } -}; + // Delete failed on the sandbox, intercept and do not delete on + // the global object. + args.GetReturnValue().Set(false); +} class ContextifyScript : public BaseObject { private: @@ -1194,7 +1177,7 @@ void InitContextify(Local<Object> target, ContextifyScript::Init(env, target); } -} // anonymous namespace +} // namespace contextify } // namespace node -NODE_BUILTIN_MODULE_CONTEXT_AWARE(contextify, node::InitContextify) +NODE_BUILTIN_MODULE_CONTEXT_AWARE(contextify, node::contextify::InitContextify) diff --git a/src/node_contextify.h b/src/node_contextify.h new file mode 100644 index 0000000000..9705c981a7 --- /dev/null +++ b/src/node_contextify.h @@ -0,0 +1,100 @@ +#ifndef SRC_NODE_CONTEXTIFY_H_ +#define SRC_NODE_CONTEXTIFY_H_ + +#include "node_internals.h" +#include "node_watchdog.h" +#include "base_object-inl.h" + +namespace node { +namespace contextify { + +class ContextifyContext { + protected: + // V8 reserves the first field in context objects for the debugger. We use the + // second field to hold a reference to the sandbox object. + enum { kSandboxObjectIndex = 1 }; + + Environment* const env_; + v8::Persistent<v8::Context> context_; + + public: + ContextifyContext(Environment* env, + v8::Local<v8::Object> sandbox_obj, + v8::Local<v8::Object> options_obj); + ~ContextifyContext(); + + v8::Local<v8::Value> CreateDataWrapper(Environment* env); + v8::Local<v8::Context> CreateV8Context(Environment* env, + v8::Local<v8::Object> sandbox_obj, v8::Local<v8::Object> options_obj); + static void Init(Environment* env, v8::Local<v8::Object> target); + + static ContextifyContext* ContextFromContextifiedSandbox( + Environment* env, + const v8::Local<v8::Object>& sandbox); + + inline Environment* env() const { + return env_; + } + + inline v8::Local<v8::Context> context() const { + return PersistentToLocal(env()->isolate(), context_); + } + + inline v8::Local<v8::Object> global_proxy() const { + return context()->Global(); + } + + inline v8::Local<v8::Object> sandbox() const { + return v8::Local<v8::Object>::Cast( + context()->GetEmbedderData(kSandboxObjectIndex)); + } + + private: + static void RunInDebugContext( + const v8::FunctionCallbackInfo<v8::Value>& args); + static void MakeContext(const v8::FunctionCallbackInfo<v8::Value>& args); + static void IsContext(const v8::FunctionCallbackInfo<v8::Value>& args); + static void WeakCallback( + const v8::WeakCallbackInfo<ContextifyContext>& data); + static void PropertyGetterCallback( + v8::Local<v8::Name> property, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void PropertySetterCallback( + v8::Local<v8::Name> property, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void PropertyDescriptorCallback( + v8::Local<v8::Name> property, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void PropertyDefinerCallback( + v8::Local<v8::Name> property, + const v8::PropertyDescriptor& desc, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void PropertyDeleterCallback( + v8::Local<v8::Name> property, + const v8::PropertyCallbackInfo<v8::Boolean>& args); + static void PropertyEnumeratorCallback( + const v8::PropertyCallbackInfo<v8::Array>& args); + static void IndexedPropertyGetterCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void IndexedPropertySetterCallback( + uint32_t index, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void IndexedPropertyDescriptorCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void IndexedPropertyDefinerCallback( + uint32_t index, + const v8::PropertyDescriptor& desc, + const v8::PropertyCallbackInfo<v8::Value>& args); + static void IndexedPropertyDeleterCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Boolean>& args); +}; + +} // namespace contextify +} // namespace node + +#endif // SRC_NODE_CONTEXTIFY_H_ |