• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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