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