Skip to content

Commit

Permalink
vm: lazily initialize primordials for vm contexts
Browse files Browse the repository at this point in the history
Lazily initialize primordials when cross-context support for
builtins is needed to fix the performance regression in context
creation.

PR-URL: nodejs#31738
Fixes: nodejs#29842
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: David Carlier <devnexen@gmail.com>
  • Loading branch information
joyeecheung committed Feb 19, 2020
1 parent 822101f commit e6c2277
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 43 deletions.
84 changes: 43 additions & 41 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,8 @@ MaybeLocal<Object> GetPerContextExports(Local<Context> context) {
return handle_scope.Escape(existing_value.As<Object>());

Local<Object> exports = Object::New(isolate);
if (context->Global()->SetPrivate(context, key, exports).IsNothing())
if (context->Global()->SetPrivate(context, key, exports).IsNothing() ||
!InitializePrimordials(context))
return MaybeLocal<Object>();
return handle_scope.Escape(exports);
}
Expand Down Expand Up @@ -461,49 +462,50 @@ bool InitializeContextForSnapshot(Local<Context> context) {

context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
True(isolate));
return InitializePrimordials(context);
}

bool InitializePrimordials(Local<Context> context) {
// Run per-context JS files.
Isolate* isolate = context->GetIsolate();
Context::Scope context_scope(context);
Local<Object> exports;

Local<String> primordials_string =
FIXED_ONE_BYTE_STRING(isolate, "primordials");
Local<String> global_string = FIXED_ONE_BYTE_STRING(isolate, "global");
Local<String> exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports");

// Create primordials first and make it available to per-context scripts.
Local<Object> 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<Object> exports;

Local<String> primordials_string =
FIXED_ONE_BYTE_STRING(isolate, "primordials");
Local<String> global_string = FIXED_ONE_BYTE_STRING(isolate, "global");
Local<String> exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports");

// Create primordials first and make it available to per-context scripts.
Local<Object> 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<Local<String>> parameters = {
global_string, exports_string, primordials_string};
Local<Value> arguments[] = {context->Global(), exports, primordials};
MaybeLocal<Function> maybe_fn =
native_module::NativeModuleEnv::LookupAndCompile(
context, *module, &parameters, 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<Local<String>> parameters = {
global_string, exports_string, primordials_string};
Local<Value> arguments[] = {context->Global(), exports, primordials};
MaybeLocal<Function> maybe_fn =
native_module::NativeModuleEnv::LookupAndCompile(
context, *module, &parameters, nullptr);
if (maybe_fn.IsEmpty()) {
return false;
}
Local<Function> fn = maybe_fn.ToLocalChecked();
MaybeLocal<Value> 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<Function> fn = maybe_fn.ToLocalChecked();
MaybeLocal<Value> 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;
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/node_contextify.cc
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,11 @@ MaybeLocal<Context> ContextifyContext::CreateV8Context(

object_template->SetHandler(config);
object_template->SetHandler(indexed_config);

Local<Context> ctx = NewContext(env->isolate(), object_template);
Local<Context> ctx = Context::New(env->isolate(), nullptr, object_template);
if (ctx.IsEmpty()) return MaybeLocal<Context>();
// Only partially initialize the context - the primordials are left out
// and only initialized when necessary.
InitializeContextRuntime(ctx);

if (ctx.IsEmpty()) {
return MaybeLocal<Context>();
Expand Down
1 change: 1 addition & 0 deletions src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ std::string GetProcessTitle(const char* default_title);
std::string GetHumanReadableProcessName();

void InitializeContextRuntime(v8::Local<v8::Context>);
bool InitializePrimordials(v8::Local<v8::Context> context);

namespace task_queue {
void PromiseRejectCallback(v8::PromiseRejectMessage message);
Expand Down

0 comments on commit e6c2277

Please sign in to comment.