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