diff --git a/src/env.cc b/src/env.cc index c0c9f17cdc71aa..9bc9346e653fe1 100644 --- a/src/env.cc +++ b/src/env.cc @@ -6,6 +6,7 @@ #include "memory_tracker-inl.h" #include "node_buffer.h" #include "node_context_data.h" +#include "node_contextify.h" #include "node_errors.h" #include "node_internals.h" #include "node_options-inl.h" @@ -444,6 +445,8 @@ void IsolateData::CreateProperties() { #undef V // TODO(legendecas): eagerly create per isolate templates. + set_contextify_global_template( + contextify::ContextifyContext::CreateGlobalTemplate(isolate_)); } IsolateData::IsolateData(Isolate* isolate, @@ -512,13 +515,19 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() { void Environment::AssignToContext(Local context, const ContextInfo& info) { - ContextEmbedderTag::TagNodeContext(context); context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment, this); // Used to retrieve bindings context->SetAlignedPointerInEmbedderData( ContextEmbedderIndex::kBindingListIndex, &(this->bindings_)); + // ContextifyContexts will update this to a pointer to the native object. + context->SetAlignedPointerInEmbedderData( + ContextEmbedderIndex::kContextifyContext, nullptr); + + // This must not be done before other context fields are initialized. + ContextEmbedderTag::TagNodeContext(context); + #if HAVE_INSPECTOR inspector_agent()->ContextCreated(context, info); #endif // HAVE_INSPECTOR diff --git a/src/env.h b/src/env.h index 42c5ef888b2f49..ef5850ee3b3aef 100644 --- a/src/env.h +++ b/src/env.h @@ -349,6 +349,7 @@ class NoArrayBufferZeroFillScope { V(nistcurve_string, "nistCurve") \ V(node_string, "node") \ V(nsname_string, "nsname") \ + V(object_string, "Object") \ V(ocsp_request_string, "OCSPRequest") \ V(oncertcb_string, "oncertcb") \ V(onchange_string, "onchange") \ @@ -477,6 +478,7 @@ class NoArrayBufferZeroFillScope { V(binding_data_ctor_template, v8::FunctionTemplate) \ V(blob_constructor_template, v8::FunctionTemplate) \ V(blocklist_constructor_template, v8::FunctionTemplate) \ + V(contextify_global_template, v8::ObjectTemplate) \ V(compiled_fn_entry_template, v8::ObjectTemplate) \ V(dir_instance_template, v8::ObjectTemplate) \ V(fd_constructor_template, v8::ObjectTemplate) \ @@ -560,7 +562,6 @@ class NoArrayBufferZeroFillScope { V(primordials_safe_weak_set_prototype_object, v8::Object) \ V(promise_hook_handler, v8::Function) \ V(promise_reject_callback, v8::Function) \ - V(script_data_constructor_function, v8::Function) \ V(snapshot_serialize_callback, v8::Function) \ V(snapshot_deserialize_callback, v8::Function) \ V(snapshot_deserialize_main, v8::Function) \ diff --git a/src/node_context_data.h b/src/node_context_data.h index 632b648a0a1ef7..3fe425bdf4cff6 100644 --- a/src/node_context_data.h +++ b/src/node_context_data.h @@ -32,11 +32,15 @@ namespace node { #define NODE_CONTEXT_ALLOW_CODE_GENERATION_FROM_STRINGS_INDEX 36 #endif +#ifndef NODE_CONTEXT_CONTEXTIFY_CONTEXT_INDEX +#define NODE_CONTEXT_CONTEXTIFY_CONTEXT_INDEX 37 +#endif + // NODE_CONTEXT_TAG must be greater than any embedder indexes so that a single // check on the number of embedder data fields can assure the presence of all // embedder indexes. #ifndef NODE_CONTEXT_TAG -#define NODE_CONTEXT_TAG 37 +#define NODE_CONTEXT_TAG 38 #endif enum ContextEmbedderIndex { @@ -46,6 +50,7 @@ enum ContextEmbedderIndex { kBindingListIndex = NODE_BINDING_LIST_INDEX, kAllowCodeGenerationFromStrings = NODE_CONTEXT_ALLOW_CODE_GENERATION_FROM_STRINGS_INDEX, + kContextifyContext = NODE_CONTEXT_CONTEXTIFY_CONTEXT_INDEX, kContextTag = NODE_CONTEXT_TAG, }; diff --git a/src/node_contextify.cc b/src/node_contextify.cc index e1fc5e5513d6ab..9f166f20e3c11c 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -113,12 +113,26 @@ ContextifyContext::ContextifyContext( const ContextOptions& options) : env_(env), microtask_queue_wrap_(options.microtask_queue_wrap) { - MaybeLocal v8_context = CreateV8Context(env, sandbox_obj, options); + Local object_template = env->contextify_global_template(); + if (object_template.IsEmpty()) { + object_template = CreateGlobalTemplate(env->isolate()); + env->set_contextify_global_template(object_template); + } + + MicrotaskQueue* queue = + microtask_queue() + ? microtask_queue().get() + : env->isolate()->GetCurrentContext()->GetMicrotaskQueue(); - // Allocation failure, maximum call stack size reached, termination, etc. - if (v8_context.IsEmpty()) return; + Local v8_context; + if (!(CreateV8Context(env->isolate(), object_template, queue) + .ToLocal(&v8_context)) || + !InitializeContext(v8_context, env, sandbox_obj, options)) { + // Allocation failure, maximum call stack size reached, termination, etc. + return; + } - context_.Reset(env->isolate(), v8_context.ToLocalChecked()); + context_.Reset(env->isolate(), v8_context); context_.SetWeak(this, WeakCallback, WeakCallbackType::kParameter); env->AddCleanupHook(CleanupHook, this); } @@ -140,41 +154,13 @@ void ContextifyContext::CleanupHook(void* arg) { delete self; } - -// 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. -MaybeLocal ContextifyContext::CreateDataWrapper(Environment* env) { - Local wrapper; - if (!env->script_data_constructor_function() - ->NewInstance(env->context()) - .ToLocal(&wrapper)) { - return MaybeLocal(); - } - - wrapper->SetAlignedPointerInInternalField(ContextifyContext::kSlot, this); - return wrapper; -} - -MaybeLocal ContextifyContext::CreateV8Context( - Environment* env, - Local sandbox_obj, - const ContextOptions& options) { - EscapableHandleScope scope(env->isolate()); - Local function_template = - FunctionTemplate::New(env->isolate()); - - function_template->SetClassName(sandbox_obj->GetConstructorName()); +Local ContextifyContext::CreateGlobalTemplate( + Isolate* isolate) { + Local function_template = FunctionTemplate::New(isolate); Local object_template = function_template->InstanceTemplate(); - Local data_wrapper; - if (!CreateDataWrapper(env).ToLocal(&data_wrapper)) - return MaybeLocal(); - NamedPropertyHandlerConfiguration config( PropertyGetterCallback, PropertySetterCallback, @@ -182,7 +168,7 @@ MaybeLocal ContextifyContext::CreateV8Context( PropertyDeleterCallback, PropertyEnumeratorCallback, PropertyDefinerCallback, - data_wrapper, + {}, PropertyHandlerFlags::kHasNoSideEffect); IndexedPropertyHandlerConfiguration indexed_config( @@ -192,33 +178,48 @@ MaybeLocal ContextifyContext::CreateV8Context( IndexedPropertyDeleterCallback, PropertyEnumeratorCallback, IndexedPropertyDefinerCallback, - data_wrapper, + {}, PropertyHandlerFlags::kHasNoSideEffect); object_template->SetHandler(config); object_template->SetHandler(indexed_config); - Local ctx = Context::New( - env->isolate(), - nullptr, // extensions - object_template, - {}, // global object - {}, // deserialization callback - microtask_queue() ? - microtask_queue().get() : - env->isolate()->GetCurrentContext()->GetMicrotaskQueue()); + + return object_template; +} + +MaybeLocal ContextifyContext::CreateV8Context( + Isolate* isolate, + Local object_template, + MicrotaskQueue* queue) { + EscapableHandleScope scope(isolate); + + Local ctx = Context::New(isolate, + nullptr, // extensions + object_template, + {}, // global object + {}, // deserialization callback + queue); if (ctx.IsEmpty()) return MaybeLocal(); - // Only partially initialize the context - the primordials are left out - // and only initialized when necessary. - if (InitializeContextRuntime(ctx).IsNothing()) { - return MaybeLocal(); - } - if (ctx.IsEmpty()) { - return MaybeLocal(); + return scope.Escape(ctx); +} + +bool ContextifyContext::InitializeContext(Local ctx, + Environment* env, + Local sandbox_obj, + const ContextOptions& options) { + HandleScope scope(env->isolate()); + + // This only initializes part of the context. The primordials are + // only initilaized when needed because even deserializing them slows + // things down significantly and they are only needed in rare occasions + // in the vm contexts. + if (InitializeContextRuntime(ctx).IsNothing()) { + return false; } - Local context = env->context(); - ctx->SetSecurityToken(context->GetSecurityToken()); + Local main_context = env->context(); + ctx->SetSecurityToken(main_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 @@ -227,11 +228,9 @@ MaybeLocal ContextifyContext::CreateV8Context( // 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(ContextEmbedderIndex::kSandboxObject, sandbox_obj); - sandbox_obj->SetPrivate(context, - env->contextify_global_private_symbol(), - ctx->Global()); + sandbox_obj->SetPrivate( + main_context, env->contextify_global_private_symbol(), ctx->Global()); - Utf8Value name_val(env->isolate(), options.name); // Delegate the code generation validation to // node::ModifyCodeGenerationFromStrings. ctx->AllowCodeGenerationFromStrings(false); @@ -240,30 +239,39 @@ MaybeLocal ContextifyContext::CreateV8Context( ctx->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, options.allow_code_gen_wasm); + Utf8Value name_val(env->isolate(), options.name); ContextInfo info(*name_val); - if (!options.origin.IsEmpty()) { Utf8Value origin_val(env->isolate(), options.origin); info.origin = *origin_val; } + { + Context::Scope context_scope(ctx); + Local ctor_name = sandbox_obj->GetConstructorName(); + if (!ctor_name->Equals(ctx, env->object_string()).FromMaybe(false) && + ctx->Global() + ->DefineOwnProperty( + ctx, + v8::Symbol::GetToStringTag(env->isolate()), + ctor_name, + static_cast(v8::DontEnum)) + .IsNothing()) { + return false; + } + } + env->AssignToContext(ctx, info); - return scope.Escape(ctx); + // This should only be done after the initial initializations of the context + // global object is finished. + ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kContextifyContext, + this); + return true; } - void ContextifyContext::Init(Environment* env, Local target) { - Isolate* isolate = env->isolate(); Local context = env->context(); - - Local function_template = - NewFunctionTemplate(isolate, nullptr); - function_template->InstanceTemplate()->SetInternalFieldCount( - ContextifyContext::kInternalFieldCount); - env->set_script_data_constructor_function( - function_template->GetFunction(env->context()).ToLocalChecked()); - SetMethod(context, target, "makeContext", MakeContext); SetMethod(context, target, "isContext", IsContext); SetMethod(context, target, "compileFunction", CompileFunction); @@ -274,6 +282,17 @@ void ContextifyContext::RegisterExternalReferences( registry->Register(MakeContext); registry->Register(IsContext); registry->Register(CompileFunction); + registry->Register(PropertyGetterCallback); + registry->Register(PropertySetterCallback); + registry->Register(PropertyDescriptorCallback); + registry->Register(PropertyDeleterCallback); + registry->Register(PropertyEnumeratorCallback); + registry->Register(PropertyDefinerCallback); + registry->Register(IndexedPropertyGetterCallback); + registry->Register(IndexedPropertySetterCallback); + registry->Register(IndexedPropertyDescriptorCallback); + registry->Register(IndexedPropertyDeleterCallback); + registry->Register(IndexedPropertyDefinerCallback); } // makeContext(sandbox, name, origin, strings, wasm); @@ -323,8 +342,8 @@ void ContextifyContext::MakeContext(const FunctionCallbackInfo& args) { return; } - if (context_ptr->context().IsEmpty()) - return; + Local new_context = context_ptr->context(); + if (new_context.IsEmpty()) return; sandbox->SetPrivate( env->context(), @@ -371,10 +390,20 @@ ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox( // static template ContextifyContext* ContextifyContext::Get(const PropertyCallbackInfo& args) { - Local data = args.Data(); + Local context; + if (!args.This()->GetCreationContext().ToLocal(&context)) { + return nullptr; + } + if (!ContextEmbedderTag::IsNodeContext(context)) { + return nullptr; + } return static_cast( - data.As()->GetAlignedPointerFromInternalField( - ContextifyContext::kSlot)); + context->GetAlignedPointerFromEmbedderData( + ContextEmbedderIndex::kContextifyContext)); +} + +bool ContextifyContext::IsStillInitializing(const ContextifyContext* ctx) { + return ctx == nullptr || ctx->context_.IsEmpty(); } // static @@ -384,8 +413,7 @@ void ContextifyContext::PropertyGetterCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; Local context = ctx->context(); Local sandbox = ctx->sandbox(); @@ -413,8 +441,7 @@ void ContextifyContext::PropertySetterCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; Local context = ctx->context(); PropertyAttribute attributes = PropertyAttribute::None; @@ -466,8 +493,7 @@ void ContextifyContext::PropertyDescriptorCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; Local context = ctx->context(); @@ -489,8 +515,7 @@ void ContextifyContext::PropertyDefinerCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; Local context = ctx->context(); Isolate* isolate = context->GetIsolate(); @@ -550,8 +575,7 @@ void ContextifyContext::PropertyDeleterCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; Maybe success = ctx->sandbox()->Delete(ctx->context(), property); @@ -569,8 +593,7 @@ void ContextifyContext::PropertyEnumeratorCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; Local properties; @@ -587,8 +610,7 @@ void ContextifyContext::IndexedPropertyGetterCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; ContextifyContext::PropertyGetterCallback( Uint32ToName(ctx->context(), index), args); @@ -602,8 +624,7 @@ void ContextifyContext::IndexedPropertySetterCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; ContextifyContext::PropertySetterCallback( Uint32ToName(ctx->context(), index), value, args); @@ -616,8 +637,7 @@ void ContextifyContext::IndexedPropertyDescriptorCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; ContextifyContext::PropertyDescriptorCallback( Uint32ToName(ctx->context(), index), args); @@ -631,8 +651,7 @@ void ContextifyContext::IndexedPropertyDefinerCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; ContextifyContext::PropertyDefinerCallback( Uint32ToName(ctx->context(), index), desc, args); @@ -645,8 +664,7 @@ void ContextifyContext::IndexedPropertyDeleterCallback( ContextifyContext* ctx = ContextifyContext::Get(args); // Still initializing - if (ctx->context_.IsEmpty()) - return; + if (IsStillInitializing(ctx)) return; Maybe success = ctx->sandbox()->Delete(ctx->context(), index); @@ -891,8 +909,8 @@ void ContextifyScript::RunInContext(const FunctionCallbackInfo& args) { bool break_on_first_line = args[4]->IsTrue(); // Do the eval within the context - Context::Scope context_scope(context); - EvalMachine(env, + EvalMachine(context, + env, timeout, display_errors, break_on_sigint, @@ -901,13 +919,16 @@ void ContextifyScript::RunInContext(const FunctionCallbackInfo& args) { args); } -bool ContextifyScript::EvalMachine(Environment* env, +bool ContextifyScript::EvalMachine(Local context, + Environment* env, const int64_t timeout, const bool display_errors, const bool break_on_sigint, const bool break_on_first_line, std::shared_ptr mtask_queue, const FunctionCallbackInfo& args) { + Context::Scope context_scope(context); + if (!env->can_call_into_js()) return false; if (!ContextifyScript::InstanceOf(env, args.Holder())) { @@ -916,6 +937,7 @@ bool ContextifyScript::EvalMachine(Environment* env, "Script methods can only be called on script instances."); return false; } + TryCatchScope try_catch(env); Isolate::SafeForTerminationScope safe_for_termination(env->isolate()); ContextifyScript* wrapped_script; @@ -934,7 +956,7 @@ bool ContextifyScript::EvalMachine(Environment* env, bool timed_out = false; bool received_signal = false; auto run = [&]() { - MaybeLocal result = script->Run(env->context()); + MaybeLocal result = script->Run(context); if (!result.IsEmpty() && mtask_queue) mtask_queue->PerformCheckpoint(env->isolate()); return result; diff --git a/src/node_contextify.h b/src/node_contextify.h index c9b5fc0f62dcfc..088b5c5a56192c 100644 --- a/src/node_contextify.h +++ b/src/node_contextify.h @@ -43,17 +43,20 @@ struct ContextOptions { class ContextifyContext { public: - enum InternalFields { kSlot, kInternalFieldCount }; ContextifyContext(Environment* env, v8::Local sandbox_obj, const ContextOptions& options); ~ContextifyContext(); static void CleanupHook(void* arg); - v8::MaybeLocal CreateDataWrapper(Environment* env); - v8::MaybeLocal CreateV8Context(Environment* env, - v8::Local sandbox_obj, - const ContextOptions& options); + static v8::MaybeLocal CreateV8Context( + v8::Isolate* isolate, + v8::Local object_template, + v8::MicrotaskQueue* queue); + bool InitializeContext(v8::Local ctx, + Environment* env, + v8::Local sandbox_obj, + const ContextOptions& options); static void Init(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -83,11 +86,14 @@ class ContextifyContext { return microtask_queue_wrap_->microtask_queue(); } - template static ContextifyContext* Get(const v8::PropertyCallbackInfo& args); + static v8::Local CreateGlobalTemplate( + v8::Isolate* isolate); + private: + static bool IsStillInitializing(const ContextifyContext* ctx); static void MakeContext(const v8::FunctionCallbackInfo& args); static void IsContext(const v8::FunctionCallbackInfo& args); static void CompileFunction( @@ -150,7 +156,8 @@ class ContextifyScript : public BaseObject { static bool InstanceOf(Environment* env, const v8::Local& args); static void CreateCachedData(const v8::FunctionCallbackInfo& args); static void RunInContext(const v8::FunctionCallbackInfo& args); - static bool EvalMachine(Environment* env, + static bool EvalMachine(v8::Local context, + Environment* env, const int64_t timeout, const bool display_errors, const bool break_on_sigint, diff --git a/src/node_external_reference.h b/src/node_external_reference.h index b7d7a63a8a88a6..a77c1ab14ee0de 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -30,7 +30,12 @@ class ExternalReferenceRegistry { V(v8::GenericNamedPropertyDeleterCallback) \ V(v8::GenericNamedPropertyEnumeratorCallback) \ V(v8::GenericNamedPropertyQueryCallback) \ - V(v8::GenericNamedPropertySetterCallback) + V(v8::GenericNamedPropertySetterCallback) \ + V(v8::IndexedPropertySetterCallback) \ + V(v8::IndexedPropertyDefinerCallback) \ + V(v8::IndexedPropertyDeleterCallback) \ + V(v8::IndexedPropertyQueryCallback) \ + V(v8::IndexedPropertyDescriptorCallback) #define V(ExternalReferenceType) \ void Register(ExternalReferenceType addr) { RegisterT(addr); }