GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_realm.cc Lines: 115 139 82.7 %
Date: 2022-12-07 04:23:16 Branches: 333 706 47.2 %

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
6431
Realm::Realm(Environment* env,
24
             v8::Local<v8::Context> context,
25
6431
             const RealmSerializeInfo* realm_info)
26
6431
    : env_(env), isolate_(context->GetIsolate()) {
27
6431
  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
6431
  if (realm_info == nullptr) {
33
800
    CreateProperties();
34
  }
35
6431
}
36
37
757020
Realm::~Realm() {
38
11470
  CHECK_EQ(base_object_count_, 0);
39
22940
}
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
50
37
  ForEachBaseObject([&](BaseObject* obj) {
51
977
    if (obj->IsDoneInitializing()) {
52
976
      tracker->Track(obj);
53
    }
54
977
  });
55
37
}
56
57
800
void Realm::CreateProperties() {
58
1600
  HandleScope handle_scope(isolate_);
59
800
  Local<Context> ctx = context();
60
61
  // Store primordials setup by the per-context script in the environment.
62
  Local<Object> per_context_bindings =
63
1600
      GetPerContextExports(ctx).ToLocalChecked();
64
  Local<Value> primordials =
65
1600
      per_context_bindings->Get(ctx, env_->primordials_string())
66
800
          .ToLocalChecked();
67
800
  CHECK(primordials->IsObject());
68
800
  set_primordials(primordials.As<Object>());
69
70
  Local<String> prototype_string =
71
800
      FIXED_ONE_BYTE_STRING(isolate(), "prototype");
72
73
#define V(EnvPropertyName, PrimordialsPropertyName)                            \
74
  {                                                                            \
75
    Local<Value> ctor =                                                        \
76
        primordials.As<Object>()                                               \
77
            ->Get(ctx,                                                         \
78
                  FIXED_ONE_BYTE_STRING(isolate(), PrimordialsPropertyName))   \
79
            .ToLocalChecked();                                                 \
80
    CHECK(ctor->IsObject());                                                   \
81
    Local<Value> prototype =                                                   \
82
        ctor.As<Object>()->Get(ctx, prototype_string).ToLocalChecked();        \
83
    CHECK(prototype->IsObject());                                              \
84
    set_##EnvPropertyName(prototype.As<Object>());                             \
85
  }
86
87

4800
  V(primordials_safe_map_prototype_object, "SafeMap");
88

4800
  V(primordials_safe_set_prototype_object, "SafeSet");
89

4800
  V(primordials_safe_weak_map_prototype_object, "SafeWeakMap");
90

4800
  V(primordials_safe_weak_set_prototype_object, "SafeWeakSet");
91
#undef V
92
93
  // TODO(legendecas): some methods probably doesn't need to be created with
94
  // process. Distinguish them and create process object only in the principal
95
  // realm.
96
  Local<Object> process_object =
97
800
      node::CreateProcessObject(this).FromMaybe(Local<Object>());
98
800
  set_process_object(process_object);
99
800
}
100
101
7
RealmSerializeInfo Realm::Serialize(SnapshotCreator* creator) {
102
7
  RealmSerializeInfo info;
103
7
  Local<Context> ctx = context();
104
105
7
  uint32_t id = 0;
106
#define V(PropertyName, TypeName)                                              \
107
  do {                                                                         \
108
    Local<TypeName> field = PropertyName();                                    \
109
    if (!field.IsEmpty()) {                                                    \
110
      size_t index = creator->AddData(ctx, field);                             \
111
      info.persistent_values.push_back({#PropertyName, id, index});            \
112
    }                                                                          \
113
    id++;                                                                      \
114
  } while (0);
115































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






























































































































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

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

16410
  TRACE_EVENT0(TRACING_CATEGORY_NODE1(realm), "RunCleanup");
295
296
5727
  cleanup_queue_.Drain();
297
5727
}
298
299
void Realm::PrintInfoForSnapshot() {
300
  fprintf(stderr, "Realm = %p\n", this);
301
  fprintf(stderr, "BaseObjects of the Realm:\n");
302
  size_t i = 0;
303
  ForEachBaseObject([&](BaseObject* obj) {
304
    std::cout << "#" << i++ << " " << obj << ": " << obj->MemoryInfoName()
305
              << "\n";
306
  });
307
  fprintf(stderr, "End of the Realm.\n");
308
}
309
310
5373
void Realm::VerifyNoStrongBaseObjects() {
311
  // When a process exits cleanly, i.e. because the event loop ends up without
312
  // things to wait for, the Node.js objects that are left on the heap should
313
  // be:
314
  //
315
  //   1. weak, i.e. ready for garbage collection once no longer referenced, or
316
  //   2. detached, i.e. scheduled for destruction once no longer referenced, or
317
  //   3. an unrefed libuv handle, i.e. does not keep the event loop alive, or
318
  //   4. an inactive libuv handle (essentially the same here)
319
  //
320
  // There are a few exceptions to this rule, but generally, if there are
321
  // C++-backed Node.js objects on the heap that do not fall into the above
322
  // categories, we may be looking at a potential memory leak. Most likely,
323
  // the cause is a missing MakeWeak() call on the corresponding object.
324
  //
325
  // In order to avoid this kind of problem, we check the list of BaseObjects
326
  // for these criteria. Currently, we only do so when explicitly instructed to
327
  // or when in debug mode (where --verify-base-objects is always-on).
328
329
  // TODO(legendecas): introduce per-realm options.
330
5373
  if (!env()->options()->verify_base_objects) return;
331
332
  ForEachBaseObject([](BaseObject* obj) {
333
    if (obj->IsNotIndicativeOfMemoryLeakAtExit()) return;
334
    fprintf(stderr,
335
            "Found bad BaseObject during clean exit: %s\n",
336
            obj->MemoryInfoName().c_str());
337
    fflush(stderr);
338
    ABORT();
339
  });
340
}
341
342
}  // namespace node