Skip to content

Commit e6c2277

Browse files
committed
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: #31738 Fixes: #29842 Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: David Carlier <[email protected]>
1 parent 822101f commit e6c2277

File tree

3 files changed

+49
-43
lines changed

3 files changed

+49
-43
lines changed

src/api/environment.cc

+43-41
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ MaybeLocal<Object> GetPerContextExports(Local<Context> context) {
405405
return handle_scope.Escape(existing_value.As<Object>());
406406

407407
Local<Object> exports = Object::New(isolate);
408-
if (context->Global()->SetPrivate(context, key, exports).IsNothing())
408+
if (context->Global()->SetPrivate(context, key, exports).IsNothing() ||
409+
!InitializePrimordials(context))
409410
return MaybeLocal<Object>();
410411
return handle_scope.Escape(exports);
411412
}
@@ -461,49 +462,50 @@ bool InitializeContextForSnapshot(Local<Context> context) {
461462

462463
context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
463464
True(isolate));
465+
return InitializePrimordials(context);
466+
}
467+
468+
bool InitializePrimordials(Local<Context> context) {
469+
// Run per-context JS files.
470+
Isolate* isolate = context->GetIsolate();
471+
Context::Scope context_scope(context);
472+
Local<Object> exports;
473+
474+
Local<String> primordials_string =
475+
FIXED_ONE_BYTE_STRING(isolate, "primordials");
476+
Local<String> global_string = FIXED_ONE_BYTE_STRING(isolate, "global");
477+
Local<String> exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports");
478+
479+
// Create primordials first and make it available to per-context scripts.
480+
Local<Object> primordials = Object::New(isolate);
481+
if (!primordials->SetPrototype(context, Null(isolate)).FromJust() ||
482+
!GetPerContextExports(context).ToLocal(&exports) ||
483+
!exports->Set(context, primordials_string, primordials).FromJust()) {
484+
return false;
485+
}
464486

465-
{
466-
// Run per-context JS files.
467-
Context::Scope context_scope(context);
468-
Local<Object> exports;
469-
470-
Local<String> primordials_string =
471-
FIXED_ONE_BYTE_STRING(isolate, "primordials");
472-
Local<String> global_string = FIXED_ONE_BYTE_STRING(isolate, "global");
473-
Local<String> exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports");
474-
475-
// Create primordials first and make it available to per-context scripts.
476-
Local<Object> primordials = Object::New(isolate);
477-
if (!primordials->SetPrototype(context, Null(isolate)).FromJust() ||
478-
!GetPerContextExports(context).ToLocal(&exports) ||
479-
!exports->Set(context, primordials_string, primordials).FromJust()) {
487+
static const char* context_files[] = {"internal/per_context/primordials",
488+
"internal/per_context/domexception",
489+
"internal/per_context/messageport",
490+
nullptr};
491+
492+
for (const char** module = context_files; *module != nullptr; module++) {
493+
std::vector<Local<String>> parameters = {
494+
global_string, exports_string, primordials_string};
495+
Local<Value> arguments[] = {context->Global(), exports, primordials};
496+
MaybeLocal<Function> maybe_fn =
497+
native_module::NativeModuleEnv::LookupAndCompile(
498+
context, *module, &parameters, nullptr);
499+
if (maybe_fn.IsEmpty()) {
480500
return false;
481501
}
482-
483-
static const char* context_files[] = {"internal/per_context/primordials",
484-
"internal/per_context/domexception",
485-
"internal/per_context/messageport",
486-
nullptr};
487-
488-
for (const char** module = context_files; *module != nullptr; module++) {
489-
std::vector<Local<String>> parameters = {
490-
global_string, exports_string, primordials_string};
491-
Local<Value> arguments[] = {context->Global(), exports, primordials};
492-
MaybeLocal<Function> maybe_fn =
493-
native_module::NativeModuleEnv::LookupAndCompile(
494-
context, *module, &parameters, nullptr);
495-
if (maybe_fn.IsEmpty()) {
496-
return false;
497-
}
498-
Local<Function> fn = maybe_fn.ToLocalChecked();
499-
MaybeLocal<Value> result =
500-
fn->Call(context, Undefined(isolate),
501-
arraysize(arguments), arguments);
502-
// Execution failed during context creation.
503-
// TODO(joyeecheung): deprecate this signature and return a MaybeLocal.
504-
if (result.IsEmpty()) {
505-
return false;
506-
}
502+
Local<Function> fn = maybe_fn.ToLocalChecked();
503+
MaybeLocal<Value> result =
504+
fn->Call(context, Undefined(isolate), arraysize(arguments), arguments);
505+
// Execution failed during context creation.
506+
// TODO(joyeecheung): deprecate this signature and return a MaybeLocal.
507+
if (result.IsEmpty()) {
508+
return false;
507509
}
508510
}
509511

src/node_contextify.cc

+5-2
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,11 @@ MaybeLocal<Context> ContextifyContext::CreateV8Context(
185185

186186
object_template->SetHandler(config);
187187
object_template->SetHandler(indexed_config);
188-
189-
Local<Context> ctx = NewContext(env->isolate(), object_template);
188+
Local<Context> ctx = Context::New(env->isolate(), nullptr, object_template);
189+
if (ctx.IsEmpty()) return MaybeLocal<Context>();
190+
// Only partially initialize the context - the primordials are left out
191+
// and only initialized when necessary.
192+
InitializeContextRuntime(ctx);
190193

191194
if (ctx.IsEmpty()) {
192195
return MaybeLocal<Context>();

src/node_internals.h

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ std::string GetProcessTitle(const char* default_title);
9999
std::string GetHumanReadableProcessName();
100100

101101
void InitializeContextRuntime(v8::Local<v8::Context>);
102+
bool InitializePrimordials(v8::Local<v8::Context> context);
102103

103104
namespace task_queue {
104105
void PromiseRejectCallback(v8::PromiseRejectMessage message);

0 commit comments

Comments
 (0)