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
Realm(Environment * env,v8::Local<v8::Context> context,const RealmSerializeInfo * realm_info)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
~Realm()38 Realm::~Realm() {
39 CHECK_EQ(base_object_count_, 0);
40 }
41
MemoryInfo(MemoryTracker * tracker) const42 void Realm::MemoryInfo(MemoryTracker* tracker) const {
43 #define V(PropertyName, TypeName) \
44 tracker->TrackField(#PropertyName, PropertyName());
45 PER_REALM_STRONG_PERSISTENT_VALUES(V)
46 #undef V
47
48 tracker->TrackField("env", env_);
49 tracker->TrackField("cleanup_queue", cleanup_queue_);
50 }
51
CreateProperties()52 void Realm::CreateProperties() {
53 HandleScope handle_scope(isolate_);
54 Local<Context> ctx = context();
55
56 // Store primordials setup by the per-context script in the environment.
57 Local<Object> per_context_bindings =
58 GetPerContextExports(ctx).ToLocalChecked();
59 Local<Value> primordials =
60 per_context_bindings->Get(ctx, env_->primordials_string())
61 .ToLocalChecked();
62 CHECK(primordials->IsObject());
63 set_primordials(primordials.As<Object>());
64
65 Local<String> prototype_string =
66 FIXED_ONE_BYTE_STRING(isolate(), "prototype");
67
68 #define V(EnvPropertyName, PrimordialsPropertyName) \
69 { \
70 Local<Value> ctor = \
71 primordials.As<Object>() \
72 ->Get(ctx, \
73 FIXED_ONE_BYTE_STRING(isolate(), PrimordialsPropertyName)) \
74 .ToLocalChecked(); \
75 CHECK(ctor->IsObject()); \
76 Local<Value> prototype = \
77 ctor.As<Object>()->Get(ctx, prototype_string).ToLocalChecked(); \
78 CHECK(prototype->IsObject()); \
79 set_##EnvPropertyName(prototype.As<Object>()); \
80 }
81
82 V(primordials_safe_map_prototype_object, "SafeMap");
83 V(primordials_safe_set_prototype_object, "SafeSet");
84 V(primordials_safe_weak_map_prototype_object, "SafeWeakMap");
85 V(primordials_safe_weak_set_prototype_object, "SafeWeakSet");
86 #undef V
87
88 // TODO(legendecas): some methods probably doesn't need to be created with
89 // process. Distinguish them and create process object only in the principal
90 // realm.
91 Local<Object> process_object =
92 node::CreateProcessObject(this).FromMaybe(Local<Object>());
93 set_process_object(process_object);
94 }
95
Serialize(SnapshotCreator * creator)96 RealmSerializeInfo Realm::Serialize(SnapshotCreator* creator) {
97 RealmSerializeInfo info;
98 Local<Context> ctx = context();
99
100 uint32_t id = 0;
101 #define V(PropertyName, TypeName) \
102 do { \
103 Local<TypeName> field = PropertyName(); \
104 if (!field.IsEmpty()) { \
105 size_t index = creator->AddData(ctx, field); \
106 info.persistent_values.push_back({#PropertyName, id, index}); \
107 } \
108 id++; \
109 } while (0);
110 PER_REALM_STRONG_PERSISTENT_VALUES(V)
111 #undef V
112
113 // Do this after other creator->AddData() calls so that Snapshotable objects
114 // can use 0 to indicate that a SnapshotIndex is invalid.
115 SerializeSnapshotableObjects(this, creator, &info);
116
117 info.context = creator->AddData(ctx, ctx);
118 return info;
119 }
120
DeserializeProperties(const RealmSerializeInfo * info)121 void Realm::DeserializeProperties(const RealmSerializeInfo* info) {
122 Local<Context> ctx = context();
123
124 const std::vector<PropInfo>& values = info->persistent_values;
125 size_t i = 0; // index to the array
126 uint32_t id = 0;
127 #define V(PropertyName, TypeName) \
128 do { \
129 if (values.size() > i && id == values[i].id) { \
130 const PropInfo& d = values[i]; \
131 DCHECK_EQ(d.name, #PropertyName); \
132 MaybeLocal<TypeName> maybe_field = \
133 ctx->GetDataFromSnapshotOnce<TypeName>(d.index); \
134 Local<TypeName> field; \
135 if (!maybe_field.ToLocal(&field)) { \
136 fprintf(stderr, \
137 "Failed to deserialize realm value " #PropertyName "\n"); \
138 } \
139 set_##PropertyName(field); \
140 i++; \
141 } \
142 id++; \
143 } while (0);
144
145 PER_REALM_STRONG_PERSISTENT_VALUES(V);
146 #undef V
147
148 MaybeLocal<Context> maybe_ctx_from_snapshot =
149 ctx->GetDataFromSnapshotOnce<Context>(info->context);
150 Local<Context> ctx_from_snapshot;
151 if (!maybe_ctx_from_snapshot.ToLocal(&ctx_from_snapshot)) {
152 fprintf(stderr,
153 "Failed to deserialize context back reference from the snapshot\n");
154 }
155 CHECK_EQ(ctx_from_snapshot, ctx);
156
157 DoneBootstrapping();
158 }
159
ExecuteBootstrapper(const char * id,std::vector<Local<Value>> * arguments)160 MaybeLocal<Value> Realm::ExecuteBootstrapper(
161 const char* id, std::vector<Local<Value>>* arguments) {
162 EscapableHandleScope scope(isolate());
163 Local<Context> ctx = context();
164 MaybeLocal<Function> maybe_fn =
165 BuiltinLoader::LookupAndCompile(ctx, id, env());
166
167 Local<Function> fn;
168 if (!maybe_fn.ToLocal(&fn)) {
169 return MaybeLocal<Value>();
170 }
171
172 MaybeLocal<Value> result =
173 fn->Call(ctx, Undefined(isolate()), arguments->size(), arguments->data());
174
175 // If there was an error during bootstrap, it must be unrecoverable
176 // (e.g. max call stack exceeded). Clear the stack so that the
177 // AsyncCallbackScope destructor doesn't fail on the id check.
178 // There are only two ways to have a stack size > 1: 1) the user manually
179 // called MakeCallback or 2) user awaited during bootstrap, which triggered
180 // _tickCallback().
181 if (result.IsEmpty()) {
182 env()->async_hooks()->clear_async_id_stack();
183 }
184
185 return scope.EscapeMaybe(result);
186 }
187
BootstrapInternalLoaders()188 MaybeLocal<Value> Realm::BootstrapInternalLoaders() {
189 EscapableHandleScope scope(isolate_);
190
191 // Arguments must match the parameters specified in
192 // BuiltinLoader::LookupAndCompile().
193 std::vector<Local<Value>> loaders_args = {
194 process_object(),
195 NewFunctionTemplate(isolate_, binding::GetLinkedBinding)
196 ->GetFunction(context())
197 .ToLocalChecked(),
198 NewFunctionTemplate(isolate_, binding::GetInternalBinding)
199 ->GetFunction(context())
200 .ToLocalChecked(),
201 primordials()};
202
203 // Bootstrap internal loaders
204 Local<Value> loader_exports;
205 if (!ExecuteBootstrapper("internal/bootstrap/loaders", &loaders_args)
206 .ToLocal(&loader_exports)) {
207 return MaybeLocal<Value>();
208 }
209 CHECK(loader_exports->IsObject());
210 Local<Object> loader_exports_obj = loader_exports.As<Object>();
211 Local<Value> internal_binding_loader =
212 loader_exports_obj->Get(context(), env_->internal_binding_string())
213 .ToLocalChecked();
214 CHECK(internal_binding_loader->IsFunction());
215 set_internal_binding_loader(internal_binding_loader.As<Function>());
216 Local<Value> require =
217 loader_exports_obj->Get(context(), env_->require_string())
218 .ToLocalChecked();
219 CHECK(require->IsFunction());
220 set_builtin_module_require(require.As<Function>());
221
222 return scope.Escape(loader_exports);
223 }
224
BootstrapNode()225 MaybeLocal<Value> Realm::BootstrapNode() {
226 EscapableHandleScope scope(isolate_);
227
228 // Arguments must match the parameters specified in
229 // BuiltinLoader::LookupAndCompile().
230 // process, require, internalBinding, primordials
231 std::vector<Local<Value>> node_args = {process_object(),
232 builtin_module_require(),
233 internal_binding_loader(),
234 primordials()};
235
236 MaybeLocal<Value> result =
237 ExecuteBootstrapper("internal/bootstrap/node", &node_args);
238
239 if (result.IsEmpty()) {
240 return MaybeLocal<Value>();
241 }
242
243 if (!env_->no_browser_globals()) {
244 result = ExecuteBootstrapper("internal/bootstrap/browser", &node_args);
245
246 if (result.IsEmpty()) {
247 return MaybeLocal<Value>();
248 }
249 }
250
251 // TODO(joyeecheung): skip these in the snapshot building for workers.
252 auto thread_switch_id =
253 env_->is_main_thread() ? "internal/bootstrap/switches/is_main_thread"
254 : "internal/bootstrap/switches/is_not_main_thread";
255 result = ExecuteBootstrapper(thread_switch_id, &node_args);
256
257 if (result.IsEmpty()) {
258 return MaybeLocal<Value>();
259 }
260
261 auto process_state_switch_id =
262 env_->owns_process_state()
263 ? "internal/bootstrap/switches/does_own_process_state"
264 : "internal/bootstrap/switches/does_not_own_process_state";
265 result = ExecuteBootstrapper(process_state_switch_id, &node_args);
266
267 if (result.IsEmpty()) {
268 return MaybeLocal<Value>();
269 }
270
271 Local<String> env_string = FIXED_ONE_BYTE_STRING(isolate_, "env");
272 Local<Object> env_proxy;
273 CreateEnvProxyTemplate(isolate_, env_->isolate_data());
274 if (!env_->env_proxy_template()->NewInstance(context()).ToLocal(&env_proxy) ||
275 process_object()->Set(context(), env_string, env_proxy).IsNothing()) {
276 return MaybeLocal<Value>();
277 }
278
279 return scope.EscapeMaybe(result);
280 }
281
RunBootstrapping()282 MaybeLocal<Value> Realm::RunBootstrapping() {
283 EscapableHandleScope scope(isolate_);
284
285 CHECK(!has_run_bootstrapping_code());
286
287 if (BootstrapInternalLoaders().IsEmpty()) {
288 return MaybeLocal<Value>();
289 }
290
291 Local<Value> result;
292 if (!BootstrapNode().ToLocal(&result)) {
293 return MaybeLocal<Value>();
294 }
295
296 DoneBootstrapping();
297
298 return scope.Escape(result);
299 }
300
DoneBootstrapping()301 void Realm::DoneBootstrapping() {
302 // Make sure that no request or handle is created during bootstrap -
303 // if necessary those should be done in pre-execution.
304 // Usually, doing so would trigger the checks present in the ReqWrap and
305 // HandleWrap classes, so this is only a consistency check.
306
307 // TODO(legendecas): track req_wrap and handle_wrap by realms instead of
308 // environments.
309 CHECK(env_->req_wrap_queue()->IsEmpty());
310 CHECK(env_->handle_wrap_queue()->IsEmpty());
311
312 has_run_bootstrapping_code_ = true;
313
314 // This adjusts the return value of base_object_created_after_bootstrap() so
315 // that tests that check the count do not have to account for internally
316 // created BaseObjects.
317 base_object_created_by_bootstrap_ = base_object_count_;
318 }
319
RunCleanup()320 void Realm::RunCleanup() {
321 TRACE_EVENT0(TRACING_CATEGORY_NODE1(realm), "RunCleanup");
322 binding_data_store_.clear();
323
324 cleanup_queue_.Drain();
325 }
326
PrintInfoForSnapshot()327 void Realm::PrintInfoForSnapshot() {
328 fprintf(stderr, "Realm = %p\n", this);
329 fprintf(stderr, "BaseObjects of the Realm:\n");
330 size_t i = 0;
331 ForEachBaseObject([&](BaseObject* obj) {
332 std::cout << "#" << i++ << " " << obj << ": " << obj->MemoryInfoName()
333 << "\n";
334 });
335 fprintf(stderr, "End of the Realm.\n");
336 }
337
VerifyNoStrongBaseObjects()338 void Realm::VerifyNoStrongBaseObjects() {
339 // When a process exits cleanly, i.e. because the event loop ends up without
340 // things to wait for, the Node.js objects that are left on the heap should
341 // be:
342 //
343 // 1. weak, i.e. ready for garbage collection once no longer referenced, or
344 // 2. detached, i.e. scheduled for destruction once no longer referenced, or
345 // 3. an unrefed libuv handle, i.e. does not keep the event loop alive, or
346 // 4. an inactive libuv handle (essentially the same here)
347 //
348 // There are a few exceptions to this rule, but generally, if there are
349 // C++-backed Node.js objects on the heap that do not fall into the above
350 // categories, we may be looking at a potential memory leak. Most likely,
351 // the cause is a missing MakeWeak() call on the corresponding object.
352 //
353 // In order to avoid this kind of problem, we check the list of BaseObjects
354 // for these criteria. Currently, we only do so when explicitly instructed to
355 // or when in debug mode (where --verify-base-objects is always-on).
356
357 // TODO(legendecas): introduce per-realm options.
358 if (!env()->options()->verify_base_objects) return;
359
360 ForEachBaseObject([](BaseObject* obj) {
361 if (obj->IsNotIndicativeOfMemoryLeakAtExit()) return;
362 fprintf(stderr,
363 "Found bad BaseObject during clean exit: %s\n",
364 obj->MemoryInfoName());
365 fflush(stderr);
366 ABORT();
367 });
368 }
369
370 } // namespace node
371