Skip to content

Commit a7f3bc0

Browse files
legendecasdanielleadams
authored andcommittedJan 3, 2023
src: introduce node::Realm
To distinguish per-context values from the node::Environment, split those values to a new node::Realm structure and consolidate bootstrapping methods with it. PR-URL: #44179 Refs: #42528 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent b11616b commit a7f3bc0

21 files changed

+1142
-830
lines changed
 

‎node.gyp

+4
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@
525525
'src/node_process_events.cc',
526526
'src/node_process_methods.cc',
527527
'src/node_process_object.cc',
528+
'src/node_realm.cc',
528529
'src/node_report.cc',
529530
'src/node_report_module.cc',
530531
'src/node_report_utils.cc',
@@ -585,6 +586,7 @@
585586
'src/connection_wrap.h',
586587
'src/debug_utils.h',
587588
'src/debug_utils-inl.h',
589+
'src/env_properties.h',
588590
'src/env.h',
589591
'src/env-inl.h',
590592
'src/handle_wrap.h',
@@ -632,6 +634,8 @@
632634
'src/node_platform.h',
633635
'src/node_process.h',
634636
'src/node_process-inl.h',
637+
'src/node_realm.h',
638+
'src/node_realm-inl.h',
635639
'src/node_report.h',
636640
'src/node_revert.h',
637641
'src/node_root_certs.h',

‎src/api/environment.cc

+7-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "node_internals.h"
66
#include "node_options-inl.h"
77
#include "node_platform.h"
8+
#include "node_realm-inl.h"
89
#include "node_shadow_realm.h"
910
#include "node_v8_platform-inl.h"
1011
#include "node_wasm_web_api.h"
@@ -378,7 +379,7 @@ Environment* CreateEnvironment(
378379
}
379380
#endif
380381

381-
if (env->RunBootstrapping().IsEmpty()) {
382+
if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
382383
FreeEnvironment(env);
383384
return nullptr;
384385
}
@@ -453,11 +454,13 @@ MaybeLocal<Value> LoadEnvironment(
453454
builtins::BuiltinLoader::Add(
454455
name.c_str(), UnionBytes(**main_utf16, main_utf16->length()));
455456
env->set_main_utf16(std::move(main_utf16));
457+
Realm* realm = env->principal_realm();
458+
456459
// Arguments must match the parameters specified in
457460
// BuiltinLoader::LookupAndCompile().
458-
std::vector<Local<Value>> args = {env->process_object(),
459-
env->builtin_module_require()};
460-
return ExecuteBootstrapper(env, name.c_str(), &args);
461+
std::vector<Local<Value>> args = {realm->process_object(),
462+
realm->builtin_module_require()};
463+
return realm->ExecuteBootstrapper(name.c_str(), &args);
461464
});
462465
}
463466

‎src/base_object-inl.h

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232

