GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_realm.cc Lines: 121 154 78.6 %
Date: 2022-12-31 04:22:30 Branches: 328 712 46.1 %

Line Branch Exec Source
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::Value;
22
23
6536
Realm::Realm(Environment* env,
24
             v8::Local<v8::Context> context,
25
6536
             const RealmSerializeInfo* realm_info)
26
6536
    : env_(env), isolate_(context->GetIsolate()) {
27
6536
  context_.Reset(isolate_, context);
28
29
  // Create properties if not deserializing from snapshot.
30
  // Or the properties are deserialized with DeserializeProperties() when the
31
  // env drained the deserialize requests.
32
6536
  if (realm_info == nullptr) {
33
827
    CreateProperties();
34
  }
35
6536
}
36
37
781836
Realm::~Realm() {
38
11846
  CHECK_EQ(base_object_count_, 0);
39
23692
}
40
41
37
void Realm::MemoryInfo(MemoryTracker* tracker) const {
42
#define V(PropertyName, TypeName)                                              \
43
  tracker->TrackField(#PropertyName, PropertyName());
44
37
  PER_REALM_STRONG_PERSISTENT_VALUES(V)
45
#undef V
46
47
37
  tracker->TrackField("env", env_);
48
37
  tracker->TrackField("cleanup_queue", cleanup_queue_);
49
37
  tracker->TrackField("builtins_with_cache", builtins_with_cache);
50
37
  tracker->TrackField("builtins_without_cache", builtins_without_cache);
51
52
37
  ForEachBaseObject([&](BaseObject* obj) {
53
886
    if (obj->IsDoneInitializing()) {
54
885
      tracker->Track(obj);
55
    }
56
886
  });
57
37
}
58
59
827
void Realm::CreateProperties() {
60
1654
  HandleScope handle_scope(isolate_);
61
827
  Local<Context> ctx = context();
62
63
  // Store primordials setup by the per-context script in the environment.
64
  Local<Object> per_context_bindings =
65
1654
      GetPerContextExports(ctx).ToLocalChecked();
66
  Local<Value> primordials =
67
1654
      per_context_bindings->Get(ctx, env_->primordials_string())
68
827
          .ToLocalChecked();
69
827
  CHECK(primordials->IsObject());
70
827
  set_primordials(primordials.As<Object>());
71
72
  Local<String> prototype_string =
73
827
      FIXED_ONE_BYTE_STRING(isolate(), "prototype");
74
75
#define V(EnvPropertyName, PrimordialsPropertyName)                            \
76
  {                                                                            \
77
    Local<Value> ctor =                                                        \
78
        primordials.As<Object>()                                               \
79
            ->Get(ctx,                                                         \
80
                  FIXED_ONE_BYTE_STRING(isolate(), PrimordialsPropertyName))   \
81
            .ToLocalChecked();                                                 \
82
    CHECK(ctor->IsObject());                                                   \
83
    Local<Value> prototype =                                                   \
84
        ctor.As<Object>()->Get(ctx, prototype_string).ToLocalChecked();        \
85
    CHECK(prototype->IsObject());                                              \
86
    set_##EnvPropertyName(prototype.As<Object>());                             \
87
  }
88
89

4962
  V(primordials_safe_map_prototype_object, "SafeMap");
90

4962
  V(primordials_safe_set_prototype_object, "SafeSet");
91

4962
  V(primordials_safe_weak_map_prototype_object, "SafeWeakMap");
92

4962
  V(primordials_safe_weak_set_prototype_object, "SafeWeakSet");
93
#undef V
94
95
  // TODO(legendecas): some methods probably doesn't need to be created with
96
  // process. Distinguish them and create process object only in the principal
97
  // realm.
98
  Local<Object> process_object =
99
827
      node::CreateProcessObject(this).FromMaybe(Local<Object>());
100
827
  set_process_object(process_object);
101
827
}
102
103
7
RealmSerializeInfo Realm::Serialize(SnapshotCreator* creator) {
104
7
  RealmSerializeInfo info;
105
7
  Local<Context> ctx = context();
106
107
  // Currently all modules are compiled without cache in builtin snapshot
108
  // builder.
109
14
  info.builtins = std::vector<std::string>(builtins_without_cache.begin(),
110
7
                                           builtins_without_cache.end());
111
112
7
  uint32_t id = 0;
113
#define V(PropertyName, TypeName)                                              \
114
  do {                                                                         \
115
    Local<TypeName> field = PropertyName();                                    \
116
    if (!field.IsEmpty()) {                                                    \
117
      size_t index = creator->AddData(ctx, field);                             \
118
      info.persistent_values.push_back({#PropertyName, id, index});            \
119
    }                                                                          \
120
    id++;                                                                      \
121
  } while (0);
122































656
  PER_REALM_STRONG_PERSISTENT_VALUES(V)
123
#undef V
124
125
  // Do this after other creator->AddData() calls so that Snapshotable objects
126
  // can use 0 to indicate that a SnapshotIndex is invalid.
127
7
  SerializeSnapshotableObjects(this, creator, &info);
128
129
7
  info.context = creator->AddData(ctx, ctx);
130
7
  return info;
131
}
132
133
5709
void Realm::DeserializeProperties(const RealmSerializeInfo* info) {
134
5709
  Local<Context> ctx = context();
135
136
5709
  builtins_in_snapshot = info->builtins;
137
138
5709
  const std::vector<PropInfo>& values = info->persistent_values;
139
5709
  size_t i = 0;  // index to the array
140
5709
  uint32_t id = 0;
141
#define V(PropertyName, TypeName)                                              \
142
  do {                                                                         \
143
    if (values.size() > i && id == values[i].id) {                             \
144
      const PropInfo& d = values[i];                                           \
145
      DCHECK_EQ(d.name, #PropertyName);                                        \
146
      MaybeLocal<TypeName> maybe_field =                                       \
147
          ctx->GetDataFromSnapshotOnce<TypeName>(d.index);                     \
148
      Local<TypeName> field;                                                   \
149
      if (!maybe_field.ToLocal(&field)) {                                      \
150
        fprintf(stderr,                                                        \
151
                "Failed to deserialize realm value " #PropertyName "\n");      \
152
      }                                                                        \
153
      set_##PropertyName(field);                                               \
154
      i++;                                                                     \
155
    }                                                                          \
156
    id++;                                                                      \
157
  } while (0);
158
159






























































































































502392
  PER_REALM_STRONG_PERSISTENT_VALUES(V);
160
#undef V
161
162
  MaybeLocal<Context> maybe_ctx_from_snapshot =
163
11418
      ctx->GetDataFromSnapshotOnce<Context>(info->context);
164
  Local<Context> ctx_from_snapshot;
165
5709
  if (!maybe_ctx_from_snapshot.ToLocal(&ctx_from_snapshot)) {
166
    fprintf(stderr,
167
            "Failed to deserialize context back reference from the snapshot\n");
168
  }
169
5709
  CHECK_EQ(ctx_from_snapshot, ctx);
170
171
5709
  DoneBootstrapping();
172
5709
}
173
174
10650
MaybeLocal<Value> Realm::ExecuteBootstrapper(const char* id) {
175
10650
  EscapableHandleScope scope(isolate());
176
10650
  Local<Context> ctx = context();
177
10650
  MaybeLocal<Value> result = BuiltinLoader::CompileAndCall(ctx, id, this);
178
179
  // If there was an error during bootstrap, it must be unrecoverable
180
  // (e.g. max call stack exceeded). Clear the stack so that the
181
  // AsyncCallbackScope destructor doesn't fail on the id check.
182
  // There are only two ways to have a stack size > 1: 1) the user manually
183
  // called MakeCallback or 2) user awaited during bootstrap, which triggered
184
  // _tickCallback().
185
10349
  if (result.IsEmpty()) {
186
32
    env()->async_hooks()->clear_async_id_stack();
187
  }
188
189
10349
  return scope.EscapeMaybe(result);
190
}
191
192
827
MaybeLocal<Value> Realm::BootstrapInternalLoaders() {
193
827
  EscapableHandleScope scope(isolate_);
194
195
  // Bootstrap internal loaders
196
  Local<Value> loader_exports;
197
827
  if (!ExecuteBootstrapper("internal/bootstrap/loaders")
198
827
           .ToLocal(&loader_exports)) {
199
    return MaybeLocal<Value>();
200
  }
201
827
  CHECK(loader_exports->IsObject());
202
827
  Local<Object> loader_exports_obj = loader_exports.As<Object>();
203
  Local<Value> internal_binding_loader =
204
1654
      loader_exports_obj->Get(context(), env_->internal_binding_string())
205
827
          .ToLocalChecked();
206
827
  CHECK(internal_binding_loader->IsFunction());
207
827
  set_internal_binding_loader(internal_binding_loader.As<Function>());
208
  Local<Value> require =
209
1654
      loader_exports_obj->Get(context(), env_->require_string())
210
827
          .ToLocalChecked();
211
827
  CHECK(require->IsFunction());
212
827
  set_builtin_module_require(require.As<Function>());
213
214
827
  return scope.Escape(loader_exports);
215
}
216
217
827
MaybeLocal<Value> Realm::BootstrapNode() {
218
827
  EscapableHandleScope scope(isolate_);
219
220
827
  MaybeLocal<Value> result = ExecuteBootstrapper("internal/bootstrap/node");
221
222
827
  if (result.IsEmpty()) {
223
    return MaybeLocal<Value>();
224
  }
225
226
827
  if (!env_->no_browser_globals()) {
227
827
    result = ExecuteBootstrapper("internal/bootstrap/browser");
228
229
827
    if (result.IsEmpty()) {
230
      return MaybeLocal<Value>();
231
    }
232
  }
233
234
  // TODO(joyeecheung): skip these in the snapshot building for workers.
235
  auto thread_switch_id =
236
827
      env_->is_main_thread() ? "internal/bootstrap/switches/is_main_thread"
237
827
                             : "internal/bootstrap/switches/is_not_main_thread";
238
827
  result = ExecuteBootstrapper(thread_switch_id);
239
240
827
  if (result.IsEmpty()) {
241
    return MaybeLocal<Value>();
242
  }
243
244
  auto process_state_switch_id =
245
827
      env_->owns_process_state()
246
827
          ? "internal/bootstrap/switches/does_own_process_state"
247
827
          : "internal/bootstrap/switches/does_not_own_process_state";
248
827
  result = ExecuteBootstrapper(process_state_switch_id);
249
250
827
  if (result.IsEmpty()) {
251
    return MaybeLocal<Value>();
252
  }
253
254
827
  Local<String> env_string = FIXED_ONE_BYTE_STRING(isolate_, "env");
255
  Local<Object> env_proxy;
256
827
  CreateEnvProxyTemplate(isolate_, env_->isolate_data());
257
3308
  if (!env_->env_proxy_template()->NewInstance(context()).ToLocal(&env_proxy) ||
258

3308
      process_object()->Set(context(), env_string, env_proxy).IsNothing()) {
259
    return MaybeLocal<Value>();
260
  }
261
262
827
  return scope.EscapeMaybe(result);
263
}
264
265
827
MaybeLocal<Value> Realm::RunBootstrapping() {
266
827
  EscapableHandleScope scope(isolate_);
267
268
827
  CHECK(!has_run_bootstrapping_code());
269
270
1654
  if (BootstrapInternalLoaders().IsEmpty()) {
271
    return MaybeLocal<Value>();
272
  }
273
274
  Local<Value> result;
275
1654
  if (!BootstrapNode().ToLocal(&result)) {
276
    return MaybeLocal<Value>();
277
  }
278
279
827
  DoneBootstrapping();
280
281
827
  return scope.Escape(result);
282
}
283
284
6536
void Realm::DoneBootstrapping() {
285
  // Make sure that no request or handle is created during bootstrap -
286
  // if necessary those should be done in pre-execution.
287
  // Usually, doing so would trigger the checks present in the ReqWrap and
288
  // HandleWrap classes, so this is only a consistency check.
289
290
  // TODO(legendecas): track req_wrap and handle_wrap by realms instead of
291
  // environments.
292
6536
  CHECK(env_->req_wrap_queue()->IsEmpty());
293
6536
  CHECK(env_->handle_wrap_queue()->IsEmpty());
294
295
6536
  has_run_bootstrapping_code_ = true;
296
297
  // This adjusts the return value of base_object_created_after_bootstrap() so
298
  // that tests that check the count do not have to account for internally
299
  // created BaseObjects.
300
6536
  base_object_created_by_bootstrap_ = base_object_count_;
301
6536
}
302
303
5907
void Realm::RunCleanup() {
304

16932
  TRACE_EVENT0(TRACING_CATEGORY_NODE1(realm), "RunCleanup");
305
306
5907
  cleanup_queue_.Drain();
307
5907
}
308
309
void Realm::PrintInfoForSnapshot() {
310
  fprintf(stderr, "Realm = %p\n", this);
311
  fprintf(stderr, "BaseObjects of the Realm:\n");
312
  size_t i = 0;
313
  ForEachBaseObject([&](BaseObject* obj) {
314
    std::cout << "#" << i++ << " " << obj << ": " << obj->MemoryInfoName()
315
              << "\n";
316
  });
317
318
  fprintf(stderr, "\nnBuiltins without cache:\n");
319
  for (const auto& s : builtins_without_cache) {
320
    fprintf(stderr, "%s\n", s.c_str());
321
  }
322
  fprintf(stderr, "\nBuiltins with cache:\n");
323
  for (const auto& s : builtins_with_cache) {
324
    fprintf(stderr, "%s\n", s.c_str());
325
  }
326
  fprintf(stderr, "\nStatic bindings (need to be registered):\n");
327
  for (const auto mod : internal_bindings) {
328
    fprintf(stderr, "%s:%s\n", mod->nm_filename, mod->nm_modname);
329
  }
330
331
  fprintf(stderr, "End of the Realm.\n");
332
}
333
334
5544
void Realm::VerifyNoStrongBaseObjects() {
335
  // When a process exits cleanly, i.e. because the event loop ends up without
336
  // things to wait for, the Node.js objects that are left on the heap should
337
  // be:
338
  //
339
  //   1. weak, i.e. ready for garbage collection once no longer referenced, or
340
  //   2. detached, i.e. scheduled for destruction once no longer referenced, or
341
  //   3. an unrefed libuv handle, i.e. does not keep the event loop alive, or
342
  //   4. an inactive libuv handle (essentially the same here)
343
  //
344
  // There are a few exceptions to this rule, but generally, if there are
345
  // C++-backed Node.js objects on the heap that do not fall into the above
346
  // categories, we may be looking at a potential memory leak. Most likely,
347
  // the cause is a missing MakeWeak() call on the corresponding object.
348
  //
349
  // In order to avoid this kind of problem, we check the list of BaseObjects
350
  // for these criteria. Currently, we only do so when explicitly instructed to
351
  // or when in debug mode (where --verify-base-objects is always-on).
352
353
  // TODO(legendecas): introduce per-realm options.
354
5544
  if (!env()->options()->verify_base_objects) return;
355
356
  ForEachBaseObject([](BaseObject* obj) {
357
    if (obj->IsNotIndicativeOfMemoryLeakAtExit()) return;
358
    fprintf(stderr,
359
            "Found bad BaseObject during clean exit: %s\n",
360
            obj->MemoryInfoName().c_str());
361
    fflush(stderr);
362
    ABORT();
363
  });
364
}
365
366
}  // namespace node