GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
#include "node.h" |
||
2 |
#include "async_wrap-inl.h" |
||
3 |
#include "env-inl.h" |
||
4 |
#include "v8.h" |
||
5 |
|||
6 |
namespace node { |
||
7 |
|||
8 |
using v8::Context; |
||
9 |
using v8::EscapableHandleScope; |
||
10 |
using v8::Function; |
||
11 |
using v8::HandleScope; |
||
12 |
using v8::Isolate; |
||
13 |
using v8::Local; |
||
14 |
using v8::MaybeLocal; |
||
15 |
using v8::Object; |
||
16 |
using v8::String; |
||
17 |
using v8::Value; |
||
18 |
|||
19 |
4 |
CallbackScope::CallbackScope(Isolate* isolate, |
|
20 |
Local<Object> object, |
||
21 |
4 |
async_context async_context) |
|
22 |
4 |
: CallbackScope(Environment::GetCurrent(isolate), object, async_context) {} |
|
23 |
|||
24 |
115383 |
CallbackScope::CallbackScope(Environment* env, |
|
25 |
Local<Object> object, |
||
26 |
115383 |
async_context asyncContext) |
|
27 |
: private_(new InternalCallbackScope(env, |
||
28 |
object, |
||
29 |
115383 |
asyncContext)), |
|
30 |
230766 |
try_catch_(env->isolate()) { |
|
31 |
115383 |
try_catch_.SetVerbose(true); |
|
32 |
115383 |
} |
|
33 |
|||
34 |
115382 |
CallbackScope::~CallbackScope() { |
|
35 |
✓✓ | 115382 |
if (try_catch_.HasCaught()) |
36 |
1 |
private_->MarkAsFailed(); |
|
37 |
✓✗ | 115382 |
delete private_; |
38 |
115382 |
} |
|
39 |
|||
40 |
42118 |
InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags) |
|
41 |
: InternalCallbackScope(async_wrap->env(), |
||
42 |
async_wrap->object(), |
||
43 |
42118 |
{ async_wrap->get_async_id(), |
|
44 |
42118 |
async_wrap->get_trigger_async_id() }, |
|
45 |
42118 |
flags) {} |
|
46 |
|||
47 |
786918 |
InternalCallbackScope::InternalCallbackScope(Environment* env, |
|
48 |
Local<Object> object, |
||
49 |
const async_context& asyncContext, |
||
50 |
786918 |
int flags) |
|
51 |
: env_(env), |
||
52 |
async_context_(asyncContext), |
||
53 |
object_(object), |
||
54 |
786918 |
skip_hooks_(flags & kSkipAsyncHooks), |
|
55 |
1573836 |
skip_task_queues_(flags & kSkipTaskQueues) { |
|
56 |
✗✓ | 786918 |
CHECK_NOT_NULL(env); |
57 |
786918 |
env->PushAsyncCallbackScope(); |
|
58 |
|||
59 |
✓✓ | 786918 |
if (!env->can_call_into_js()) { |
60 |
13559 |
failed_ = true; |
|
61 |
13559 |
return; |
|
62 |
} |
||
63 |
|||
64 |
773359 |
Isolate* isolate = env->isolate(); |
|
65 |
|||
66 |
1546718 |
HandleScope handle_scope(isolate); |
|
67 |
773359 |
Local<Context> current_context = isolate->GetCurrentContext(); |
|
68 |
// If you hit this assertion, the caller forgot to enter the right Node.js |
||
69 |
// Environment's v8::Context first. |
||
70 |
// We first check `env->context() != current_context` because the contexts |
||
71 |
// likely *are* the same, in which case we can skip the slightly more |
||
72 |
// expensive Environment::GetCurrent() call. |
||
73 |
✓✓ | 1546718 |
if (UNLIKELY(env->context() != current_context)) { |
74 |
✗✓ | 6 |
CHECK_EQ(Environment::GetCurrent(isolate), env); |
75 |
} |
||
76 |
|||
77 |
773359 |
isolate->SetIdle(false); |
|
78 |
|||
79 |
773359 |
env->async_hooks()->push_async_context( |
|
80 |
async_context_.async_id, async_context_.trigger_async_id, object); |
||
81 |
|||
82 |
773359 |
pushed_ids_ = true; |
|
83 |
|||
84 |
✓✓✓✓ |
773359 |
if (asyncContext.async_id != 0 && !skip_hooks_) { |
85 |
// No need to check a return value because the application will exit if |
||
86 |
// an exception occurs. |
||
87 |
487140 |
AsyncWrap::EmitBefore(env, asyncContext.async_id); |
|
88 |
} |
||
89 |
} |
||
90 |
|||
91 |
1572863 |
InternalCallbackScope::~InternalCallbackScope() { |
|
92 |
786480 |
Close(); |
|
93 |
786383 |
env_->PopAsyncCallbackScope(); |
|
94 |
786383 |
} |
|
95 |
|||
96 |
1192734 |
void InternalCallbackScope::Close() { |
|
97 |
✓✓ | 1788434 |
if (closed_) return; |
98 |
786600 |
closed_ = true; |
|
99 |
|||
100 |
786600 |
Isolate* isolate = env_->isolate(); |
|
101 |
1572983 |
auto idle = OnScopeLeave([&]() { isolate->SetIdle(true); }); |
|
102 |
|||
103 |
✓✓ | 786600 |
if (!env_->can_call_into_js()) return; |
104 |
1474077 |
auto perform_stopping_check = [&]() { |
|
105 |
✓✓ | 1474077 |
if (env_->is_stopping()) { |
106 |
206 |
MarkAsFailed(); |
|
107 |
206 |
env_->async_hooks()->clear_async_id_stack(); |
|
108 |
} |
||
109 |
2247044 |
}; |
|
110 |
772967 |
perform_stopping_check(); |
|
111 |
|||
112 |
✓✓✓✓ ✓✓ |
772967 |
if (!failed_ && async_context_.async_id != 0 && !skip_hooks_) { |
113 |
487056 |
AsyncWrap::EmitAfter(env_, async_context_.async_id); |
|
114 |
} |
||
115 |
|||
116 |
✓✗ | 772967 |
if (pushed_ids_) |
117 |
772967 |
env_->async_hooks()->pop_async_context(async_context_.async_id); |
|
118 |
|||
119 |
✓✓ | 772966 |
if (failed_) return; |
120 |
|||
121 |
✓✓✓✓ ✓✓ |
771968 |
if (env_->async_callback_scope_depth() > 1 || skip_task_queues_) { |
122 |
71988 |
return; |
|
123 |
} |
||
124 |
|||
125 |
699980 |
TickInfo* tick_info = env_->tick_info(); |
|
126 |
|||
127 |
✓✓ | 699980 |
if (!env_->can_call_into_js()) return; |
128 |
|||
129 |
1399738 |
auto weakref_cleanup = OnScopeLeave([&]() { env_->RunWeakRefCleanup(); }); |
|
130 |
|||
131 |
699977 |
Local<Context> context = env_->context(); |
|
132 |
✓✓ | 699977 |
if (!tick_info->has_tick_scheduled()) { |
133 |
510498 |
context->GetMicrotaskQueue()->PerformCheckpoint(isolate); |
|
134 |
|||
135 |
510427 |
perform_stopping_check(); |
|
136 |
} |
||
137 |
|||
138 |
// Make sure the stack unwound properly. If there are nested MakeCallback's |
||
139 |
// then it should return early and not reach this code. |
||
140 |
✓✓ | 699906 |
if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) { |
141 |
✗✓ | 65000 |
CHECK_EQ(env_->execution_async_id(), 0); |
142 |
✗✓ | 65000 |
CHECK_EQ(env_->trigger_async_id(), 0); |
143 |
} |
||
144 |
|||
145 |
✓✓✓✓ ✓✓ |
699906 |
if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) { |
146 |
509077 |
return; |
|
147 |
} |
||
148 |
|||
149 |
190829 |
HandleScope handle_scope(isolate); |
|
150 |
190829 |
Local<Object> process = env_->process_object(); |
|
151 |
|||
152 |
✓✓ | 190829 |
if (!env_->can_call_into_js()) return; |
153 |
|||
154 |
190828 |
Local<Function> tick_callback = env_->tick_callback_function(); |
|
155 |
|||
156 |
// The tick is triggered before JS land calls SetTickCallback |
||
157 |
// to initializes the tick callback during bootstrap. |
||
158 |
✗✓ | 190828 |
CHECK(!tick_callback.IsEmpty()); |
159 |
|||
160 |
✓✓ | 381511 |
if (tick_callback->Call(context, process, 0, nullptr).IsEmpty()) { |
161 |
344 |
failed_ = true; |
|
162 |
} |
||
163 |
190683 |
perform_stopping_check(); |
|
164 |
} |
||
165 |
|||
166 |
409023 |
MaybeLocal<Value> InternalMakeCallback(Environment* env, |
|
167 |
Local<Object> resource, |
||
168 |
Local<Object> recv, |
||
169 |
const Local<Function> callback, |
||
170 |
int argc, |
||
171 |
Local<Value> argv[], |
||
172 |
async_context asyncContext) { |
||
173 |
✗✓ | 409023 |
CHECK(!recv.IsEmpty()); |
174 |
#ifdef DEBUG |
||
175 |
for (int i = 0; i < argc; i++) |
||
176 |
CHECK(!argv[i].IsEmpty()); |
||
177 |
#endif |
||
178 |
|||
179 |
409023 |
Local<Function> hook_cb = env->async_hooks_callback_trampoline(); |
|
180 |
409023 |
int flags = InternalCallbackScope::kNoFlags; |
|
181 |
409023 |
bool use_async_hooks_trampoline = false; |
|
182 |
409023 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
183 |
✓✓ | 409023 |
if (!hook_cb.IsEmpty()) { |
184 |
// Use the callback trampoline if there are any before or after hooks, or |
||
185 |
// we can expect some kind of usage of async_hooks.executionAsyncResource(). |
||
186 |
47486 |
flags = InternalCallbackScope::kSkipAsyncHooks; |
|
187 |
47486 |
use_async_hooks_trampoline = |
|
188 |
47486 |
async_hooks->fields()[AsyncHooks::kBefore] + |
|
189 |
47486 |
async_hooks->fields()[AsyncHooks::kAfter] + |
|
190 |
47486 |
async_hooks->fields()[AsyncHooks::kUsesExecutionAsyncResource] > 0; |
|
191 |
} |
||
192 |
|||
193 |
817876 |
InternalCallbackScope scope(env, resource, asyncContext, flags); |
|
194 |
✓✓ | 409023 |
if (scope.Failed()) { |
195 |
1691 |
return MaybeLocal<Value>(); |
|
196 |
} |
||
197 |
|||
198 |
MaybeLocal<Value> ret; |
||
199 |
|||
200 |
407332 |
Local<Context> context = env->context(); |
|
201 |
✓✓ | 407332 |
if (use_async_hooks_trampoline) { |
202 |
30860 |
MaybeStackBuffer<Local<Value>, 16> args(3 + argc); |
|
203 |
61720 |
args[0] = v8::Number::New(env->isolate(), asyncContext.async_id); |
|
204 |
30860 |
args[1] = resource; |
|
205 |
30860 |
args[2] = callback; |
|
206 |
✓✓ | 41683 |
for (int i = 0; i < argc; i++) { |
207 |
10823 |
args[i + 3] = argv[i]; |
|
208 |
} |
||
209 |
30860 |
ret = hook_cb->Call(context, recv, args.length(), &args[0]); |
|
210 |
} else { |
||
211 |
376472 |
ret = callback->Call(context, recv, argc, argv); |
|
212 |
} |
||
213 |
|||
214 |
✓✓ | 407282 |
if (ret.IsEmpty()) { |
215 |
1028 |
scope.MarkAsFailed(); |
|
216 |
1028 |
return MaybeLocal<Value>(); |
|
217 |
} |
||
218 |
|||
219 |
406254 |
scope.Close(); |
|
220 |
✓✓ | 406134 |
if (scope.Failed()) { |
221 |
257 |
return MaybeLocal<Value>(); |
|
222 |
} |
||
223 |
|||
224 |
405877 |
return ret; |
|
225 |
} |
||
226 |
|||
227 |
// Public MakeCallback()s |
||
228 |
|||
229 |
10547 |
MaybeLocal<Value> MakeCallback(Isolate* isolate, |
|
230 |
Local<Object> recv, |
||
231 |
const char* method, |
||
232 |
int argc, |
||
233 |
Local<Value> argv[], |
||
234 |
async_context asyncContext) { |
||
235 |
Local<String> method_string = |
||
236 |
10547 |
String::NewFromUtf8(isolate, method).ToLocalChecked(); |
|
237 |
10547 |
return MakeCallback(isolate, recv, method_string, argc, argv, asyncContext); |
|
238 |
} |
||
239 |
|||
240 |
10556 |
MaybeLocal<Value> MakeCallback(Isolate* isolate, |
|
241 |
Local<Object> recv, |
||
242 |
Local<String> symbol, |
||
243 |
int argc, |
||
244 |
Local<Value> argv[], |
||
245 |
async_context asyncContext) { |
||
246 |
// Check can_call_into_js() first because calling Get() might do so. |
||
247 |
Environment* env = |
||
248 |
21112 |
Environment::GetCurrent(recv->GetCreationContext().ToLocalChecked()); |
|
249 |
✗✓ | 10556 |
CHECK_NOT_NULL(env); |
250 |
✓✓ | 10567 |
if (!env->can_call_into_js()) return Local<Value>(); |
251 |
|||
252 |
Local<Value> callback_v; |
||
253 |
✗✓ | 21090 |
if (!recv->Get(isolate->GetCurrentContext(), symbol).ToLocal(&callback_v)) |
254 |
return Local<Value>(); |
||
255 |
✗✓ | 10545 |
if (!callback_v->IsFunction()) { |
256 |
// This used to return an empty value, but Undefined() makes more sense |
||
257 |
// since no exception is pending here. |
||
258 |
return Undefined(isolate); |
||
259 |
} |
||
260 |
10545 |
Local<Function> callback = callback_v.As<Function>(); |
|
261 |
10545 |
return MakeCallback(isolate, recv, callback, argc, argv, asyncContext); |
|
262 |
} |
||
263 |
|||
264 |
53442 |
MaybeLocal<Value> MakeCallback(Isolate* isolate, |
|
265 |
Local<Object> recv, |
||
266 |
Local<Function> callback, |
||
267 |
int argc, |
||
268 |
Local<Value> argv[], |
||
269 |
async_context asyncContext) { |
||
270 |
// Observe the following two subtleties: |
||
271 |
// |
||
272 |
// 1. The environment is retrieved from the callback function's context. |
||
273 |
// 2. The context to enter is retrieved from the environment. |
||
274 |
// |
||
275 |
// Because of the AssignToContext() call in src/node_contextify.cc, |
||
276 |
// the two contexts need not be the same. |
||
277 |
Environment* env = |
||
278 |
106884 |
Environment::GetCurrent(callback->GetCreationContext().ToLocalChecked()); |
|
279 |
✗✓ | 53442 |
CHECK_NOT_NULL(env); |
280 |
53442 |
Context::Scope context_scope(env->context()); |
|
281 |
MaybeLocal<Value> ret = |
||
282 |
53442 |
InternalMakeCallback(env, recv, recv, callback, argc, argv, asyncContext); |
|
283 |
✓✓✓✓ ✓✓ |
53422 |
if (ret.IsEmpty() && env->async_callback_scope_depth() == 0) { |
284 |
// This is only for legacy compatibility and we may want to look into |
||
285 |
// removing/adjusting it. |
||
286 |
1050 |
return Undefined(isolate); |
|
287 |
} |
||
288 |
52372 |
return ret; |
|
289 |
} |
||
290 |
|||
291 |
// Use this if you just want to safely invoke some JS callback and |
||
292 |
// would like to retain the currently active async_context, if any. |
||
293 |
// In case none is available, a fixed default context will be |
||
294 |
// installed otherwise. |
||
295 |
16 |
MaybeLocal<Value> MakeSyncCallback(Isolate* isolate, |
|
296 |
Local<Object> recv, |
||
297 |
Local<Function> callback, |
||
298 |
int argc, |
||
299 |
Local<Value> argv[]) { |
||
300 |
Environment* env = |
||
301 |
32 |
Environment::GetCurrent(callback->GetCreationContext().ToLocalChecked()); |
|
302 |
✗✓ | 16 |
CHECK_NOT_NULL(env); |
303 |
✗✓ | 16 |
if (!env->can_call_into_js()) return Local<Value>(); |
304 |
|||
305 |
16 |
Local<Context> context = env->context(); |
|
306 |
16 |
Context::Scope context_scope(context); |
|
307 |
✓✗ | 16 |
if (env->async_callback_scope_depth()) { |
308 |
// There's another MakeCallback() on the stack, piggy back on it. |
||
309 |
// In particular, retain the current async_context. |
||
310 |
16 |
return callback->Call(context, recv, argc, argv); |
|
311 |
} |
||
312 |
|||
313 |
// This is a toplevel invocation and the caller (intentionally) |
||
314 |
// didn't provide any async_context to run in. Install a default context. |
||
315 |
MaybeLocal<Value> ret = |
||
316 |
InternalMakeCallback(env, env->process_object(), recv, callback, argc, argv, |
||
317 |
async_context{0, 0}); |
||
318 |
return ret; |
||
319 |
} |
||
320 |
|||
321 |
// Legacy MakeCallback()s |
||
322 |
|||
323 |
Local<Value> MakeCallback(Isolate* isolate, |
||
324 |
Local<Object> recv, |
||
325 |
const char* method, |
||
326 |
int argc, |
||
327 |
Local<Value>* argv) { |
||
328 |
EscapableHandleScope handle_scope(isolate); |
||
329 |
return handle_scope.Escape( |
||
330 |
MakeCallback(isolate, recv, method, argc, argv, {0, 0}) |
||
331 |
.FromMaybe(Local<Value>())); |
||
332 |
} |
||
333 |
|||
334 |
Local<Value> MakeCallback(Isolate* isolate, |
||
335 |
Local<Object> recv, |
||
336 |
Local<String> symbol, |
||
337 |
int argc, |
||
338 |
Local<Value>* argv) { |
||
339 |
EscapableHandleScope handle_scope(isolate); |
||
340 |
return handle_scope.Escape( |
||
341 |
MakeCallback(isolate, recv, symbol, argc, argv, {0, 0}) |
||
342 |
.FromMaybe(Local<Value>())); |
||
343 |
} |
||
344 |
|||
345 |
Local<Value> MakeCallback(Isolate* isolate, |
||
346 |
Local<Object> recv, |
||
347 |
Local<Function> callback, |
||
348 |
int argc, |
||
349 |
Local<Value>* argv) { |
||
350 |
EscapableHandleScope handle_scope(isolate); |
||
351 |
return handle_scope.Escape( |
||
352 |
MakeCallback(isolate, recv, callback, argc, argv, {0, 0}) |
||
353 |
.FromMaybe(Local<Value>())); |
||
354 |
} |
||
355 |
|||
356 |
} // namespace node |
Generated by: GCOVR (Version 4.2) |