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 |
CallbackScope::CallbackScope(Isolate* isolate, |
||
20 |
Local<Object> object, |
||
21 |
async_context asyncContext) |
||
22 |
: private_(new InternalCallbackScope(Environment::GetCurrent(isolate), |
||
23 |
object, |
||
24 |
asyncContext)), |
||
25 |
try_catch_(isolate) { |
||
26 |
try_catch_.SetVerbose(true); |
||
27 |
} |
||
28 |
|||
29 |
CallbackScope::~CallbackScope() { |
||
30 |
if (try_catch_.HasCaught()) |
||
31 |
private_->MarkAsFailed(); |
||
32 |
delete private_; |
||
33 |
} |
||
34 |
|||
35 |
971 |
InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags) |
|
36 |
: InternalCallbackScope(async_wrap->env(), |
||
37 |
async_wrap->object(), |
||
38 |
971 |
{ async_wrap->get_async_id(), |
|
39 |
971 |
async_wrap->get_trigger_async_id() }, |
|
40 |
1942 |
flags) {} |
|
41 |
|||
42 |
27973 |
InternalCallbackScope::InternalCallbackScope(Environment* env, |
|
43 |
Local<Object> object, |
||
44 |
const async_context& asyncContext, |
||
45 |
27973 |
int flags) |
|
46 |
: env_(env), |
||
47 |
async_context_(asyncContext), |
||
48 |
object_(object), |
||
49 |
27973 |
skip_hooks_(flags & kSkipAsyncHooks), |
|
50 |
55946 |
skip_task_queues_(flags & kSkipTaskQueues) { |
|
51 |
✗✓ | 27973 |
CHECK_NOT_NULL(env); |
52 |
27973 |
env->PushAsyncCallbackScope(); |
|
53 |
|||
54 |
✓✓ | 27973 |
if (!env->can_call_into_js()) { |
55 |
323 |
failed_ = true; |
|
56 |
323 |
return; |
|
57 |
} |
||
58 |
|||
59 |
55300 |
HandleScope handle_scope(env->isolate()); |
|
60 |
// If you hit this assertion, you forgot to enter the v8::Context first. |
||
61 |
✗✓ | 27650 |
CHECK_EQ(Environment::GetCurrent(env->isolate()), env); |
62 |
|||
63 |
27650 |
env->async_hooks()->push_async_context( |
|
64 |
27650 |
async_context_.async_id, async_context_.trigger_async_id, object); |
|
65 |
|||
66 |
27650 |
pushed_ids_ = true; |
|
67 |
|||
68 |
✓✓✓✓ |
27650 |
if (asyncContext.async_id != 0 && !skip_hooks_) { |
69 |
// No need to check a return value because the application will exit if |
||
70 |
// an exception occurs. |
||
71 |
971 |
AsyncWrap::EmitBefore(env, asyncContext.async_id); |
|
72 |
} |
||
73 |
} |
||
74 |
|||
75 |
55938 |
InternalCallbackScope::~InternalCallbackScope() { |
|
76 |
27969 |
Close(); |
|
77 |
27969 |
env_->PopAsyncCallbackScope(); |
|
78 |
27969 |
} |
|
79 |
|||
80 |
47115 |
void InternalCallbackScope::Close() { |
|
81 |
✓✓ | 91686 |
if (closed_) return; |
82 |
27971 |
closed_ = true; |
|
83 |
|||
84 |
✓✓ | 27971 |
if (!env_->can_call_into_js()) return; |
85 |
55346 |
auto perform_stopping_check = [&]() { |
|
86 |
✓✓ | 55348 |
if (env_->is_stopping()) { |
87 |
2 |
MarkAsFailed(); |
|
88 |
2 |
env_->async_hooks()->clear_async_id_stack(); |
|
89 |
} |
||
90 |
82993 |
}; |
|
91 |
27647 |
perform_stopping_check(); |
|
92 |
|||
93 |
✓✗✓✓ ✓✓ |
27647 |
if (!failed_ && async_context_.async_id != 0 && !skip_hooks_) { |
94 |
971 |
AsyncWrap::EmitAfter(env_, async_context_.async_id); |
|
95 |
} |
||
96 |
|||
97 |
✓✗ | 27647 |
if (pushed_ids_) |
98 |
27647 |
env_->async_hooks()->pop_async_context(async_context_.async_id); |
|
99 |
|||
100 |
✗✓ | 27647 |
if (failed_) return; |
101 |
|||
102 |
✓✓✗✓ ✓✓ |
27647 |
if (env_->async_callback_scope_depth() > 1 || skip_task_queues_) { |
103 |
212 |
return; |
|
104 |
} |
||
105 |
|||
106 |
27435 |
TickInfo* tick_info = env_->tick_info(); |
|
107 |
|||
108 |
✗✓ | 27435 |
if (!env_->can_call_into_js()) return; |
109 |
|||
110 |
57410 |
auto weakref_cleanup = OnScopeLeave([&]() { env_->RunWeakRefCleanup(); }); |
|
111 |
|||
112 |
✓✓ | 27435 |
if (!tick_info->has_tick_scheduled()) { |
113 |
50314 |
env_->context()->GetMicrotaskQueue()->PerformCheckpoint(env_->isolate()); |
|
114 |
|||
115 |
25157 |
perform_stopping_check(); |
|
116 |
} |
||
117 |
|||
118 |
// Make sure the stack unwound properly. If there are nested MakeCallback's |
||
119 |
// then it should return early and not reach this code. |
||
120 |
✗✓ | 27435 |
if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) { |
121 |
CHECK_EQ(env_->execution_async_id(), 0); |
||
122 |
CHECK_EQ(env_->trigger_async_id(), 0); |
||
123 |
} |
||
124 |
|||
125 |
✓✓✓✗ ✓✓ |
27435 |
if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) { |
126 |
24891 |
return; |
|
127 |
} |
||
128 |
|||
129 |
✓✓ | 5086 |
HandleScope handle_scope(env_->isolate()); |
130 |
2544 |
Local<Object> process = env_->process_object(); |
|
131 |
|||
132 |
✗✓ | 2544 |
if (!env_->can_call_into_js()) return; |
133 |
|||
134 |
2544 |
Local<Function> tick_callback = env_->tick_callback_function(); |
|
135 |
|||
136 |
// The tick is triggered before JS land calls SetTickCallback |
||
137 |
// to initializes the tick callback during bootstrap. |
||
138 |
✗✓ | 2544 |
CHECK(!tick_callback.IsEmpty()); |
139 |
|||
140 |
✓✓ | 7630 |
if (tick_callback->Call(env_->context(), process, 0, nullptr).IsEmpty()) { |
141 |
2 |
failed_ = true; |
|
142 |
} |
||
143 |
✓✗ | 2542 |
perform_stopping_check(); |
144 |
} |
||
145 |
|||
146 |
19146 |
MaybeLocal<Value> InternalMakeCallback(Environment* env, |
|
147 |
Local<Object> resource, |
||
148 |
Local<Object> recv, |
||
149 |
const Local<Function> callback, |
||
150 |
int argc, |
||
151 |
Local<Value> argv[], |
||
152 |
async_context asyncContext) { |
||
153 |
✗✓ | 19146 |
CHECK(!recv.IsEmpty()); |
154 |
#ifdef DEBUG |
||
155 |
for (int i = 0; i < argc; i++) |
||
156 |
CHECK(!argv[i].IsEmpty()); |
||
157 |
#endif |
||
158 |
|||
159 |
19146 |
Local<Function> hook_cb = env->async_hooks_callback_trampoline(); |
|
160 |
19146 |
int flags = InternalCallbackScope::kNoFlags; |
|
161 |
19146 |
bool use_async_hooks_trampoline = false; |
|
162 |
19146 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
163 |
✓✗ | 19146 |
if (!hook_cb.IsEmpty()) { |
164 |
// Use the callback trampoline if there are any before or after hooks, or |
||
165 |
// we can expect some kind of usage of async_hooks.executionAsyncResource(). |
||
166 |
19146 |
flags = InternalCallbackScope::kSkipAsyncHooks; |
|
167 |
19146 |
use_async_hooks_trampoline = |
|
168 |
38292 |
async_hooks->fields()[AsyncHooks::kBefore] + |
|
169 |
57438 |
async_hooks->fields()[AsyncHooks::kAfter] + |
|
170 |
38292 |
async_hooks->fields()[AsyncHooks::kUsesExecutionAsyncResource] > 0; |
|
171 |
} |
||
172 |
|||
173 |
38290 |
InternalCallbackScope scope(env, resource, asyncContext, flags); |
|
174 |
✗✓ | 19146 |
if (scope.Failed()) { |
175 |
return MaybeLocal<Value>(); |
||
176 |
} |
||
177 |
|||
178 |
MaybeLocal<Value> ret; |
||
179 |
|||
180 |
✗✓ | 19146 |
if (use_async_hooks_trampoline) { |
181 |
MaybeStackBuffer<Local<Value>, 16> args(3 + argc); |
||
182 |
args[0] = v8::Number::New(env->isolate(), asyncContext.async_id); |
||
183 |
args[1] = resource; |
||
184 |
args[2] = callback; |
||
185 |
for (int i = 0; i < argc; i++) { |
||
186 |
args[i + 3] = argv[i]; |
||
187 |
} |
||
188 |
ret = hook_cb->Call(env->context(), recv, args.length(), &args[0]); |
||
189 |
} else { |
||
190 |
38292 |
ret = callback->Call(env->context(), recv, argc, argv); |
|
191 |
} |
||
192 |
|||
193 |
✗✓ | 19146 |
if (ret.IsEmpty()) { |
194 |
scope.MarkAsFailed(); |
||
195 |
return MaybeLocal<Value>(); |
||
196 |
} |
||
197 |
|||
198 |
19146 |
scope.Close(); |
|
199 |
✗✓ | 19144 |
if (scope.Failed()) { |
200 |
return MaybeLocal<Value>(); |
||
201 |
} |
||
202 |
|||
203 |
19144 |
return ret; |
|
204 |
} |
||
205 |
|||
206 |
// Public MakeCallback()s |
||
207 |
|||
208 |
208 |
MaybeLocal<Value> MakeCallback(Isolate* isolate, |
|
209 |
Local<Object> recv, |
||
210 |
const char* method, |
||
211 |
int argc, |
||
212 |
Local<Value> argv[], |
||
213 |
async_context asyncContext) { |
||
214 |
Local<String> method_string = |
||
215 |
416 |
String::NewFromUtf8(isolate, method).ToLocalChecked(); |
|
216 |
208 |
return MakeCallback(isolate, recv, method_string, argc, argv, asyncContext); |
|
217 |
} |
||
218 |
|||
219 |
208 |
MaybeLocal<Value> MakeCallback(Isolate* isolate, |
|
220 |
Local<Object> recv, |
||
221 |
Local<String> symbol, |
||
222 |
int argc, |
||
223 |
Local<Value> argv[], |
||
224 |
async_context asyncContext) { |
||
225 |
// Check can_call_into_js() first because calling Get() might do so. |
||
226 |
208 |
Environment* env = Environment::GetCurrent(recv->CreationContext()); |
|
227 |
✗✓ | 208 |
CHECK_NOT_NULL(env); |
228 |
✗✓ | 208 |
if (!env->can_call_into_js()) return Local<Value>(); |
229 |
|||
230 |
Local<Value> callback_v; |
||
231 |
✗✓ | 624 |
if (!recv->Get(isolate->GetCurrentContext(), symbol).ToLocal(&callback_v)) |
232 |
return Local<Value>(); |
||
233 |
✗✓ | 208 |
if (!callback_v->IsFunction()) { |
234 |
// This used to return an empty value, but Undefined() makes more sense |
||
235 |
// since no exception is pending here. |
||
236 |
return Undefined(isolate); |
||
237 |
} |
||
238 |
208 |
Local<Function> callback = callback_v.As<Function>(); |
|
239 |
208 |
return MakeCallback(isolate, recv, callback, argc, argv, asyncContext); |
|
240 |
} |
||
241 |
|||
242 |
208 |
MaybeLocal<Value> MakeCallback(Isolate* isolate, |
|
243 |
Local<Object> recv, |
||
244 |
Local<Function> callback, |
||
245 |
int argc, |
||
246 |
Local<Value> argv[], |
||
247 |
async_context asyncContext) { |
||
248 |
// Observe the following two subtleties: |
||
249 |
// |
||
250 |
// 1. The environment is retrieved from the callback function's context. |
||
251 |
// 2. The context to enter is retrieved from the environment. |
||
252 |
// |
||
253 |
// Because of the AssignToContext() call in src/node_contextify.cc, |
||
254 |
// the two contexts need not be the same. |
||
255 |
208 |
Environment* env = Environment::GetCurrent(callback->CreationContext()); |
|
256 |
✗✓ | 208 |
CHECK_NOT_NULL(env); |
257 |
208 |
Context::Scope context_scope(env->context()); |
|
258 |
MaybeLocal<Value> ret = |
||
259 |
208 |
InternalMakeCallback(env, recv, recv, callback, argc, argv, asyncContext); |
|
260 |
✗✓✗✗ ✗✓ |
208 |
if (ret.IsEmpty() && env->async_callback_scope_depth() == 0) { |
261 |
// This is only for legacy compatibility and we may want to look into |
||
262 |
// removing/adjusting it. |
||
263 |
return Undefined(env->isolate()); |
||
264 |
} |
||
265 |
208 |
return ret; |
|
266 |
} |
||
267 |
|||
268 |
// Use this if you just want to safely invoke some JS callback and |
||
269 |
// would like to retain the currently active async_context, if any. |
||
270 |
// In case none is available, a fixed default context will be |
||
271 |
// installed otherwise. |
||
272 |
MaybeLocal<Value> MakeSyncCallback(Isolate* isolate, |
||
273 |
Local<Object> recv, |
||
274 |
Local<Function> callback, |
||
275 |
int argc, |
||
276 |
Local<Value> argv[]) { |
||
277 |
Environment* env = Environment::GetCurrent(callback->CreationContext()); |
||
278 |
CHECK_NOT_NULL(env); |
||
279 |
if (!env->can_call_into_js()) return Local<Value>(); |
||
280 |
|||
281 |
Context::Scope context_scope(env->context()); |
||
282 |
if (env->async_callback_scope_depth()) { |
||
283 |
// There's another MakeCallback() on the stack, piggy back on it. |
||
284 |
// In particular, retain the current async_context. |
||
285 |
return callback->Call(env->context(), recv, argc, argv); |
||
286 |
} |
||
287 |
|||
288 |
// This is a toplevel invocation and the caller (intentionally) |
||
289 |
// didn't provide any async_context to run in. Install a default context. |
||
290 |
MaybeLocal<Value> ret = |
||
291 |
InternalMakeCallback(env, env->process_object(), recv, callback, argc, argv, |
||
292 |
async_context{0, 0}); |
||
293 |
return ret; |
||
294 |
} |
||
295 |
|||
296 |
// Legacy MakeCallback()s |
||
297 |
|||
298 |
Local<Value> MakeCallback(Isolate* isolate, |
||
299 |
Local<Object> recv, |
||
300 |
const char* method, |
||
301 |
int argc, |
||
302 |
Local<Value>* argv) { |
||
303 |
EscapableHandleScope handle_scope(isolate); |
||
304 |
return handle_scope.Escape( |
||
305 |
MakeCallback(isolate, recv, method, argc, argv, {0, 0}) |
||
306 |
.FromMaybe(Local<Value>())); |
||
307 |
} |
||
308 |
|||
309 |
Local<Value> MakeCallback(Isolate* isolate, |
||
310 |
Local<Object> recv, |
||
311 |
Local<String> symbol, |
||
312 |
int argc, |
||
313 |
Local<Value>* argv) { |
||
314 |
EscapableHandleScope handle_scope(isolate); |
||
315 |
return handle_scope.Escape( |
||
316 |
MakeCallback(isolate, recv, symbol, argc, argv, {0, 0}) |
||
317 |
.FromMaybe(Local<Value>())); |
||
318 |
} |
||
319 |
|||
320 |
Local<Value> MakeCallback(Isolate* isolate, |
||
321 |
Local<Object> recv, |
||
322 |
Local<Function> callback, |
||
323 |
int argc, |
||
324 |
Local<Value>* argv) { |
||
325 |
EscapableHandleScope handle_scope(isolate); |
||
326 |
return handle_scope.Escape( |
||
327 |
MakeCallback(isolate, recv, callback, argc, argv, {0, 0}) |
||
328 |
.FromMaybe(Local<Value>())); |
||
329 |
} |
||
330 |
|||
331 |
✓✗✓✗ |
366 |
} // namespace node |
Generated by: GCOVR (Version 3.4) |