• 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 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