GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
Generated by: GCOVR (Version 4.2) |