From a86cb0e480b0a47eed5cc36366d7b90737d587f4 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 11 Feb 2020 22:49:24 +0800 Subject: [PATCH] vm: lazily initialize primordials for vm contexts Lazily initialize primordials when cross-context support for builtins is needed to fix the performance regression in context creation. PR-URL: https://github.com/nodejs/node/pull/31738 Fixes: https://github.com/nodejs/node/issues/29842 Reviewed-By: Gus Caplan Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- src/api/environment.cc | 84 +++++++++++++++++++++--------------------- src/node_contextify.cc | 7 +++- src/node_internals.h | 1 + 3 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/api/environment.cc b/src/api/environment.cc index f25a4cf183292f..0096b498e1c511 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -411,7 +411,8 @@ MaybeLocal GetPerContextExports(Local context) { return handle_scope.Escape(existing_value.As()); Local exports = Object::New(isolate); - if (context->Global()->SetPrivate(context, key, exports).IsNothing()) + if (context->Global()->SetPrivate(context, key, exports).IsNothing() || + !InitializePrimordials(context)) return MaybeLocal(); return handle_scope.Escape(exports); } @@ -467,49 +468,50 @@ bool InitializeContextForSnapshot(Local context) { context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, True(isolate)); + return InitializePrimordials(context); +} + +bool InitializePrimordials(Local context) { + // Run per-context JS files. + Isolate* isolate = context->GetIsolate(); + Context::Scope context_scope(context); + Local exports; + + Local primordials_string = + FIXED_ONE_BYTE_STRING(isolate, "primordials"); + Local global_string = FIXED_ONE_BYTE_STRING(isolate, "global"); + Local exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports"); + + // Create primordials first and make it available to per-context scripts. + Local primordials = Object::New(isolate); + if (!primordials->SetPrototype(context, Null(isolate)).FromJust() || + !GetPerContextExports(context).ToLocal(&exports) || + !exports->Set(context, primordials_string, primordials).FromJust()) { + return false; + } - { - // Run per-context JS files. - Context::Scope context_scope(context); - Local exports; - - Local primordials_string = - FIXED_ONE_BYTE_STRING(isolate, "primordials"); - Local global_string = FIXED_ONE_BYTE_STRING(isolate, "global"); - Local exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports"); - - // Create primordials first and make it available to per-context scripts. - Local primordials = Object::New(isolate); - if (!primordials->SetPrototype(context, Null(isolate)).FromJust() || - !GetPerContextExports(context).ToLocal(&exports) || - !exports->Set(context, primordials_string, primordials).FromJust()) { + static const char* context_files[] = {"internal/per_context/primordials", + "internal/per_context/domexception", + "internal/per_context/messageport", + nullptr}; + + for (const char** module = context_files; *module != nullptr; module++) { + std::vector> parameters = { + global_string, exports_string, primordials_string}; + Local arguments[] = {context->Global(), exports, primordials}; + MaybeLocal maybe_fn = + native_module::NativeModuleEnv::LookupAndCompile( + context, *module, ¶meters, nullptr); + if (maybe_fn.IsEmpty()) { return false; } - - static const char* context_files[] = {"internal/per_context/primordials", - "internal/per_context/domexception", - "internal/per_context/messageport", - nullptr}; - - for (const char** module = context_files; *module != nullptr; module++) { - std::vector> parameters = { - global_string, exports_string, primordials_string}; - Local arguments[] = {context->Global(), exports, primordials}; - MaybeLocal maybe_fn = - native_module::NativeModuleEnv::LookupAndCompile( - context, *module, ¶meters, nullptr); - if (maybe_fn.IsEmpty()) { - return false; - } - Local fn = maybe_fn.ToLocalChecked(); - MaybeLocal result = - fn->Call(context, Undefined(isolate), - arraysize(arguments), arguments); - // Execution failed during context creation. - // TODO(joyeecheung): deprecate this signature and return a MaybeLocal. - if (result.IsEmpty()) { - return false; - } + Local fn = maybe_fn.ToLocalChecked(); + MaybeLocal result = + fn->Call(context, Undefined(isolate), arraysize(arguments), arguments); + // Execution failed during context creation. + // TODO(joyeecheung): deprecate this signature and return a MaybeLocal. + if (result.IsEmpty()) { + return false; } } diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 46a1d7c8ef0691..5f289aecb3414c 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -185,8 +185,11 @@ MaybeLocal ContextifyContext::CreateV8Context( object_template->SetHandler(config); object_template->SetHandler(indexed_config); - - Local ctx = NewContext(env->isolate(), object_template); + Local ctx = Context::New(env->isolate(), nullptr, object_template); + if (ctx.IsEmpty()) return MaybeLocal(); + // Only partially initialize the context - the primordials are left out + // and only initialized when necessary. + InitializeContextRuntime(ctx); if (ctx.IsEmpty()) { return MaybeLocal(); diff --git a/src/node_internals.h b/src/node_internals.h index 8d63f023c1ffac..91ba2a58a755ae 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -99,6 +99,7 @@ std::string GetProcessTitle(const char* default_title); std::string GetHumanReadableProcessName(); void InitializeContextRuntime(v8::Local); +bool InitializePrimordials(v8::Local context); namespace task_queue { void PromiseRejectCallback(v8::PromiseRejectMessage message);