3333
namespace node {
3434

35+
// static
36+
v8::Local<v8::FunctionTemplate> BaseObject::GetConstructorTemplate(
37+
Environment* env) {
38+
return BaseObject::GetConstructorTemplate(env->isolate_data());
39+
}
40+
3541
void BaseObject::Detach() {
3642
CHECK_GT(pointer_data()->strong_ptr_count, 0);
3743
pointer_data()->is_detached = true;

‎src/base_object.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
namespace node {
3232

3333
class Environment;
34+
class IsolateData;
3435
template <typename T, bool kIsWeak>
3536
class BaseObjectPtrImpl;
3637

@@ -113,8 +114,10 @@ class BaseObject : public MemoryRetainer {
113114
// a BaseObjectPtr to this object.
114115
inline void Detach();
115116

116-
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
117+
static inline v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
117118
Environment* env);
119+
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
120+
IsolateData* isolate_data);
118121

119122
// Interface for transferring BaseObject instances using the .postMessage()
120123
// method of MessagePorts (and, by extension, Workers).

‎src/env-inl.h

+20-17
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "node_context_data.h"
3232
#include "node_internals.h"
3333
#include "node_perf_common.h"
34+
#include "node_realm-inl.h"
3435
#include "util-inl.h"
3536
#include "uv.h"
3637
#include "v8.h"
@@ -614,15 +615,7 @@ inline void Environment::set_can_call_into_js(bool can_call_into_js) {
614615
}
615616

616617
inline bool Environment::has_run_bootstrapping_code() const {
617-
return has_run_bootstrapping_code_;
618-
}
619-
620-
inline void Environment::DoneBootstrapping() {
621-
has_run_bootstrapping_code_ = true;
622-
// This adjusts the return value of base_object_created_after_bootstrap() so
623-
// that tests that check the count do not have to account for internally
624-
// created BaseObjects.
625-
base_object_created_by_bootstrap_ = base_object_count_;
618+
return principal_realm_->has_run_bootstrapping_code();
626619
}
627620

628621
inline bool Environment::has_serialized_options() const {
@@ -805,14 +798,18 @@ void Environment::modify_base_object_count(int64_t delta) {
805798
base_object_count_ += delta;
806799
}
807800

808-
int64_t Environment::base_object_created_after_bootstrap() const {
809-
return base_object_count_ - base_object_created_by_bootstrap_;
810-
}
811-
812801
int64_t Environment::base_object_count() const {
813802
return base_object_count_;
814803
}
815804

805+
inline void Environment::set_base_object_created_by_bootstrap(int64_t count) {
806+
base_object_created_by_bootstrap_ = base_object_count_;
807+
}
808+
809+
int64_t Environment::base_object_created_after_bootstrap() const {
810+
return base_object_count_ - base_object_created_by_bootstrap_;
811+
}
812+
816813
void Environment::set_main_utf16(std::unique_ptr<v8::String::Value> str) {
817814
CHECK(!main_utf16_);
818815
main_utf16_ = std::move(str);
@@ -877,16 +874,22 @@ void Environment::set_process_exit_handler(
877874

878875
#define V(PropertyName, TypeName) \
879876
inline v8::Local<TypeName> Environment::PropertyName() const { \
880-
return PersistentToLocal::Strong(PropertyName##_); \
877+
DCHECK_NOT_NULL(principal_realm_); \
878+
return principal_realm_->PropertyName(); \
881879
} \
882880
inline void Environment::set_##PropertyName(v8::Local<TypeName> value) { \
883-
PropertyName##_.Reset(isolate(), value); \
881+
DCHECK_NOT_NULL(principal_realm_); \
882+
principal_realm_->set_##PropertyName(value); \
884883
}
885-
ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)
884+
PER_REALM_STRONG_PERSISTENT_VALUES(V)
886885
#undef V
887886

888887
v8::Local<v8::Context> Environment::context() const {
889-
return PersistentToLocal::Strong(context_);
888+
return principal_realm()->context();
889+
}
890+
891+
Realm* Environment::principal_realm() const {
892+
return principal_realm_.get();
890893
}
891894

892895
inline void Environment::set_heap_snapshot_near_heap_limit(uint32_t limit) {

‎src/env.cc

+35-174
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,12 @@ void IsolateData::CreateProperties() {
448448
#undef V
449449

450450
// TODO(legendecas): eagerly create per isolate templates.
451+
Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
452+
templ->InstanceTemplate()->SetInternalFieldCount(
453+
BaseObject::kInternalFieldCount);
454+
templ->Inherit(BaseObject::GetConstructorTemplate(this));
455+
set_binding_data_ctor_template(templ);
456+
451457
set_contextify_global_template(
452458
contextify::ContextifyContext::CreateGlobalTemplate(isolate_));
453459
}
@@ -502,6 +508,10 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() {
502508
return;
503509
}
504510

511+
if (env_->principal_realm() == nullptr) {
512+
return;
513+
}
514+
505515
bool async_hooks_enabled = (*(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
506516
TRACING_CATEGORY_NODE1(async_hooks)))) != 0;
507517

@@ -517,9 +527,11 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() {
517527
}
518528

519529
void Environment::AssignToContext(Local<v8::Context> context,
530+
Realm* realm,
520531
const ContextInfo& info) {
521532
context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
522533
this);
534+
context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, realm);
523535
// Used to retrieve bindings
524536
context->SetAlignedPointerInEmbedderData(
525537
ContextEmbedderIndex::kBindingListIndex, &(this->bindings_));
@@ -594,56 +606,6 @@ std::unique_ptr<v8::BackingStore> Environment::release_managed_buffer(
594606
return bs;
595607
}
596608

597-
void Environment::CreateProperties() {
598-
HandleScope handle_scope(isolate_);
599-
Local<Context> ctx = context();
600-
601-
{
602-
Context::Scope context_scope(ctx);
603-
Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
604-
templ->InstanceTemplate()->SetInternalFieldCount(
605-
BaseObject::kInternalFieldCount);
606-
templ->Inherit(BaseObject::GetConstructorTemplate(this));
607-
608-
set_binding_data_ctor_template(templ);
609-
}
610-
611-
// Store primordials setup by the per-context script in the environment.
612-
Local<Object> per_context_bindings =
613-
GetPerContextExports(ctx).ToLocalChecked();
614-
Local<Value> primordials =
615-
per_context_bindings->Get(ctx, primordials_string()).ToLocalChecked();
616-
CHECK(primordials->IsObject());
617-
set_primordials(primordials.As<Object>());
618-
619-
Local<String> prototype_string =
620-
FIXED_ONE_BYTE_STRING(isolate(), "prototype");
621-
622-
#define V(EnvPropertyName, PrimordialsPropertyName) \
623-
{ \
624-
Local<Value> ctor = \
625-
primordials.As<Object>() \
626-
->Get(ctx, \
627-
FIXED_ONE_BYTE_STRING(isolate(), PrimordialsPropertyName)) \
628-
.ToLocalChecked(); \
629-
CHECK(ctor->IsObject()); \
630-
Local<Value> prototype = \
631-
ctor.As<Object>()->Get(ctx, prototype_string).ToLocalChecked(); \
632-
CHECK(prototype->IsObject()); \
633-
set_##EnvPropertyName(prototype.As<Object>()); \
634-
}
635-
636-
V(primordials_safe_map_prototype_object, "SafeMap");
637-
V(primordials_safe_set_prototype_object, "SafeSet");
638-
V(primordials_safe_weak_map_prototype_object, "SafeWeakMap");
639-
V(primordials_safe_weak_set_prototype_object, "SafeWeakSet");
640-
#undef V
641-
642-
Local<Object> process_object =
643-
node::CreateProcessObject(this).FromMaybe(Local<Object>());
644-
set_process_object(process_object);
645-
}
646-
647609
std::string GetExecPath(const std::vector<std::string>& argv) {
648610
char exec_path_buf[2 * PATH_MAX];
649611
size_t exec_path_len = sizeof(exec_path_buf);
@@ -781,12 +743,11 @@ Environment::Environment(IsolateData* isolate_data,
781743

782744
void Environment::InitializeMainContext(Local<Context> context,
783745
const EnvSerializeInfo* env_info) {
784-
context_.Reset(context->GetIsolate(), context);
785-
AssignToContext(context, ContextInfo(""));
746+
principal_realm_ = std::make_unique<Realm>(
747+
this, context, MAYBE_FIELD_PTR(env_info, principal_realm));
748+
AssignToContext(context, principal_realm_.get(), ContextInfo(""));
786749
if (env_info != nullptr) {
787750
DeserializeProperties(env_info);
788-
} else {
789-
CreateProperties();
790751
}
791752

792753
if (!options_->force_async_hooks_checks) {
@@ -811,16 +772,18 @@ void Environment::InitializeMainContext(Local<Context> context,
811772
}
812773

813774
Environment::~Environment() {
775+
HandleScope handle_scope(isolate());
776+
Local<Context> ctx = context();
777+
814778
if (Environment** interrupt_data = interrupt_data_.load()) {
815779
// There are pending RequestInterrupt() callbacks. Tell them not to run,
816780
// then force V8 to run interrupts by compiling and running an empty script
817781
// so as not to leak memory.
818782
*interrupt_data = nullptr;
819783

820784
Isolate::AllowJavascriptExecutionScope allow_js_here(isolate());
821-
HandleScope handle_scope(isolate());
822785
TryCatch try_catch(isolate());
823-
Context::Scope context_scope(context());
786+
Context::Scope context_scope(ctx);
824787

825788
#ifdef DEBUG
826789
bool consistency_check = false;
@@ -830,8 +793,8 @@ Environment::~Environment() {
830793
#endif
831794

832795
Local<Script> script;
833-
if (Script::Compile(context(), String::Empty(isolate())).ToLocal(&script))
834-
USE(script->Run(context()));
796+
if (Script::Compile(ctx, String::Empty(isolate())).ToLocal(&script))
797+
USE(script->Run(ctx));
835798

836799
DCHECK(consistency_check);
837800
}
@@ -846,16 +809,15 @@ Environment::~Environment() {
846809
isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
847810
BuildEmbedderGraph, this);
848811

849-
HandleScope handle_scope(isolate());
850-
851812
#if HAVE_INSPECTOR
852813
// Destroy inspector agent before erasing the context. The inspector
853814
// destructor depends on the context still being accessible.
854815
inspector_agent_.reset();
855816
#endif
856817

857-
context()->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
858-
nullptr);
818+
ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
819+
nullptr);
820+
ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, nullptr);
859821

860822
if (trace_state_observer_) {
861823
tracing::AgentWriterHandle* writer = GetTracingAgentWriter();
@@ -1677,81 +1639,14 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
16771639
info.should_abort_on_uncaught_toggle =
16781640
should_abort_on_uncaught_toggle_.Serialize(ctx, creator);
16791641

1680-
uint32_t id = 0;
1681-
#define V(PropertyName, TypeName) \
1682-
do { \
1683-
Local<TypeName> field = PropertyName(); \
1684-
if (!field.IsEmpty()) { \
1685-
size_t index = creator->AddData(ctx, field); \
1686-
info.persistent_values.push_back({#PropertyName, id, index}); \
1687-
} \
1688-
id++; \
1689-
} while (0);
1690-
ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)
1691-
#undef V
1692-
16931642
// Do this after other creator->AddData() calls so that Snapshotable objects
16941643
// can use 0 to indicate that a SnapshotIndex is invalid.
16951644
SerializeSnapshotableObjects(this, creator, &info);
16961645

1697-
info.context = creator->AddData(ctx, context());
1646+
info.principal_realm = principal_realm_->Serialize(creator);
16981647
return info;
16991648
}
17001649

1701-
std::ostream& operator<<(std::ostream& output,
1702-
const std::vector<PropInfo>& vec) {
1703-
output << "{\n";
1704-
for (const auto& info : vec) {
1705-
output << " " << info << ",\n";
1706-
}
1707-
output << "}";
1708-
return output;
1709-
}
1710-
1711-
std::ostream& operator<<(std::ostream& output, const PropInfo& info) {
1712-
output << "{ \"" << info.name << "\", " << std::to_string(info.id) << ", "
1713-
<< std::to_string(info.index) << " }";
1714-
return output;
1715-
}
1716-
1717-
std::ostream& operator<<(std::ostream& output,
1718-
const std::vector<std::string>& vec) {
1719-
output << "{\n";
1720-
for (const auto& info : vec) {
1721-
output << " \"" << info << "\",\n";
1722-
}
1723-
output << "}";
1724-
return output;
1725-
}
1726-
1727-
std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
1728-
output << "{\n"
1729-
<< "// -- native_objects begins --\n"
1730-
<< i.native_objects << ",\n"
1731-
<< "// -- native_objects ends --\n"
1732-
<< "// -- builtins begins --\n"
1733-
<< i.builtins << ",\n"
1734-
<< "// -- builtins ends --\n"
1735-
<< "// -- async_hooks begins --\n"
1736-
<< i.async_hooks << ",\n"
1737-
<< "// -- async_hooks ends --\n"
1738-
<< i.tick_info << ", // tick_info\n"
1739-
<< i.immediate_info << ", // immediate_info\n"
1740-
<< "// -- performance_state begins --\n"
1741-
<< i.performance_state << ",\n"
1742-
<< "// -- performance_state ends --\n"
1743-
<< i.exiting << ", // exiting\n"
1744-
<< i.stream_base_state << ", // stream_base_state\n"
1745-
<< i.should_abort_on_uncaught_toggle
1746-
<< ", // should_abort_on_uncaught_toggle\n"
1747-
<< "// -- persistent_values begins --\n"
1748-
<< i.persistent_values << ",\n"
1749-
<< "// -- persistent_values ends --\n"
1750-
<< i.context << ", // context\n"
1751-
<< "}";
1752-
return output;
1753-
}
1754-
17551650
void Environment::EnqueueDeserializeRequest(DeserializeRequestCallback cb,
17561651
Local<Object> holder,
17571652
int index,
@@ -1789,44 +1684,12 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
17891684
stream_base_state_.Deserialize(ctx);
17901685
should_abort_on_uncaught_toggle_.Deserialize(ctx);
17911686

1687+
principal_realm_->DeserializeProperties(&info->principal_realm);
1688+
17921689
if (enabled_debug_list_.enabled(DebugCategory::MKSNAPSHOT)) {
17931690
fprintf(stderr, "deserializing...\n");
17941691
std::cerr << *info << "\n";
17951692
}
1796-
1797-
const std::vector<PropInfo>& values = info->persistent_values;
1798-
size_t i = 0; // index to the array
1799-
uint32_t id = 0;
1800-
#define V(PropertyName, TypeName) \
1801-
do { \
1802-
if (values.size() > i && id == values[i].id) { \
1803-
const PropInfo& d = values[i]; \
1804-
DCHECK_EQ(d.name, #PropertyName); \
1805-
MaybeLocal<TypeName> maybe_field = \
1806-
ctx->GetDataFromSnapshotOnce<TypeName>(d.index); \
1807-
Local<TypeName> field; \
1808-
if (!maybe_field.ToLocal(&field)) { \
1809-
fprintf(stderr, \
1810-
"Failed to deserialize environment value " #PropertyName \
1811-
"\n"); \
1812-
} \
1813-
set_##PropertyName(field); \
1814-
i++; \
1815-
} \
1816-
id++; \
1817-
} while (0);
1818-
1819-
ENVIRONMENT_STRONG_PERSISTENT_VALUES(V);
1820-
#undef V
1821-
1822-
MaybeLocal<Context> maybe_ctx_from_snapshot =
1823-
ctx->GetDataFromSnapshotOnce<Context>(info->context);
1824-
Local<Context> ctx_from_snapshot;
1825-
if (!maybe_ctx_from_snapshot.ToLocal(&ctx_from_snapshot)) {
1826-
fprintf(stderr,
1827-
"Failed to deserialize context back reference from the snapshot\n");
1828-
}
1829-
CHECK_EQ(ctx_from_snapshot, ctx);
18301693
}
18311694

18321695
uint64_t GuessMemoryAvailableToTheProcess() {
@@ -2011,11 +1874,7 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
20111874
tracker->TrackField("async_hooks", async_hooks_);
20121875
tracker->TrackField("immediate_info", immediate_info_);
20131876
tracker->TrackField("tick_info", tick_info_);
2014-
2015-
#define V(PropertyName, TypeName) \
2016-
tracker->TrackField(#PropertyName, PropertyName());
2017-
ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)
2018-
#undef V
1877+
tracker->TrackField("principal_realm", principal_realm_);
20191878

20201879
// FIXME(joyeecheung): track other fields in Environment.
20211880
// Currently MemoryTracker is unable to track these
@@ -2164,12 +2023,14 @@ bool BaseObject::IsRootNode() const {
21642023
return !persistent_handle_.IsWeak();
21652024
}
21662025

2167-
Local<FunctionTemplate> BaseObject::GetConstructorTemplate(Environment* env) {
2168-
Local<FunctionTemplate> tmpl = env->base_object_ctor_template();
2026+
Local<FunctionTemplate> BaseObject::GetConstructorTemplate(
2027+
IsolateData* isolate_data) {
2028+
Local<FunctionTemplate> tmpl = isolate_data->base_object_ctor_template();
21692029
if (tmpl.IsEmpty()) {
2170-
tmpl = NewFunctionTemplate(env->isolate(), nullptr);
2171-
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "BaseObject"));
2172-
env->set_base_object_ctor_template(tmpl);
2030+
tmpl = NewFunctionTemplate(isolate_data->isolate(), nullptr);
2031+
tmpl->SetClassName(
2032+
FIXED_ONE_BYTE_STRING(isolate_data->isolate(), "BaseObject"));
2033+
isolate_data->set_base_object_ctor_template(tmpl);
21732034
}
21742035
return tmpl;
21752036
}

‎src/env.h

+33-455
Large diffs are not rendered by default.

‎src/env_properties.h

+433
Large diffs are not rendered by default.

‎src/node.cc

+3-153
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "node_options-inl.h"
3737
#include "node_perf.h"
3838
#include "node_process-inl.h"
39+
#include "node_realm-inl.h"
3940
#include "node_report.h"
4041
#include "node_revert.h"
4142
#include "node_snapshot_builder.h"
@@ -128,13 +129,10 @@ namespace node {
128129
using builtins::BuiltinLoader;
129130

130131
using v8::EscapableHandleScope;
131-
using v8::Function;
132132
using v8::Isolate;
133133
using v8::Local;
134134
using v8::MaybeLocal;
135135
using v8::Object;
136-
using v8::String;
137-
using v8::Undefined;
138136
using v8::V8;
139137
using v8::Value;
140138

@@ -171,36 +169,6 @@ void SignalExit(int signo, siginfo_t* info, void* ucontext) {
171169
}
172170
#endif // __POSIX__
173171

174-
MaybeLocal<Value> ExecuteBootstrapper(Environment* env,
175-
const char* id,
176-
std::vector<Local<Value>>* arguments) {
177-
EscapableHandleScope scope(env->isolate());
178-
MaybeLocal<Function> maybe_fn =
179-
BuiltinLoader::LookupAndCompile(env->context(), id, env);
180-
181-
Local<Function> fn;
182-
if (!maybe_fn.ToLocal(&fn)) {
183-
return MaybeLocal<Value>();
184-
}
185-
186-
MaybeLocal<Value> result = fn->Call(env->context(),
187-
Undefined(env->isolate()),
188-
arguments->size(),
189-
arguments->data());
190-
191-
// If there was an error during bootstrap, it must be unrecoverable
192-
// (e.g. max call stack exceeded). Clear the stack so that the
193-
// AsyncCallbackScope destructor doesn't fail on the id check.
194-
// There are only two ways to have a stack size > 1: 1) the user manually
195-
// called MakeCallback or 2) user awaited during bootstrap, which triggered
196-
// _tickCallback().
197-
if (result.IsEmpty()) {
198-
env->async_hooks()->clear_async_id_stack();
199-
}
200-
201-
return scope.EscapeMaybe(result);
202-
}
203-
204172
#if HAVE_INSPECTOR
205173
int Environment::InitializeInspector(
206174
std::unique_ptr<inspector::ParentInspectorHandle> parent_handle) {
@@ -295,129 +263,11 @@ void Environment::InitializeDiagnostics() {
295263
#endif
296264
}
297265

298-
MaybeLocal<Value> Environment::BootstrapInternalLoaders() {
299-
EscapableHandleScope scope(isolate_);
300-
301-
// Arguments must match the parameters specified in
302-
// BuiltinLoader::LookupAndCompile().
303-
std::vector<Local<Value>> loaders_args = {
304-
process_object(),
305-
NewFunctionTemplate(isolate_, binding::GetLinkedBinding)
306-
->GetFunction(context())
307-
.ToLocalChecked(),
308-
NewFunctionTemplate(isolate_, binding::GetInternalBinding)
309-
->GetFunction(context())
310-
.ToLocalChecked(),
311-
primordials()};
312-
313-
// Bootstrap internal loaders
314-
Local<Value> loader_exports;
315-
if (!ExecuteBootstrapper(this, "internal/bootstrap/loaders", &loaders_args)
316-
.ToLocal(&loader_exports)) {
317-
return MaybeLocal<Value>();
318-
}
319-
CHECK(loader_exports->IsObject());
320-
Local<Object> loader_exports_obj = loader_exports.As<Object>();
321-
Local<Value> internal_binding_loader =
322-
loader_exports_obj->Get(context(), internal_binding_string())
323-
.ToLocalChecked();
324-
CHECK(internal_binding_loader->IsFunction());
325-
set_internal_binding_loader(internal_binding_loader.As<Function>());
326-
Local<Value> require =
327-
loader_exports_obj->Get(context(), require_string()).ToLocalChecked();
328-
CHECK(require->IsFunction());
329-
set_builtin_module_require(require.As<Function>());
330-
331-
return scope.Escape(loader_exports);
332-
}
333-
334-
MaybeLocal<Value> Environment::BootstrapNode() {
335-
EscapableHandleScope scope(isolate_);
336-
337-
// Arguments must match the parameters specified in
338-
// BuiltinLoader::LookupAndCompile().
339-
// process, require, internalBinding, primordials
340-
std::vector<Local<Value>> node_args = {process_object(),
341-
builtin_module_require(),
342-
internal_binding_loader(),
343-
primordials()};
344-
345-
MaybeLocal<Value> result =
346-
ExecuteBootstrapper(this, "internal/bootstrap/node", &node_args);
347-
348-
if (result.IsEmpty()) {
349-
return MaybeLocal<Value>();
350-
}
351-
352-
if (!no_browser_globals()) {
353-
result =
354-
ExecuteBootstrapper(this, "internal/bootstrap/browser", &node_args);
355-
356-
if (result.IsEmpty()) {
357-
return MaybeLocal<Value>();
358-
}
359-
}
360-
361-
// TODO(joyeecheung): skip these in the snapshot building for workers.
362-
auto thread_switch_id =
363-
is_main_thread() ? "internal/bootstrap/switches/is_main_thread"
364-
: "internal/bootstrap/switches/is_not_main_thread";
365-
result = ExecuteBootstrapper(this, thread_switch_id, &node_args);
366-
367-
if (result.IsEmpty()) {
368-
return MaybeLocal<Value>();
369-
}
370-
371-
auto process_state_switch_id =
372-
owns_process_state()
373-
? "internal/bootstrap/switches/does_own_process_state"
374-
: "internal/bootstrap/switches/does_not_own_process_state";
375-
result = ExecuteBootstrapper(this, process_state_switch_id, &node_args);
376-
377-
if (result.IsEmpty()) {
378-
return MaybeLocal<Value>();
379-
}
380-
381-
Local<String> env_string = FIXED_ONE_BYTE_STRING(isolate_, "env");
382-
Local<Object> env_var_proxy;
383-
if (!CreateEnvVarProxy(context(), isolate_).ToLocal(&env_var_proxy) ||
384-
process_object()->Set(context(), env_string, env_var_proxy).IsNothing()) {
385-
return MaybeLocal<Value>();
386-
}
387-
388-
return scope.EscapeMaybe(result);
389-
}
390-
391-
MaybeLocal<Value> Environment::RunBootstrapping() {
392-
EscapableHandleScope scope(isolate_);
393-
394-
CHECK(!has_run_bootstrapping_code());
395-
396-
if (BootstrapInternalLoaders().IsEmpty()) {
397-
return MaybeLocal<Value>();
398-
}
399-
400-
Local<Value> result;
401-
if (!BootstrapNode().ToLocal(&result)) {
402-
return MaybeLocal<Value>();
403-
}
404-
405-
// Make sure that no request or handle is created during bootstrap -
406-
// if necessary those should be done in pre-execution.
407-
// Usually, doing so would trigger the checks present in the ReqWrap and
408-
// HandleWrap classes, so this is only a consistency check.
409-
CHECK(req_wrap_queue()->IsEmpty());
410-
CHECK(handle_wrap_queue()->IsEmpty());
411-
412-
DoneBootstrapping();
413-
414-
return scope.Escape(result);
415-
}
416-
417266
static
418267
MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) {
419268
EscapableHandleScope scope(env->isolate());
420269
CHECK_NOT_NULL(main_script_id);
270+
Realm* realm = env->principal_realm();
421271

422272
// Arguments must match the parameters specified in
423273
// BuiltinLoader::LookupAndCompile().
@@ -427,7 +277,7 @@ MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) {
427277
env->primordials()};
428278

429279
return scope.EscapeMaybe(
430-
ExecuteBootstrapper(env, main_script_id, &arguments));
280+
realm->ExecuteBootstrapper(main_script_id, &arguments));
431281
}
432282

433283
MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {

‎src/node_context_data.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,15 @@ namespace node {
3636
#define NODE_CONTEXT_CONTEXTIFY_CONTEXT_INDEX 37
3737
#endif
3838

39+
#ifndef NODE_CONTEXT_REALM_INDEX
40+
#define NODE_CONTEXT_REALM_INDEX 38
41+
#endif
42+
3943
// NODE_CONTEXT_TAG must be greater than any embedder indexes so that a single
4044
// check on the number of embedder data fields can assure the presence of all
4145
// embedder indexes.
4246
#ifndef NODE_CONTEXT_TAG
43-
#define NODE_CONTEXT_TAG 38
47+
#define NODE_CONTEXT_TAG 39
4448
#endif
4549

4650
enum ContextEmbedderIndex {
@@ -51,6 +55,7 @@ enum ContextEmbedderIndex {
5155
kAllowCodeGenerationFromStrings =
5256
NODE_CONTEXT_ALLOW_CODE_GENERATION_FROM_STRINGS_INDEX,
5357
kContextifyContext = NODE_CONTEXT_CONTEXTIFY_CONTEXT_INDEX,
58+
kRealm = NODE_CONTEXT_REALM_INDEX,
5459
kContextTag = NODE_CONTEXT_TAG,
5560
};
5661

‎src/node_contextify.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ bool ContextifyContext::InitializeContext(Local<Context> ctx,
278278
}
279279
}
280280

281-
env->AssignToContext(ctx, info);
281+
env->AssignToContext(ctx, nullptr, info);
282282

283283
// This should only be done after the initial initializations of the context
284284
// global object is finished.

‎src/node_internals.h

-4
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,6 @@ v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params,
307307
v8::MaybeLocal<v8::Value> StartExecution(Environment* env,
308308
StartExecutionCallback cb = nullptr);
309309
v8::MaybeLocal<v8::Object> GetPerContextExports(v8::Local<v8::Context> context);
310-
v8::MaybeLocal<v8::Value> ExecuteBootstrapper(
311-
Environment* env,
312-
const char* id,
313-
std::vector<v8::Local<v8::Value>>* arguments);
314310
void MarkBootstrapComplete(const v8::FunctionCallbackInfo<v8::Value>& args);
315311

316312
class InitializationResultImpl final : public InitializationResult {

‎src/node_main_instance.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "node_external_reference.h"
99
#include "node_internals.h"
1010
#include "node_options-inl.h"
11+
#include "node_realm.h"
1112
#include "node_snapshot_builder.h"
1213
#include "node_snapshotable.h"
1314
#include "node_v8_platform-inl.h"
@@ -181,7 +182,6 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) {
181182
#if HAVE_INSPECTOR
182183
env->InitializeInspector({});
183184
#endif
184-
env->DoneBootstrapping();
185185

186186
#if HAVE_OPENSSL
187187
crypto::InitCryptoOnce(isolate_);
@@ -200,7 +200,7 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) {
200200
#if HAVE_INSPECTOR
201201
env->InitializeInspector({});
202202
#endif
203-
if (env->RunBootstrapping().IsEmpty()) {
203+
if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
204204
return nullptr;
205205
}
206206
}

‎src/node_process.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace node {
1212
class Environment;
1313
class MemoryTracker;
1414
class ExternalReferenceRegistry;
15+
class Realm;
1516

1617
v8::MaybeLocal<v8::Object> CreateEnvVarProxy(v8::Local<v8::Context> context,
1718
v8::Isolate* isolate);
@@ -41,7 +42,7 @@ v8::Maybe<bool> ProcessEmitDeprecationWarning(Environment* env,
4142
const char* warning,
4243
const char* deprecation_code);
4344

44-
v8::MaybeLocal<v8::Object> CreateProcessObject(Environment* env);
45+
v8::MaybeLocal<v8::Object> CreateProcessObject(Realm* env);
4546
void PatchProcessObject(const v8::FunctionCallbackInfo<v8::Value>& args);
4647

4748
namespace process {

‎src/node_process_object.cc

+11-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "node_metadata.h"
66
#include "node_options-inl.h"
77
#include "node_process-inl.h"
8+
#include "node_realm-inl.h"
89
#include "node_revert.h"
910
#include "util-inl.h"
1011

@@ -77,13 +78,13 @@ static void GetParentProcessId(Local<Name> property,
7778
info.GetReturnValue().Set(uv_os_getppid());
7879
}
7980

80-
MaybeLocal<Object> CreateProcessObject(Environment* env) {
81-
Isolate* isolate = env->isolate();
81+
MaybeLocal<Object> CreateProcessObject(Realm* realm) {
82+
Isolate* isolate = realm->isolate();
8283
EscapableHandleScope scope(isolate);
83-
Local<Context> context = env->context();
84+
Local<Context> context = realm->context();
8485

8586
Local<FunctionTemplate> process_template = FunctionTemplate::New(isolate);
86-
process_template->SetClassName(env->process_string());
87+
process_template->SetClassName(realm->env()->process_string());
8788
Local<Function> process_ctor;
8889
Local<Object> process;
8990
if (!process_template->GetFunction(context).ToLocal(&process_ctor) ||
@@ -94,19 +95,18 @@ MaybeLocal<Object> CreateProcessObject(Environment* env) {
9495
// process[exiting_aliased_Uint32Array]
9596
if (process
9697
->SetPrivate(context,
97-
env->exiting_aliased_Uint32Array(),
98-
env->exiting().GetJSArray())
98+
realm->env()->exiting_aliased_Uint32Array(),
99+
realm->env()->exiting().GetJSArray())
99100
.IsNothing()) {
100101
return {};
101102
}
102103

103104
// process.version
104-
READONLY_PROPERTY(process,
105-
"version",
106-
FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION));
105+
READONLY_PROPERTY(
106+
process, "version", FIXED_ONE_BYTE_STRING(isolate, NODE_VERSION));
107107

108108
// process.versions
109-
Local<Object> versions = Object::New(env->isolate());
109+
Local<Object> versions = Object::New(isolate);
110110
READONLY_PROPERTY(process, "versions", versions);
111111

112112
#define V(key) \
@@ -124,7 +124,7 @@ MaybeLocal<Object> CreateProcessObject(Environment* env) {
124124
READONLY_STRING_PROPERTY(process, "platform", per_process::metadata.platform);
125125

126126
// process.release
127-
Local<Object> release = Object::New(env->isolate());
127+
Local<Object> release = Object::New(isolate);
128128
READONLY_PROPERTY(process, "release", release);
129129
READONLY_STRING_PROPERTY(release, "name", per_process::metadata.release.name);
130130
#if NODE_VERSION_IS_LTS

‎src/node_realm-inl.h

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#ifndef SRC_NODE_REALM_INL_H_
2+
#define SRC_NODE_REALM_INL_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "node_realm.h"
7+
8+
namespace node {
9+
10+
inline Realm* Realm::GetCurrent(v8::Isolate* isolate) {
11+
if (UNLIKELY(!isolate->InContext())) return nullptr;
12+
v8::HandleScope handle_scope(isolate);
13+
return GetCurrent(isolate->GetCurrentContext());
14+
}
15+
16+
inline Realm* Realm::GetCurrent(v8::Local<v8::Context> context) {
17+
if (UNLIKELY(!ContextEmbedderTag::IsNodeContext(context))) return nullptr;
18+
return static_cast<Realm*>(
19+
context->GetAlignedPointerFromEmbedderData(ContextEmbedderIndex::kRealm));
20+
}
21+
22+
inline Realm* Realm::GetCurrent(
23+
const v8::FunctionCallbackInfo<v8::Value>& info) {
24+
return GetCurrent(info.GetIsolate()->GetCurrentContext());
25+
}
26+
27+
template <typename T>
28+
inline Realm* Realm::GetCurrent(const v8::PropertyCallbackInfo<T>& info) {
29+
return GetCurrent(info.GetIsolate()->GetCurrentContext());
30+
}
31+
32+
inline Environment* Realm::env() const {
33+
return env_;
34+
}
35+
36+
inline v8::Isolate* Realm::isolate() const {
37+
return isolate_;
38+
}
39+
40+
inline bool Realm::has_run_bootstrapping_code() const {
41+
return has_run_bootstrapping_code_;
42+
}
43+
44+
#define V(PropertyName, TypeName) \
45+
inline v8::Local<TypeName> Realm::PropertyName() const { \
46+
return PersistentToLocal::Strong(PropertyName##_); \
47+
} \
48+
inline void Realm::set_##PropertyName(v8::Local<TypeName> value) { \
49+
PropertyName##_.Reset(isolate(), value); \
50+
}
51+
PER_REALM_STRONG_PERSISTENT_VALUES(V)
52+
#undef V
53+
54+
v8::Local<v8::Context> Realm::context() const {
55+
return PersistentToLocal::Strong(context_);
56+
}
57+
58+
} // namespace node
59+
60+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
61+
62+
#endif // SRC_NODE_REALM_INL_H_

‎src/node_realm.cc

+308
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
#include "node_realm.h"
2+
#include "env-inl.h"
3+
4+
#include "memory_tracker-inl.h"
5+
#include "node_builtins.h"
6+
#include "node_process.h"
7+
#include "util.h"
8+
9+
namespace node {
10+
11+
using builtins::BuiltinLoader;
12+
using v8::Context;
13+
using v8::EscapableHandleScope;
14+
using v8::Function;
15+
using v8::HandleScope;
16+
using v8::Local;
17+
using v8::MaybeLocal;
18+
using v8::Object;
19+
using v8::SnapshotCreator;
20+
using v8::String;
21+
using v8::Undefined;
22+
using v8::Value;
23+
24+
Realm::Realm(Environment* env,
25+
v8::Local<v8::Context> context,
26+
const RealmSerializeInfo* realm_info)
27+
: env_(env), isolate_(context->GetIsolate()) {
28+
context_.Reset(isolate_, context);
29+
30+
// Create properties if not deserializing from snapshot.
31+
// Or the properties are deserialized with DeserializeProperties() when the
32+
// env drained the deserialize requests.
33+
if (realm_info == nullptr) {
34+
CreateProperties();
35+
}
36+
}
37+
38+
void Realm::MemoryInfo(MemoryTracker* tracker) const {
39+
#define V(PropertyName, TypeName) \
40+
tracker->TrackField(#PropertyName, PropertyName());
41+
PER_REALM_STRONG_PERSISTENT_VALUES(V)
42+
#undef V
43+
44+
tracker->TrackField("env", env_);
45+
}
46+
47+
void Realm::CreateProperties() {
48+
HandleScope handle_scope(isolate_);
49+
Local<Context> ctx = context();
50+
51+
// Store primordials setup by the per-context script in the environment.
52+
Local<Object> per_context_bindings =
53+
GetPerContextExports(ctx).ToLocalChecked();
54+
Local<Value> primordials =
55+
per_context_bindings->Get(ctx, env_->primordials_string())
56+
.ToLocalChecked();
57+
CHECK(primordials->IsObject());
58+
set_primordials(primordials.As<Object>());
59+
60+
Local<String> prototype_string =
61+
FIXED_ONE_BYTE_STRING(isolate(), "prototype");
62+
63+
#define V(EnvPropertyName, PrimordialsPropertyName) \
64+
{ \
65+
Local<Value> ctor = \
66+
primordials.As<Object>() \
67+
->Get(ctx, \
68+
FIXED_ONE_BYTE_STRING(isolate(), PrimordialsPropertyName)) \
69+
.ToLocalChecked(); \
70+
CHECK(ctor->IsObject()); \
71+
Local<Value> prototype = \
72+
ctor.As<Object>()->Get(ctx, prototype_string).ToLocalChecked(); \
73+
CHECK(prototype->IsObject()); \
74+
set_##EnvPropertyName(prototype.As<Object>()); \
75+
}
76+
77+
V(primordials_safe_map_prototype_object, "SafeMap");
78+
V(primordials_safe_set_prototype_object, "SafeSet");
79+
V(primordials_safe_weak_map_prototype_object, "SafeWeakMap");
80+
V(primordials_safe_weak_set_prototype_object, "SafeWeakSet");
81+
#undef V
82+
83+
// TODO(legendecas): some methods probably doesn't need to be created with
84+
// process. Distinguish them and create process object only in the principal
85+
// realm.
86+
Local<Object> process_object =
87+
node::CreateProcessObject(this).FromMaybe(Local<Object>());
88+
set_process_object(process_object);
89+
}
90+
91+
RealmSerializeInfo Realm::Serialize(SnapshotCreator* creator) {
92+
RealmSerializeInfo info;
93+
Local<Context> ctx = context();
94+
95+
uint32_t id = 0;
96+
#define V(PropertyName, TypeName) \
97+
do { \
98+
Local<TypeName> field = PropertyName(); \
99+
if (!field.IsEmpty()) { \
100+
size_t index = creator->AddData(ctx, field); \
101+
info.persistent_values.push_back({#PropertyName, id, index}); \
102+
} \
103+
id++; \
104+
} while (0);
105+
PER_REALM_STRONG_PERSISTENT_VALUES(V)
106+
#undef V
107+
108+
info.context = creator->AddData(ctx, ctx);
109+
return info;
110+
}
111+
112+
void Realm::DeserializeProperties(const RealmSerializeInfo* info) {
113+
Local<Context> ctx = context();
114+
115+
const std::vector<PropInfo>& values = info->persistent_values;
116+
size_t i = 0; // index to the array
117+
uint32_t id = 0;
118+
#define V(PropertyName, TypeName) \
119+
do { \
120+
if (values.size() > i && id == values[i].id) { \
121+
const PropInfo& d = values[i]; \
122+
DCHECK_EQ(d.name, #PropertyName); \
123+
MaybeLocal<TypeName> maybe_field = \
124+
ctx->GetDataFromSnapshotOnce<TypeName>(d.index); \
125+
Local<TypeName> field; \
126+
if (!maybe_field.ToLocal(&field)) { \
127+
fprintf(stderr, \
128+
"Failed to deserialize realm value " #PropertyName "\n"); \
129+
} \
130+
set_##PropertyName(field); \
131+
i++; \
132+
} \
133+
id++; \
134+
} while (0);
135+
136+
PER_REALM_STRONG_PERSISTENT_VALUES(V);
137+
#undef V
138+
139+
MaybeLocal<Context> maybe_ctx_from_snapshot =
140+
ctx->GetDataFromSnapshotOnce<Context>(info->context);
141+
Local<Context> ctx_from_snapshot;
142+
if (!maybe_ctx_from_snapshot.ToLocal(&ctx_from_snapshot)) {
143+
fprintf(stderr,
144+
"Failed to deserialize context back reference from the snapshot\n");
145+
}
146+
CHECK_EQ(ctx_from_snapshot, ctx);
147+
148+
DoneBootstrapping();
149+
}
150+
151+
MaybeLocal<Value> Realm::ExecuteBootstrapper(
152+
const char* id, std::vector<Local<Value>>* arguments) {
153+
EscapableHandleScope scope(isolate());
154+
Local<Context> ctx = context();
155+
MaybeLocal<Function> maybe_fn =
156+
BuiltinLoader::LookupAndCompile(ctx, id, env());
157+
158+
Local<Function> fn;
159+
if (!maybe_fn.ToLocal(&fn)) {
160+
return MaybeLocal<Value>();
161+
}
162+
163+
MaybeLocal<Value> result =
164+
fn->Call(ctx, Undefined(isolate()), arguments->size(), arguments->data());
165+
166+
// If there was an error during bootstrap, it must be unrecoverable
167+
// (e.g. max call stack exceeded). Clear the stack so that the
168+
// AsyncCallbackScope destructor doesn't fail on the id check.
169+
// There are only two ways to have a stack size > 1: 1) the user manually
170+
// called MakeCallback or 2) user awaited during bootstrap, which triggered
171+
// _tickCallback().
172+
if (result.IsEmpty()) {
173+
env()->async_hooks()->clear_async_id_stack();
174+
}
175+
176+
return scope.EscapeMaybe(result);
177+
}
178+
179+
MaybeLocal<Value> Realm::BootstrapInternalLoaders() {
180+
EscapableHandleScope scope(isolate_);
181+
182+
// Arguments must match the parameters specified in
183+
// BuiltinLoader::LookupAndCompile().
184+
std::vector<Local<Value>> loaders_args = {
185+
process_object(),
186+
NewFunctionTemplate(isolate_, binding::GetLinkedBinding)
187+
->GetFunction(context())
188+
.ToLocalChecked(),
189+
NewFunctionTemplate(isolate_, binding::GetInternalBinding)
190+
->GetFunction(context())
191+
.ToLocalChecked(),
192+
primordials()};
193+
194+
// Bootstrap internal loaders
195+
Local<Value> loader_exports;
196+
if (!ExecuteBootstrapper("internal/bootstrap/loaders", &loaders_args)
197+
.ToLocal(&loader_exports)) {
198+
return MaybeLocal<Value>();
199+
}
200+
CHECK(loader_exports->IsObject());
201+
Local<Object> loader_exports_obj = loader_exports.As<Object>();
202+
Local<Value> internal_binding_loader =
203+
loader_exports_obj->Get(context(), env_->internal_binding_string())
204+
.ToLocalChecked();
205+
CHECK(internal_binding_loader->IsFunction());
206+
set_internal_binding_loader(internal_binding_loader.As<Function>());
207+
Local<Value> require =
208+
loader_exports_obj->Get(context(), env_->require_string())
209+
.ToLocalChecked();
210+
CHECK(require->IsFunction());
211+
set_builtin_module_require(require.As<Function>());
212+
213+
return scope.Escape(loader_exports);
214+
}
215+
216+
MaybeLocal<Value> Realm::BootstrapNode() {
217+
EscapableHandleScope scope(isolate_);
218+
219+
// Arguments must match the parameters specified in
220+
// BuiltinLoader::LookupAndCompile().
221+
// process, require, internalBinding, primordials
222+
std::vector<Local<Value>> node_args = {process_object(),
223+
builtin_module_require(),
224+
internal_binding_loader(),
225+
primordials()};
226+
227+
MaybeLocal<Value> result =
228+
ExecuteBootstrapper("internal/bootstrap/node", &node_args);
229+
230+
if (result.IsEmpty()) {
231+
return MaybeLocal<Value>();
232+
}
233+
234+
if (!env_->no_browser_globals()) {
235+
result = ExecuteBootstrapper("internal/bootstrap/browser", &node_args);
236+
237+
if (result.IsEmpty()) {
238+
return MaybeLocal<Value>();
239+
}
240+
}
241+
242+
// TODO(joyeecheung): skip these in the snapshot building for workers.
243+
auto thread_switch_id =
244+
env_->is_main_thread() ? "internal/bootstrap/switches/is_main_thread"
245+
: "internal/bootstrap/switches/is_not_main_thread";
246+
result = ExecuteBootstrapper(thread_switch_id, &node_args);
247+
248+
if (result.IsEmpty()) {
249+
return MaybeLocal<Value>();
250+
}
251+
252+
auto process_state_switch_id =
253+
env_->owns_process_state()
254+
? "internal/bootstrap/switches/does_own_process_state"
255+
: "internal/bootstrap/switches/does_not_own_process_state";
256+
result = ExecuteBootstrapper(process_state_switch_id, &node_args);
257+
258+
if (result.IsEmpty()) {
259+
return MaybeLocal<Value>();
260+
}
261+
262+
Local<String> env_string = FIXED_ONE_BYTE_STRING(isolate_, "env");
263+
Local<Object> env_var_proxy;
264+
if (!CreateEnvVarProxy(context(), isolate_).ToLocal(&env_var_proxy) ||
265+
process_object()->Set(context(), env_string, env_var_proxy).IsNothing()) {
266+
return MaybeLocal<Value>();
267+
}
268+
269+
return scope.EscapeMaybe(result);
270+
}
271+
272+
MaybeLocal<Value> Realm::RunBootstrapping() {
273+
EscapableHandleScope scope(isolate_);
274+
275+
CHECK(!has_run_bootstrapping_code());
276+
277+
if (BootstrapInternalLoaders().IsEmpty()) {
278+
return MaybeLocal<Value>();
279+
}
280+
281+
Local<Value> result;
282+
if (!BootstrapNode().ToLocal(&result)) {
283+
return MaybeLocal<Value>();
284+
}
285+
286+
DoneBootstrapping();
287+
288+
return scope.Escape(result);
289+
}
290+
291+
void Realm::DoneBootstrapping() {
292+
has_run_bootstrapping_code_ = true;
293+
294+
// Make sure that no request or handle is created during bootstrap -
295+
// if necessary those should be done in pre-execution.
296+
// Usually, doing so would trigger the checks present in the ReqWrap and
297+
// HandleWrap classes, so this is only a consistency check.
298+
299+
// TODO(legendecas): track req_wrap and handle_wrap by realms instead of
300+
// environments.
301+
CHECK(env_->req_wrap_queue()->IsEmpty());
302+
CHECK(env_->handle_wrap_queue()->IsEmpty());
303+
304+
// TODO(legendecas): track base object count by realms.
305+
env_->set_base_object_created_by_bootstrap(env_->base_object_count());
306+
}
307+
308+
} // namespace node

‎src/node_realm.h

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#ifndef SRC_NODE_REALM_H_
2+
#define SRC_NODE_REALM_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include <v8.h>
7+
#include "env_properties.h"
8+
#include "memory_tracker.h"
9+
#include "node_snapshotable.h"
10+
11+
namespace node {
12+
13+
struct RealmSerializeInfo {
14+
std::vector<PropInfo> persistent_values;
15+
16+
SnapshotIndex context;
17+
friend std::ostream& operator<<(std::ostream& o, const RealmSerializeInfo& i);
18+
};
19+
20+
/**
21+
* node::Realm is a container for a set of JavaScript objects and functions
22+
* that associated with a particular global environment.
23+
*
24+
* An ECMAScript realm (https://tc39.es/ecma262/#sec-code-realms) representing
25+
* a global environment in which script is run. Each ECMAScript realm comes
26+
* with a global object and a set of intrinsic objects. An ECMAScript realm has
27+
* a [[HostDefined]] field, which contains the node::Realm object.
28+
*
29+
* Realm can be a principal realm or a synthetic realm. A principal realm is
30+
* created with an Environment as its principal global environment to evaluate
31+
* scripts. A synthetic realm is created with JS APIs like ShadowRealm.
32+
*
33+
* Native bindings and builtin modules can be evaluated in either a principal
34+
* realm or a synthetic realm.
35+
*/
36+
class Realm : public MemoryRetainer {
37+
public:
38+
static inline Realm* GetCurrent(v8::Isolate* isolate);
39+
static inline Realm* GetCurrent(v8::Local<v8::Context> context);
40+
static inline Realm* GetCurrent(
41+
const v8::FunctionCallbackInfo<v8::Value>& info);
42+
template <typename T>
43+
static inline Realm* GetCurrent(const v8::PropertyCallbackInfo<T>& info);
44+
45+
Realm(Environment* env,
46+
v8::Local<v8::Context> context,
47+
const RealmSerializeInfo* realm_info);
48+
~Realm() = default;
49+
50+
Realm(const Realm&) = delete;
51+
Realm& operator=(const Realm&) = delete;
52+
Realm(Realm&&) = delete;
53+
Realm& operator=(Realm&&) = delete;
54+
55+
SET_MEMORY_INFO_NAME(Realm)
56+
SET_SELF_SIZE(Realm);
57+
void MemoryInfo(MemoryTracker* tracker) const override;
58+
59+
void CreateProperties();
60+
RealmSerializeInfo Serialize(v8::SnapshotCreator* creator);
61+
void DeserializeProperties(const RealmSerializeInfo* info);
62+
63+
v8::MaybeLocal<v8::Value> ExecuteBootstrapper(
64+
const char* id, std::vector<v8::Local<v8::Value>>* arguments);
65+
v8::MaybeLocal<v8::Value> BootstrapInternalLoaders();
66+
v8::MaybeLocal<v8::Value> BootstrapNode();
67+
v8::MaybeLocal<v8::Value> RunBootstrapping();
68+
69+
inline Environment* env() const;
70+
inline v8::Isolate* isolate() const;
71+
inline v8::Local<v8::Context> context() const;
72+
inline bool has_run_bootstrapping_code() const;
73+
74+
#define V(PropertyName, TypeName) \
75+
inline v8::Local<TypeName> PropertyName() const; \
76+
inline void set_##PropertyName(v8::Local<TypeName> value);
77+
PER_REALM_STRONG_PERSISTENT_VALUES(V)
78+
#undef V
79+
80+
private:
81+
void InitializeContext(v8::Local<v8::Context> context,
82+
const RealmSerializeInfo* realm_info);
83+
void DoneBootstrapping();
84+
85+
Environment* env_;
86+
// Shorthand for isolate pointer.
87+
v8::Isolate* isolate_;
88+
v8::Global<v8::Context> context_;
89+
bool has_run_bootstrapping_code_ = false;
90+
91+
#define V(PropertyName, TypeName) v8::Global<TypeName> PropertyName##_;
92+
PER_REALM_STRONG_PERSISTENT_VALUES(V)
93+
#undef V
94+
};
95+
96+
} // namespace node
97+
98+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
99+
100+
#endif // SRC_NODE_REALM_H_

‎src/node_snapshotable.cc

+90-5
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,69 @@ std::ostream& operator<<(std::ostream& output,
7171
return output;
7272
}
7373

74+
std::ostream& operator<<(std::ostream& output,
75+
const std::vector<PropInfo>& vec) {
76+
output << "{\n";
77+
for (const auto& info : vec) {
78+
output << " " << info << ",\n";
79+
}
80+
output << "}";
81+
return output;
82+
}
83+
84+
std::ostream& operator<<(std::ostream& output, const PropInfo& info) {
85+
output << "{ \"" << info.name << "\", " << std::to_string(info.id) << ", "
86+
<< std::to_string(info.index) << " }";
87+
return output;
88+
}
89+
90+
std::ostream& operator<<(std::ostream& output,
91+
const std::vector<std::string>& vec) {
92+
output << "{\n";
93+
for (const auto& info : vec) {
94+
output << " \"" << info << "\",\n";
95+
}
96+
output << "}";
97+
return output;
98+
}
99+
100+
std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i) {
101+
output << "{\n"
102+
<< "// -- persistent_values begins --\n"
103+
<< i.persistent_values << ",\n"
104+
<< "// -- persistent_values ends --\n"
105+
<< i.context << ", // context\n"
106+
<< "}";
107+
return output;
108+
}
109+
110+
std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
111+
output << "{\n"
112+
<< "// -- native_objects begins --\n"
113+
<< i.native_objects << ",\n"
114+
<< "// -- native_objects ends --\n"
115+
<< "// -- builtins begins --\n"
116+
<< i.builtins << ",\n"
117+
<< "// -- builtins ends --\n"
118+
<< "// -- async_hooks begins --\n"
119+
<< i.async_hooks << ",\n"
120+
<< "// -- async_hooks ends --\n"
121+
<< i.tick_info << ", // tick_info\n"
122+
<< i.immediate_info << ", // immediate_info\n"
123+
<< "// -- performance_state begins --\n"
124+
<< i.performance_state << ",\n"
125+
<< "// -- performance_state ends --\n"
126+
<< i.exiting << ", // exiting\n"
127+
<< i.stream_base_state << ", // stream_base_state\n"
128+
<< i.should_abort_on_uncaught_toggle
129+
<< ", // should_abort_on_uncaught_toggle\n"
130+
<< "// -- principal_realm begins --\n"
131+
<< i.principal_realm << ",\n"
132+
<< "// -- principal_realm ends --\n"
133+
<< "}";
134+
return output;
135+
}
136+
74137
class FileIO {
75138
public:
76139
explicit FileIO(FILE* file)
@@ -637,6 +700,30 @@ size_t FileWriter::Write(const IsolateDataSerializeInfo& data) {
637700
return written_total;
638701
}
639702

703+
template <>
704+
RealmSerializeInfo FileReader::Read() {
705+
per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<RealmSerializeInfo>()\n");
706+
RealmSerializeInfo result;
707+
result.persistent_values = ReadVector<PropInfo>();
708+
result.context = Read<SnapshotIndex>();
709+
return result;
710+
}
711+
712+
template <>
713+
size_t FileWriter::Write(const RealmSerializeInfo& data) {
714+
if (is_debug) {
715+
std::string str = ToStr(data);
716+
Debug("\nWrite<RealmSerializeInfo>() %s\n", str.c_str());
717+
}
718+
719+
// Use += here to ensure order of evaluation.
720+
size_t written_total = WriteVector<PropInfo>(data.persistent_values);
721+
written_total += Write<SnapshotIndex>(data.context);
722+
723+
Debug("Write<RealmSerializeInfo>() wrote %d bytes\n", written_total);
724+
return written_total;
725+
}
726+
640727
template <>
641728
EnvSerializeInfo FileReader::Read() {
642729
per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n");
@@ -651,8 +738,7 @@ EnvSerializeInfo FileReader::Read() {
651738
result.exiting = Read<AliasedBufferIndex>();
652739
result.stream_base_state = Read<AliasedBufferIndex>();
653740
result.should_abort_on_uncaught_toggle = Read<AliasedBufferIndex>();
654-
result.persistent_values = ReadVector<PropInfo>();
655-
result.context = Read<SnapshotIndex>();
741+
result.principal_realm = Read<RealmSerializeInfo>();
656742
return result;
657743
}
658744

@@ -675,8 +761,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
675761
written_total += Write<AliasedBufferIndex>(data.stream_base_state);
676762
written_total +=
677763
Write<AliasedBufferIndex>(data.should_abort_on_uncaught_toggle);
678-
written_total += WriteVector<PropInfo>(data.persistent_values);
679-
written_total += Write<SnapshotIndex>(data.context);
764+
written_total += Write<RealmSerializeInfo>(data.principal_realm);
680765

681766
Debug("Write<EnvSerializeInfo>() wrote %d bytes\n", written_total);
682767
return written_total;
@@ -1083,7 +1168,7 @@ int SnapshotBuilder::Generate(SnapshotData* out,
10831168
{});
10841169

10851170
// Run scripts in lib/internal/bootstrap/
1086-
if (env->RunBootstrapping().IsEmpty()) {
1171+
if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
10871172
return BOOTSTRAP_ERROR;
10881173
}
10891174
// If --build-snapshot is true, lib/internal/main/mksnapshot.js would be

‎src/node_snapshotable.h

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ struct EnvSerializeInfo;
1414
struct SnapshotData;
1515
class ExternalReferenceRegistry;
1616

17+
using SnapshotIndex = size_t;
18+
19+
struct PropInfo {
20+
std::string name; // name for debugging
21+
uint32_t id; // In the list - in case there are any empty entries
22+
SnapshotIndex index; // In the snapshot
23+
};
24+
1725
#define SERIALIZABLE_OBJECT_TYPES(V) \
1826
V(fs_binding_data, fs::BindingData) \
1927
V(v8_binding_data, v8_utils::BindingData) \

‎test/pummel/test-heapdump-env.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ const context = require('vm').createScript('const foo = 123');
1616
validateSnapshotNodes('Node / Environment', [{
1717
children: [
1818
{ node_name: 'Node / CleanupQueue', edge_name: 'cleanup_queue' },
19-
{ node_name: 'process', edge_name: 'process_object' },
2019
{ node_name: 'Node / IsolateData', edge_name: 'isolate_data' },
20+
{ node_name: 'Node / Realm', edge_name: 'principal_realm' },
2121
]
2222
}]);
2323

@@ -27,4 +27,10 @@ validateSnapshotNodes('Node / CleanupQueue', [{
2727
]
2828
}]);
2929

30+
validateSnapshotNodes('Node / Realm', [{
31+
children: [
32+
{ node_name: 'process', edge_name: 'process_object' },
33+
]
34+
}]);
35+
3036
console.log(context); // Make sure it's not GC'ed

0 commit comments

Comments
 (0)
Please sign in to comment.