GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
// Copyright Joyent, Inc. and other Node contributors. |
||
2 |
// |
||
3 |
// Permission is hereby granted, free of charge, to any person obtaining a |
||
4 |
// copy of this software and associated documentation files (the |
||
5 |
// "Software"), to deal in the Software without restriction, including |
||
6 |
// without limitation the rights to use, copy, modify, merge, publish, |
||
7 |
// distribute, sublicense, and/or sell copies of the Software, and to permit |
||
8 |
// persons to whom the Software is furnished to do so, subject to the |
||
9 |
// following conditions: |
||
10 |
// |
||
11 |
// The above copyright notice and this permission notice shall be included |
||
12 |
// in all copies or substantial portions of the Software. |
||
13 |
// |
||
14 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||
15 |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
16 |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
||
17 |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||
18 |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||
19 |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
||
20 |
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
21 |
|||
22 |
#include "async_wrap.h" // NOLINT(build/include_inline) |
||
23 |
#include "async_wrap-inl.h" |
||
24 |
#include "env-inl.h" |
||
25 |
#include "node_errors.h" |
||
26 |
#include "node_external_reference.h" |
||
27 |
#include "tracing/traced_value.h" |
||
28 |
#include "util-inl.h" |
||
29 |
|||
30 |
#include "v8.h" |
||
31 |
|||
32 |
using v8::Context; |
||
33 |
using v8::DontDelete; |
||
34 |
using v8::EscapableHandleScope; |
||
35 |
using v8::Function; |
||
36 |
using v8::FunctionCallbackInfo; |
||
37 |
using v8::FunctionTemplate; |
||
38 |
using v8::Global; |
||
39 |
using v8::HandleScope; |
||
40 |
using v8::Integer; |
||
41 |
using v8::Isolate; |
||
42 |
using v8::Just; |
||
43 |
using v8::Local; |
||
44 |
using v8::Maybe; |
||
45 |
using v8::MaybeLocal; |
||
46 |
using v8::Name; |
||
47 |
using v8::Nothing; |
||
48 |
using v8::Number; |
||
49 |
using v8::Object; |
||
50 |
using v8::ObjectTemplate; |
||
51 |
using v8::Promise; |
||
52 |
using v8::PromiseHookType; |
||
53 |
using v8::PropertyAttribute; |
||
54 |
using v8::PropertyCallbackInfo; |
||
55 |
using v8::ReadOnly; |
||
56 |
using v8::String; |
||
57 |
using v8::Undefined; |
||
58 |
using v8::Value; |
||
59 |
using v8::WeakCallbackInfo; |
||
60 |
using v8::WeakCallbackType; |
||
61 |
|||
62 |
using TryCatchScope = node::errors::TryCatchScope; |
||
63 |
|||
64 |
namespace node { |
||
65 |
|||
66 |
static const char* const provider_names[] = { |
||
67 |
#define V(PROVIDER) \ |
||
68 |
#PROVIDER, |
||
69 |
NODE_ASYNC_PROVIDER_TYPES(V) |
||
70 |
#undef V |
||
71 |
}; |
||
72 |
|||
73 |
22235 |
void AsyncWrap::DestroyAsyncIdsCallback(Environment* env) { |
|
74 |
22235 |
Local<Function> fn = env->async_hooks_destroy_function(); |
|
75 |
|||
76 |
44470 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
77 |
|||
78 |
✓✓✓✗ |
22237 |
do { |
79 |
44474 |
std::vector<double> destroy_async_id_list; |
|
80 |
22237 |
destroy_async_id_list.swap(*env->destroy_async_id_list()); |
|
81 |
✗✓ | 22237 |
if (!env->can_call_into_js()) return; |
82 |
✓✗✓✓ |
129585 |
for (auto async_id : destroy_async_id_list) { |
83 |
// Want each callback to be cleaned up after itself, instead of cleaning |
||
84 |
// them all up after the while() loop completes. |
||
85 |
214696 |
HandleScope scope(env->isolate()); |
|
86 |
107348 |
Local<Value> async_id_value = Number::New(env->isolate(), async_id); |
|
87 |
MaybeLocal<Value> ret = fn->Call( |
||
88 |
322044 |
env->context(), Undefined(env->isolate()), 1, &async_id_value); |
|
89 |
|||
90 |
✗✓ | 107348 |
if (ret.IsEmpty()) |
91 |
return; |
||
92 |
} |
||
93 |
22237 |
} while (!env->destroy_async_id_list()->empty()); |
|
94 |
} |
||
95 |
|||
96 |
146881 |
void Emit(Environment* env, double async_id, AsyncHooks::Fields type, |
|
97 |
Local<Function> fn) { |
||
98 |
146881 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
99 |
|||
100 |
✓✓✗✓ ✓✓ |
146881 |
if (async_hooks->fields()[type] == 0 || !env->can_call_into_js()) |
101 |
145441 |
return; |
|
102 |
|||
103 |
2880 |
HandleScope handle_scope(env->isolate()); |
|
104 |
1440 |
Local<Value> async_id_value = Number::New(env->isolate(), async_id); |
|
105 |
2880 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
106 |
4320 |
USE(fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value)); |
|
107 |
} |
||
108 |
|||
109 |
|||
110 |
21157 |
void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) { |
|
111 |
21157 |
Emit(env, async_id, AsyncHooks::kPromiseResolve, |
|
112 |
21157 |
env->async_hooks_promise_resolve_function()); |
|
113 |
21157 |
} |
|
114 |
|||
115 |
|||
116 |
588691 |
void AsyncWrap::EmitTraceEventBefore() { |
|
117 |
✗✗✗✗ ✓✗✓✓ ✗✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✓✓✓ ✓✓✓✗ ✗✗✗✗ ✗✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗ |
588691 |
switch (provider_type()) { |
118 |
#define V(PROVIDER) \ |
||
119 |
case PROVIDER_ ## PROVIDER: \ |
||
120 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( \ |
||
121 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
122 |
#PROVIDER "_CALLBACK", static_cast<int64_t>(get_async_id())); \ |
||
123 |
break; |
||
124 |
✗✗✓✓ ✓✓✓✗ ✓✓✓✗ ✗✓✗✗ ✓✓✓✓ ✓✗✓✓ ✓✓✓✓ ✗✓✗✗ ✗✗✗✗ ✗✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✗ ✓✓✓✗ ✓✓✓✗ ✓✓✓✗ ✓✓✓✗ ✓✗✗✗ ✗✓✓✗ ✓✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✗✓✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✓✓✗ ✓✓✓✗ ✓✓✓✗ ✓✓✓✗ ✓✓✓✗ ✓✓✓✗ ✗✓✓✓ ✗✓✓✓ ✗✓✓✗ ✗✓✓✓ ✗✓✗✗ ✗✗✓✓ ✓✓✗✗ ✗✗✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓ |
1177570 |
NODE_ASYNC_PROVIDER_TYPES(V) |
125 |
#undef V |
||
126 |
default: |
||
127 |
UNREACHABLE(); |
||
128 |
} |
||
129 |
588785 |
} |
|
130 |
|||
131 |
|||
132 |
62868 |
void AsyncWrap::EmitBefore(Environment* env, double async_id) { |
|
133 |
62868 |
Emit(env, async_id, AsyncHooks::kBefore, |
|
134 |
62868 |
env->async_hooks_before_function()); |
|
135 |
✗✗ | 62868 |
} |
136 |
|||
137 |
|||
138 |
588763 |
void AsyncWrap::EmitTraceEventAfter(ProviderType type, double async_id) { |
|
139 |
✗✗✗✗ ✓✗✓✓ ✗✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✓✓✓ ✓✓✓✗ ✗✗✗✗ ✗✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗ |
588763 |
switch (type) { |
140 |
#define V(PROVIDER) \ |
||
141 |
case PROVIDER_ ## PROVIDER: \ |
||
142 |
TRACE_EVENT_NESTABLE_ASYNC_END0( \ |
||
143 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
144 |
#PROVIDER "_CALLBACK", static_cast<int64_t>(async_id)); \ |
||
145 |
break; |
||
146 |
✗✗✓✓ ✓✗✓✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✗✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✗✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓ |
1177544 |
NODE_ASYNC_PROVIDER_TYPES(V) |
147 |
#undef V |
||
148 |
default: |
||
149 |
UNREACHABLE(); |
||
150 |
} |
||
151 |
588772 |
} |
|
152 |
|||
153 |
|||
154 |
62856 |
void AsyncWrap::EmitAfter(Environment* env, double async_id) { |
|
155 |
// If the user's callback failed then the after() hooks will be called at the |
||
156 |
// end of _fatalException(). |
||
157 |
✗✗ | 62856 |
Emit(env, async_id, AsyncHooks::kAfter, |
158 |
62856 |
env->async_hooks_after_function()); |
|
159 |
✗✗ | 62856 |
} |
160 |
|||
161 |
// TODO(addaleax): Remove once we're on C++17. |
||
162 |
constexpr double AsyncWrap::kInvalidAsyncId; |
||
163 |
|||
164 |
195 |
static Maybe<double> GetAssignedPromiseAsyncId(Environment* env, |
|
165 |
Local<Promise> promise, |
||
166 |
Local<Value> id_symbol) { |
||
167 |
Local<Value> maybe_async_id; |
||
168 |
✗✓✗✗ |
585 |
if (!promise->Get(env->context(), id_symbol).ToLocal(&maybe_async_id)) { |
169 |
return Nothing<double>(); |
||
170 |
} |
||
171 |
195 |
return maybe_async_id->IsNumber() |
|
172 |
250 |
? maybe_async_id->NumberValue(env->context()) |
|
173 |
✓✓ | 320 |
: Just(AsyncWrap::kInvalidAsyncId); |
174 |
} |
||
175 |
|||
176 |
42040 |
class PromiseWrap : public AsyncWrap { |
|
177 |
public: |
||
178 |
21201 |
PromiseWrap(Environment* env, Local<Object> object, bool silent) |
|
179 |
21201 |
: AsyncWrap(env, object, PROVIDER_PROMISE, kInvalidAsyncId, silent) { |
|
180 |
21201 |
MakeWeak(); |
|
181 |
21201 |
} |
|
182 |
|||
183 |
PromiseWrap(Environment* env, Local<Object> object, double asyncId, |
||
184 |
double triggerAsyncId) |
||
185 |
: AsyncWrap(env, object, PROVIDER_PROMISE, asyncId, triggerAsyncId) { |
||
186 |
MakeWeak(); |
||
187 |
} |
||
188 |
|||
189 |
5 |
SET_NO_MEMORY_INFO() |
|
190 |
5 |
SET_MEMORY_INFO_NAME(PromiseWrap) |
|
191 |
5 |
SET_SELF_SIZE(PromiseWrap) |
|
192 |
|||
193 |
static PromiseWrap* New(Environment* env, |
||
194 |
Local<Promise> promise, |
||
195 |
bool silent); |
||
196 |
static void GetAsyncId(Local<Name> property, |
||
197 |
const PropertyCallbackInfo<Value>& args); |
||
198 |
static void GetTriggerAsyncId(Local<Name> property, |
||
199 |
const PropertyCallbackInfo<Value>& args); |
||
200 |
|||
201 |
static void Initialize(Environment* env); |
||
202 |
}; |
||
203 |
|||
204 |
21201 |
PromiseWrap* PromiseWrap::New(Environment* env, |
|
205 |
Local<Promise> promise, |
||
206 |
bool silent) { |
||
207 |
21201 |
Local<Context> context = env->context(); |
|
208 |
|||
209 |
Local<Object> obj; |
||
210 |
✗✓ | 63603 |
if (!env->promise_wrap_template()->NewInstance(context).ToLocal(&obj)) |
211 |
return nullptr; |
||
212 |
|||
213 |
✗✓ | 42402 |
CHECK_NULL(promise->GetAlignedPointerFromInternalField(0)); |
214 |
21201 |
promise->SetInternalField(0, obj); |
|
215 |
|||
216 |
// Skip for init events |
||
217 |
✓✓ | 21201 |
if (silent) { |
218 |
double async_id; |
||
219 |
double trigger_async_id; |
||
220 |
✗✓ | 102 |
if (!GetAssignedPromiseAsyncId(env, promise, env->async_id_symbol()) |
221 |
34 |
.To(&async_id)) return nullptr; |
|
222 |
✗✓ | 102 |
if (!GetAssignedPromiseAsyncId(env, promise, env->trigger_async_id_symbol()) |
223 |
34 |
.To(&trigger_async_id)) return nullptr; |
|
224 |
|||
225 |
✗✓✗✗ |
34 |
if (async_id != AsyncWrap::kInvalidAsyncId && |
226 |
trigger_async_id != AsyncWrap::kInvalidAsyncId) { |
||
227 |
return new PromiseWrap( |
||
228 |
env, obj, async_id, trigger_async_id); |
||
229 |
} |
||
230 |
} |
||
231 |
|||
232 |
21201 |
return new PromiseWrap(env, obj, silent); |
|
233 |
} |
||
234 |
|||
235 |
7 |
void PromiseWrap::GetAsyncId(Local<Name> property, |
|
236 |
const PropertyCallbackInfo<Value>& info) { |
||
237 |
7 |
Isolate* isolate = info.GetIsolate(); |
|
238 |
14 |
HandleScope scope(isolate); |
|
239 |
|||
240 |
7 |
PromiseWrap* wrap = Unwrap<PromiseWrap>(info.Holder()); |
|
241 |
7 |
double value = wrap->get_async_id(); |
|
242 |
|||
243 |
21 |
info.GetReturnValue().Set(Number::New(isolate, value)); |
|
244 |
7 |
} |
|
245 |
|||
246 |
7 |
void PromiseWrap::GetTriggerAsyncId(Local<Name> property, |
|
247 |
const PropertyCallbackInfo<Value>& info) { |
||
248 |
7 |
Isolate* isolate = info.GetIsolate(); |
|
249 |
14 |
HandleScope scope(isolate); |
|
250 |
|||
251 |
7 |
PromiseWrap* wrap = Unwrap<PromiseWrap>(info.Holder()); |
|
252 |
7 |
double value = wrap->get_trigger_async_id(); |
|
253 |
|||
254 |
21 |
info.GetReturnValue().Set(Number::New(isolate, value)); |
|
255 |
7 |
} |
|
256 |
|||
257 |
444 |
void PromiseWrap::Initialize(Environment* env) { |
|
258 |
444 |
Isolate* isolate = env->isolate(); |
|
259 |
888 |
HandleScope scope(isolate); |
|
260 |
|||
261 |
444 |
Local<FunctionTemplate> ctor = FunctionTemplate::New(isolate); |
|
262 |
888 |
ctor->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "PromiseWrap")); |
|
263 |
|||
264 |
444 |
Local<ObjectTemplate> promise_wrap_template = ctor->InstanceTemplate(); |
|
265 |
444 |
env->set_promise_wrap_template(promise_wrap_template); |
|
266 |
|||
267 |
444 |
promise_wrap_template->SetInternalFieldCount( |
|
268 |
444 |
PromiseWrap::kInternalFieldCount); |
|
269 |
|||
270 |
1332 |
promise_wrap_template->SetAccessor( |
|
271 |
env->async_id_symbol(), |
||
272 |
444 |
PromiseWrap::GetAsyncId); |
|
273 |
|||
274 |
1332 |
promise_wrap_template->SetAccessor( |
|
275 |
env->trigger_async_id_symbol(), |
||
276 |
444 |
PromiseWrap::GetTriggerAsyncId); |
|
277 |
444 |
} |
|
278 |
|||
279 |
74210 |
static PromiseWrap* extractPromiseWrap(Local<Promise> promise) { |
|
280 |
// This check is imperfect. If the internal field is set, it should |
||
281 |
// be an object. If it's not, we just ignore it. Ideally v8 would |
||
282 |
// have had GetInternalField returning a MaybeLocal but this works |
||
283 |
// for now. |
||
284 |
148420 |
Local<Value> obj = promise->GetInternalField(0); |
|
285 |
✓✓ | 127219 |
return obj->IsObject() ? Unwrap<PromiseWrap>(obj.As<Object>()) : nullptr; |
286 |
} |
||
287 |
|||
288 |
108 |
static uint16_t ToAsyncHooksType(PromiseHookType type) { |
|
289 |
✓✓✓✓ ✗ |
108 |
switch (type) { |
290 |
98 |
case PromiseHookType::kInit: return AsyncHooks::kInit; |
|
291 |
4 |
case PromiseHookType::kBefore: return AsyncHooks::kBefore; |
|
292 |
4 |
case PromiseHookType::kAfter: return AsyncHooks::kAfter; |
|
293 |
2 |
case PromiseHookType::kResolve: return AsyncHooks::kPromiseResolve; |
|
294 |
} |
||
295 |
UNREACHABLE(); |
||
296 |
} |
||
297 |
|||
298 |
// Simplified JavaScript hook fast-path for when there is no destroy hook |
||
299 |
300 |
static void FastPromiseHook(PromiseHookType type, Local<Promise> promise, |
|
300 |
Local<Value> parent) { |
||
301 |
300 |
Local<Context> context = promise->CreationContext(); |
|
302 |
300 |
Environment* env = Environment::GetCurrent(context); |
|
303 |
✗✓ | 492 |
if (env == nullptr) return; |
304 |
|||
305 |
✓✓✓✓ ✓✓ |
645 |
if (type == PromiseHookType::kBefore && |
306 |
345 |
env->async_hooks()->fields()[AsyncHooks::kBefore] == 0) { |
|
307 |
double async_id; |
||
308 |
double trigger_async_id; |
||
309 |
✗✓ | 126 |
if (!GetAssignedPromiseAsyncId(env, promise, env->async_id_symbol()) |
310 |
83 |
.To(&async_id)) return; |
|
311 |
✗✓ | 126 |
if (!GetAssignedPromiseAsyncId(env, promise, env->trigger_async_id_symbol()) |
312 |
42 |
.To(&trigger_async_id)) return; |
|
313 |
|||
314 |
✓✓✓✗ |
83 |
if (async_id != AsyncWrap::kInvalidAsyncId && |
315 |
41 |
trigger_async_id != AsyncWrap::kInvalidAsyncId) { |
|
316 |
82 |
env->async_hooks()->push_async_context( |
|
317 |
82 |
async_id, trigger_async_id, promise); |
|
318 |
41 |
return; |
|
319 |
} |
||
320 |
} |
||
321 |
|||
322 |
✓✓✓✓ ✓✓ |
565 |
if (type == PromiseHookType::kAfter && |
323 |
306 |
env->async_hooks()->fields()[AsyncHooks::kAfter] == 0) { |
|
324 |
double async_id; |
||
325 |
✗✓ | 129 |
if (!GetAssignedPromiseAsyncId(env, promise, env->async_id_symbol()) |
326 |
86 |
.To(&async_id)) return; |
|
327 |
|||
328 |
✓✗ | 43 |
if (async_id != AsyncWrap::kInvalidAsyncId) { |
329 |
✓✓ | 43 |
if (env->execution_async_id() == async_id) { |
330 |
// This condition might not be true if async_hooks was enabled during |
||
331 |
// the promise callback execution. |
||
332 |
41 |
env->async_hooks()->pop_async_context(async_id); |
|
333 |
} |
||
334 |
43 |
return; |
|
335 |
} |
||
336 |
} |
||
337 |
|||
338 |
✓✓✓✓ ✓✓ |
542 |
if (type == PromiseHookType::kResolve && |
339 |
326 |
env->async_hooks()->fields()[AsyncHooks::kPromiseResolve] == 0) { |
|
340 |
108 |
return; |
|
341 |
} |
||
342 |
|||
343 |
// Getting up to this point means either init type or |
||
344 |
// that there are active hooks of another type. |
||
345 |
// In both cases fast-path JS hook should be called. |
||
346 |
|||
347 |
Local<Value> argv[] = { |
||
348 |
108 |
Integer::New(env->isolate(), ToAsyncHooksType(type)), |
|
349 |
promise, |
||
350 |
parent |
||
351 |
432 |
}; |
|
352 |
|||
353 |
216 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
354 |
108 |
Local<Function> promise_hook = env->promise_hook_handler(); |
|
355 |
216 |
USE(promise_hook->Call(context, Undefined(env->isolate()), 3, argv)); |
|
356 |
} |
||
357 |
|||
358 |
63572 |
static void FullPromiseHook(PromiseHookType type, Local<Promise> promise, |
|
359 |
Local<Value> parent) { |
||
360 |
63572 |
Local<Context> context = promise->CreationContext(); |
|
361 |
|||
362 |
63572 |
Environment* env = Environment::GetCurrent(context); |
|
363 |
✗✓ | 63572 |
if (env == nullptr) return; |
364 |
TraceEventScope trace_scope(TRACING_CATEGORY_NODE1(environment), |
||
365 |
127144 |
"EnvPromiseHook", env); |
|
366 |
|||
367 |
63572 |
PromiseWrap* wrap = extractPromiseWrap(promise); |
|
368 |
✓✓✓✓ |
63572 |
if (type == PromiseHookType::kInit || wrap == nullptr) { |
369 |
21180 |
bool silent = type != PromiseHookType::kInit; |
|
370 |
|||
371 |
// set parent promise's async Id as this promise's triggerAsyncId |
||
372 |
✓✓ | 21180 |
if (parent->IsPromise()) { |
373 |
// parent promise exists, current promise |
||
374 |
// is a chained promise, so we set parent promise's id as |
||
375 |
// current promise's triggerAsyncId |
||
376 |
10638 |
Local<Promise> parent_promise = parent.As<Promise>(); |
|
377 |
10638 |
PromiseWrap* parent_wrap = extractPromiseWrap(parent_promise); |
|
378 |
✓✓ | 10638 |
if (parent_wrap == nullptr) { |
379 |
21 |
parent_wrap = PromiseWrap::New(env, parent_promise, true); |
|
380 |
✗✓ | 21 |
if (parent_wrap == nullptr) return; |
381 |
} |
||
382 |
|||
383 |
21276 |
AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(parent_wrap); |
|
384 |
10638 |
wrap = PromiseWrap::New(env, promise, silent); |
|
385 |
} else { |
||
386 |
10542 |
wrap = PromiseWrap::New(env, promise, silent); |
|
387 |
} |
||
388 |
} |
||
389 |
|||
390 |
✗✓ | 63572 |
if (wrap == nullptr) return; |
391 |
|||
392 |
✓✓ | 63572 |
if (type == PromiseHookType::kBefore) { |
393 |
10624 |
env->async_hooks()->push_async_context(wrap->get_async_id(), |
|
394 |
10624 |
wrap->get_trigger_async_id(), wrap->object()); |
|
395 |
10624 |
wrap->EmitTraceEventBefore(); |
|
396 |
10624 |
AsyncWrap::EmitBefore(wrap->env(), wrap->get_async_id()); |
|
397 |
✓✓ | 52948 |
} else if (type == PromiseHookType::kAfter) { |
398 |
10624 |
wrap->EmitTraceEventAfter(wrap->provider_type(), wrap->get_async_id()); |
|
399 |
10624 |
AsyncWrap::EmitAfter(wrap->env(), wrap->get_async_id()); |
|
400 |
✓✓ | 10624 |
if (env->execution_async_id() == wrap->get_async_id()) { |
401 |
// This condition might not be true if async_hooks was enabled during |
||
402 |
// the promise callback execution. |
||
403 |
// Popping it off the stack can be skipped in that case, because it is |
||
404 |
// known that it would correspond to exactly one call with |
||
405 |
// PromiseHookType::kBefore that was not witnessed by the PromiseHook. |
||
406 |
10622 |
env->async_hooks()->pop_async_context(wrap->get_async_id()); |
|
407 |
} |
||
408 |
✓✓ | 42324 |
} else if (type == PromiseHookType::kResolve) { |
409 |
✓✗ | 21157 |
AsyncWrap::EmitPromiseResolve(wrap->env(), wrap->get_async_id()); |
410 |
} |
||
411 |
} |
||
412 |
|||
413 |
444 |
static void SetupHooks(const FunctionCallbackInfo<Value>& args) { |
|
414 |
444 |
Environment* env = Environment::GetCurrent(args); |
|
415 |
|||
416 |
✗✓ | 888 |
CHECK(args[0]->IsObject()); |
417 |
|||
418 |
// All of init, before, after, destroy, and promise_resolve are supplied by |
||
419 |
// async_hooks internally, so this should only ever be called once. At which |
||
420 |
// time all the functions should be set. Detect this by checking if |
||
421 |
// init !IsEmpty(). |
||
422 |
✗✓ | 888 |
CHECK(env->async_hooks_init_function().IsEmpty()); |
423 |
|||
424 |
888 |
Local<Object> fn_obj = args[0].As<Object>(); |
|
425 |
|||
426 |
#define SET_HOOK_FN(name) \ |
||
427 |
do { \ |
||
428 |
Local<Value> v = \ |
||
429 |
fn_obj->Get(env->context(), \ |
||
430 |
FIXED_ONE_BYTE_STRING(env->isolate(), #name)) \ |
||
431 |
.ToLocalChecked(); \ |
||
432 |
CHECK(v->IsFunction()); \ |
||
433 |
env->set_async_hooks_##name##_function(v.As<Function>()); \ |
||
434 |
} while (0) |
||
435 |
|||
436 |
✗✓ | 2664 |
SET_HOOK_FN(init); |
437 |
✗✓ | 2664 |
SET_HOOK_FN(before); |
438 |
✗✓ | 2664 |
SET_HOOK_FN(after); |
439 |
✗✓ | 2664 |
SET_HOOK_FN(destroy); |
440 |
✗✓ | 2664 |
SET_HOOK_FN(promise_resolve); |
441 |
#undef SET_HOOK_FN |
||
442 |
444 |
} |
|
443 |
|||
444 |
301 |
static void EnablePromiseHook(const FunctionCallbackInfo<Value>& args) { |
|
445 |
301 |
Environment* env = Environment::GetCurrent(args); |
|
446 |
|||
447 |
✓✓ | 602 |
if (args[0]->IsFunction()) { |
448 |
112 |
env->set_promise_hook_handler(args[0].As<Function>()); |
|
449 |
56 |
args.GetIsolate()->SetPromiseHook(FastPromiseHook); |
|
450 |
} else { |
||
451 |
245 |
args.GetIsolate()->SetPromiseHook(FullPromiseHook); |
|
452 |
} |
||
453 |
301 |
} |
|
454 |
|||
455 |
|||
456 |
64 |
static void DisablePromiseHook(const FunctionCallbackInfo<Value>& args) { |
|
457 |
64 |
Environment* env = Environment::GetCurrent(args); |
|
458 |
64 |
env->set_promise_hook_handler(Local<Function>()); |
|
459 |
|||
460 |
// The per-Isolate API provides no way of knowing whether there are multiple |
||
461 |
// users of the PromiseHook. That hopefully goes away when V8 introduces |
||
462 |
// a per-context API. |
||
463 |
64 |
args.GetIsolate()->SetPromiseHook(nullptr); |
|
464 |
64 |
} |
|
465 |
|||
466 |
|||
467 |
14931 |
class DestroyParam { |
|
468 |
public: |
||
469 |
double asyncId; |
||
470 |
Environment* env; |
||
471 |
Global<Object> target; |
||
472 |
Global<Object> propBag; |
||
473 |
}; |
||
474 |
|||
475 |
11 |
static void DestroyParamCleanupHook(void* ptr) { |
|
476 |
✓✗ | 11 |
delete static_cast<DestroyParam*>(ptr); |
477 |
11 |
} |
|
478 |
|||
479 |
2477 |
void AsyncWrap::WeakCallback(const WeakCallbackInfo<DestroyParam>& info) { |
|
480 |
4954 |
HandleScope scope(info.GetIsolate()); |
|
481 |
|||
482 |
✓✗ | 4954 |
std::unique_ptr<DestroyParam> p{info.GetParameter()}; |
483 |
Local<Object> prop_bag = PersistentToLocal::Default(info.GetIsolate(), |
||
484 |
4954 |
p->propBag); |
|
485 |
Local<Value> val; |
||
486 |
|||
487 |
2477 |
p->env->RemoveCleanupHook(DestroyParamCleanupHook, p.get()); |
|
488 |
|||
489 |
✗✓ | 9908 |
if (!prop_bag->Get(p->env->context(), p->env->destroyed_string()) |
490 |
2477 |
.ToLocal(&val)) { |
|
491 |
return; |
||
492 |
} |
||
493 |
|||
494 |
✓✓ | 2477 |
if (val->IsFalse()) { |
495 |
✓✗ | 2472 |
AsyncWrap::EmitDestroy(p->env, p->asyncId); |
496 |
} |
||
497 |
// unique_ptr goes out of scope here and pointer is deleted. |
||
498 |
} |
||
499 |
|||
500 |
|||
501 |
2489 |
static void RegisterDestroyHook(const FunctionCallbackInfo<Value>& args) { |
|
502 |
✗✓ | 4978 |
CHECK(args[0]->IsObject()); |
503 |
✗✓ | 4978 |
CHECK(args[1]->IsNumber()); |
504 |
✗✓ | 4978 |
CHECK(args[2]->IsObject()); |
505 |
|||
506 |
2489 |
Isolate* isolate = args.GetIsolate(); |
|
507 |
2489 |
DestroyParam* p = new DestroyParam(); |
|
508 |
7467 |
p->asyncId = args[1].As<Number>()->Value(); |
|
509 |
2489 |
p->env = Environment::GetCurrent(args); |
|
510 |
7467 |
p->target.Reset(isolate, args[0].As<Object>()); |
|
511 |
7467 |
p->propBag.Reset(isolate, args[2].As<Object>()); |
|
512 |
2489 |
p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter); |
|
513 |
2489 |
p->env->AddCleanupHook(DestroyParamCleanupHook, p); |
|
514 |
2489 |
} |
|
515 |
|||
516 |
94837 |
void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) { |
|
517 |
AsyncWrap* wrap; |
||
518 |
189674 |
args.GetReturnValue().Set(kInvalidAsyncId); |
|
519 |
✓✓ | 94837 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
520 |
284448 |
args.GetReturnValue().Set(wrap->get_async_id()); |
|
521 |
} |
||
522 |
|||
523 |
|||
524 |
10 |
void AsyncWrap::PushAsyncContext(const FunctionCallbackInfo<Value>& args) { |
|
525 |
10 |
Environment* env = Environment::GetCurrent(args); |
|
526 |
// No need for CHECK(IsNumber()) on args because if FromJust() doesn't fail |
||
527 |
// then the checks in push_async_ids() and pop_async_id() will. |
||
528 |
40 |
double async_id = args[0]->NumberValue(env->context()).FromJust(); |
|
529 |
40 |
double trigger_async_id = args[1]->NumberValue(env->context()).FromJust(); |
|
530 |
10 |
env->async_hooks()->push_async_context(async_id, trigger_async_id, {}); |
|
531 |
10 |
} |
|
532 |
|||
533 |
|||
534 |
3 |
void AsyncWrap::PopAsyncContext(const FunctionCallbackInfo<Value>& args) { |
|
535 |
3 |
Environment* env = Environment::GetCurrent(args); |
|
536 |
12 |
double async_id = args[0]->NumberValue(env->context()).FromJust(); |
|
537 |
3 |
args.GetReturnValue().Set(env->async_hooks()->pop_async_context(async_id)); |
|
538 |
} |
||
539 |
|||
540 |
|||
541 |
278 |
void AsyncWrap::ExecutionAsyncResource( |
|
542 |
const FunctionCallbackInfo<Value>& args) { |
||
543 |
278 |
Environment* env = Environment::GetCurrent(args); |
|
544 |
uint32_t index; |
||
545 |
✗✓ | 1112 |
if (!args[0]->Uint32Value(env->context()).To(&index)) return; |
546 |
834 |
args.GetReturnValue().Set( |
|
547 |
env->async_hooks()->native_execution_async_resource(index)); |
||
548 |
} |
||
549 |
|||
550 |
|||
551 |
1223 |
void AsyncWrap::ClearAsyncIdStack(const FunctionCallbackInfo<Value>& args) { |
|
552 |
1223 |
Environment* env = Environment::GetCurrent(args); |
|
553 |
1223 |
env->async_hooks()->clear_async_id_stack(); |
|
554 |
1223 |
} |
|
555 |
|||
556 |
|||
557 |
285 |
void AsyncWrap::AsyncReset(const FunctionCallbackInfo<Value>& args) { |
|
558 |
✗✓ | 570 |
CHECK(args[0]->IsObject()); |
559 |
|||
560 |
AsyncWrap* wrap; |
||
561 |
✗✓ | 285 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
562 |
|||
563 |
570 |
Local<Object> resource = args[0].As<Object>(); |
|
564 |
double execution_async_id = |
||
565 |
✗✓ | 570 |
args[1]->IsNumber() ? args[1].As<Number>()->Value() : kInvalidAsyncId; |
566 |
285 |
wrap->AsyncReset(resource, execution_async_id); |
|
567 |
} |
||
568 |
|||
569 |
|||
570 |
285 |
void AsyncWrap::GetProviderType(const FunctionCallbackInfo<Value>& args) { |
|
571 |
AsyncWrap* wrap; |
||
572 |
570 |
args.GetReturnValue().Set(AsyncWrap::PROVIDER_NONE); |
|
573 |
✗✓ | 285 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
574 |
855 |
args.GetReturnValue().Set(wrap->provider_type()); |
|
575 |
} |
||
576 |
|||
577 |
|||
578 |
218049 |
void AsyncWrap::EmitDestroy(bool from_gc) { |
|
579 |
218049 |
AsyncWrap::EmitDestroy(env(), async_id_); |
|
580 |
// Ensure no double destroy is emitted via AsyncReset(). |
||
581 |
218049 |
async_id_ = kInvalidAsyncId; |
|
582 |
|||
583 |
✓✓✓✓ ✓✓ |
436098 |
if (!persistent().IsEmpty() && !from_gc) { |
584 |
12638 |
HandleScope handle_scope(env()->isolate()); |
|
585 |
25276 |
USE(object()->Set(env()->context(), env()->resource_symbol(), object())); |
|
586 |
} |
||
587 |
218049 |
} |
|
588 |
|||
589 |
67729 |
void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo<Value>& args) { |
|
590 |
✗✓ | 135458 |
CHECK(args[0]->IsNumber()); |
591 |
67729 |
AsyncWrap::EmitDestroy( |
|
592 |
Environment::GetCurrent(args), |
||
593 |
203187 |
args[0].As<Number>()->Value()); |
|
594 |
67729 |
} |
|
595 |
|||
596 |
444 |
void AsyncWrap::SetCallbackTrampoline(const FunctionCallbackInfo<Value>& args) { |
|
597 |
444 |
Environment* env = Environment::GetCurrent(args); |
|
598 |
|||
599 |
✗✓ | 888 |
CHECK(args[0]->IsFunction()); |
600 |
|||
601 |
888 |
env->set_async_hooks_callback_trampoline(args[0].As<Function>()); |
|
602 |
444 |
} |
|
603 |
|||
604 |
75895 |
Local<FunctionTemplate> AsyncWrap::GetConstructorTemplate(Environment* env) { |
|
605 |
75895 |
Local<FunctionTemplate> tmpl = env->async_wrap_ctor_template(); |
|
606 |
✓✓ | 75895 |
if (tmpl.IsEmpty()) { |
607 |
444 |
tmpl = env->NewFunctionTemplate(nullptr); |
|
608 |
888 |
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "AsyncWrap")); |
|
609 |
888 |
tmpl->Inherit(BaseObject::GetConstructorTemplate(env)); |
|
610 |
444 |
env->SetProtoMethod(tmpl, "getAsyncId", AsyncWrap::GetAsyncId); |
|
611 |
444 |
env->SetProtoMethod(tmpl, "asyncReset", AsyncWrap::AsyncReset); |
|
612 |
444 |
env->SetProtoMethod(tmpl, "getProviderType", AsyncWrap::GetProviderType); |
|
613 |
444 |
env->set_async_wrap_ctor_template(tmpl); |
|
614 |
} |
||
615 |
75895 |
return tmpl; |
|
616 |
} |
||
617 |
|||
618 |
444 |
void AsyncWrap::Initialize(Local<Object> target, |
|
619 |
Local<Value> unused, |
||
620 |
Local<Context> context, |
||
621 |
void* priv) { |
||
622 |
444 |
Environment* env = Environment::GetCurrent(context); |
|
623 |
444 |
Isolate* isolate = env->isolate(); |
|
624 |
888 |
HandleScope scope(isolate); |
|
625 |
|||
626 |
444 |
env->SetMethod(target, "setupHooks", SetupHooks); |
|
627 |
444 |
env->SetMethod(target, "setCallbackTrampoline", SetCallbackTrampoline); |
|
628 |
444 |
env->SetMethod(target, "pushAsyncContext", PushAsyncContext); |
|
629 |
444 |
env->SetMethod(target, "popAsyncContext", PopAsyncContext); |
|
630 |
444 |
env->SetMethod(target, "executionAsyncResource", ExecutionAsyncResource); |
|
631 |
444 |
env->SetMethod(target, "clearAsyncIdStack", ClearAsyncIdStack); |
|
632 |
444 |
env->SetMethod(target, "queueDestroyAsyncId", QueueDestroyAsyncId); |
|
633 |
444 |
env->SetMethod(target, "enablePromiseHook", EnablePromiseHook); |
|
634 |
444 |
env->SetMethod(target, "disablePromiseHook", DisablePromiseHook); |
|
635 |
444 |
env->SetMethod(target, "registerDestroyHook", RegisterDestroyHook); |
|
636 |
|||
637 |
PropertyAttribute ReadOnlyDontDelete = |
||
638 |
444 |
static_cast<PropertyAttribute>(ReadOnly | DontDelete); |
|
639 |
|||
640 |
#define FORCE_SET_TARGET_FIELD(obj, str, field) \ |
||
641 |
(obj)->DefineOwnProperty(context, \ |
||
642 |
FIXED_ONE_BYTE_STRING(isolate, str), \ |
||
643 |
field, \ |
||
644 |
ReadOnlyDontDelete).FromJust() |
||
645 |
|||
646 |
// Attach the uint32_t[] where each slot contains the count of the number of |
||
647 |
// callbacks waiting to be called on a particular event. It can then be |
||
648 |
// incremented/decremented from JS quickly to communicate to C++ if there are |
||
649 |
// any callbacks waiting to be called. |
||
650 |
1776 |
FORCE_SET_TARGET_FIELD(target, |
|
651 |
"async_hook_fields", |
||
652 |
env->async_hooks()->fields().GetJSArray()); |
||
653 |
|||
654 |
// The following v8::Float64Array has 5 fields. These fields are shared in |
||
655 |
// this way to allow JS and C++ to read/write each value as quickly as |
||
656 |
// possible. The fields are represented as follows: |
||
657 |
// |
||
658 |
// kAsyncIdCounter: Maintains the state of the next unique id to be assigned. |
||
659 |
// |
||
660 |
// kDefaultTriggerAsyncId: Write the id of the resource responsible for a |
||
661 |
// handle's creation just before calling the new handle's constructor. |
||
662 |
// After the new handle is constructed kDefaultTriggerAsyncId is set back |
||
663 |
// to kInvalidAsyncId. |
||
664 |
1776 |
FORCE_SET_TARGET_FIELD(target, |
|
665 |
"async_id_fields", |
||
666 |
env->async_hooks()->async_id_fields().GetJSArray()); |
||
667 |
|||
668 |
1776 |
FORCE_SET_TARGET_FIELD(target, |
|
669 |
"execution_async_resources", |
||
670 |
env->async_hooks()->js_execution_async_resources()); |
||
671 |
|||
672 |
888 |
target->Set(context, |
|
673 |
env->async_ids_stack_string(), |
||
674 |
1776 |
env->async_hooks()->async_ids_stack().GetJSArray()).Check(); |
|
675 |
|||
676 |
444 |
Local<Object> constants = Object::New(isolate); |
|
677 |
#define SET_HOOKS_CONSTANT(name) \ |
||
678 |
FORCE_SET_TARGET_FIELD( \ |
||
679 |
constants, #name, Integer::New(isolate, AsyncHooks::name)) |
||
680 |
|||
681 |
1776 |
SET_HOOKS_CONSTANT(kInit); |
|
682 |
1776 |
SET_HOOKS_CONSTANT(kBefore); |
|
683 |
1776 |
SET_HOOKS_CONSTANT(kAfter); |
|
684 |
1776 |
SET_HOOKS_CONSTANT(kDestroy); |
|
685 |
1776 |
SET_HOOKS_CONSTANT(kPromiseResolve); |
|
686 |
1776 |
SET_HOOKS_CONSTANT(kTotals); |
|
687 |
1776 |
SET_HOOKS_CONSTANT(kCheck); |
|
688 |
1776 |
SET_HOOKS_CONSTANT(kExecutionAsyncId); |
|
689 |
1776 |
SET_HOOKS_CONSTANT(kTriggerAsyncId); |
|
690 |
1776 |
SET_HOOKS_CONSTANT(kAsyncIdCounter); |
|
691 |
1776 |
SET_HOOKS_CONSTANT(kDefaultTriggerAsyncId); |
|
692 |
1776 |
SET_HOOKS_CONSTANT(kUsesExecutionAsyncResource); |
|
693 |
1776 |
SET_HOOKS_CONSTANT(kStackLength); |
|
694 |
#undef SET_HOOKS_CONSTANT |
||
695 |
1332 |
FORCE_SET_TARGET_FIELD(target, "constants", constants); |
|
696 |
|||
697 |
444 |
Local<Object> async_providers = Object::New(isolate); |
|
698 |
#define V(p) \ |
||
699 |
FORCE_SET_TARGET_FIELD( \ |
||
700 |
async_providers, #p, Integer::New(isolate, AsyncWrap::PROVIDER_ ## p)); |
||
701 |
55056 |
NODE_ASYNC_PROVIDER_TYPES(V) |
|
702 |
#undef V |
||
703 |
3996 |
FORCE_SET_TARGET_FIELD(target, "Providers", async_providers); |
|
704 |
|||
705 |
2664 |
#undef FORCE_SET_TARGET_FIELD |
|
706 |
2664 |
||
707 |
2220 |
env->set_async_hooks_init_function(Local<Function>()); |
|
708 |
1332 |
env->set_async_hooks_before_function(Local<Function>()); |
|
709 |
3108 |
env->set_async_hooks_after_function(Local<Function>()); |
|
710 |
2220 |
env->set_async_hooks_destroy_function(Local<Function>()); |
|
711 |
1332 |
env->set_async_hooks_promise_resolve_function(Local<Function>()); |
|
712 |
3108 |
env->set_async_hooks_binding(target); |
|
713 |
1776 |
||
714 |
888 |
// TODO(qard): maybe this should be GetConstructorTemplate instead? |
|
715 |
3108 |
PromiseWrap::Initialize(env); |
|
716 |
2220 |
} |
|
717 |
888 |
||
718 |
7278 |
void AsyncWrap::RegisterExternalReferences( |
|
719 |
2664 |
ExternalReferenceRegistry* registry) { |
|
720 |
6390 |
registry->Register(SetupHooks); |
|
721 |
5502 |
registry->Register(SetCallbackTrampoline); |
|
722 |
7278 |
registry->Register(PushAsyncContext); |
|
723 |
6390 |
registry->Register(PopAsyncContext); |
|
724 |
5502 |
registry->Register(ExecutionAsyncResource); |
|
725 |
7278 |
registry->Register(ClearAsyncIdStack); |
|
726 |
6390 |
registry->Register(QueueDestroyAsyncId); |
|
727 |
5502 |
registry->Register(EnablePromiseHook); |
|
728 |
7278 |
registry->Register(DisablePromiseHook); |
|
729 |
7278 |
registry->Register(RegisterDestroyHook); |
|
730 |
4614 |
registry->Register(AsyncWrap::GetAsyncId); |
|
731 |
7278 |
registry->Register(AsyncWrap::AsyncReset); |
|
732 |
7278 |
registry->Register(AsyncWrap::GetProviderType); |
|
733 |
4614 |
registry->Register(PromiseWrap::GetAsyncId); |
|
734 |
4614 |
registry->Register(PromiseWrap::GetTriggerAsyncId); |
|
735 |
4614 |
} |
|
736 |
|||
737 |
187822 |
AsyncWrap::AsyncWrap(Environment* env, |
|
738 |
Local<Object> object, |
||
739 |
ProviderType provider, |
||
740 |
187822 |
double execution_async_id) |
|
741 |
187822 |
: AsyncWrap(env, object, provider, execution_async_id, false) {} |
|
742 |
|||
743 |
209023 |
AsyncWrap::AsyncWrap(Environment* env, |
|
744 |
Local<Object> object, |
||
745 |
ProviderType provider, |
||
746 |
double execution_async_id, |
||
747 |
209023 |
bool silent) |
|
748 |
209023 |
: AsyncWrap(env, object) { |
|
749 |
✗✓ | 209023 |
CHECK_NE(provider, PROVIDER_NONE); |
750 |
209023 |
provider_type_ = provider; |
|
751 |
|||
752 |
// Use AsyncReset() call to execute the init() callbacks. |
||
753 |
209023 |
AsyncReset(object, execution_async_id, silent); |
|
754 |
209023 |
init_hook_ran_ = true; |
|
755 |
209023 |
} |
|
756 |
|||
757 |
AsyncWrap::AsyncWrap(Environment* env, |
||
758 |
Local<Object> object, |
||
759 |
ProviderType provider, |
||
760 |
double execution_async_id, |
||
761 |
double trigger_async_id) |
||
762 |
: AsyncWrap(env, object, provider, execution_async_id, true) { |
||
763 |
trigger_async_id_ = trigger_async_id; |
||
764 |
} |
||
765 |
|||
766 |
212611 |
AsyncWrap::AsyncWrap(Environment* env, Local<Object> object) |
|
767 |
212611 |
: BaseObject(env, object) { |
|
768 |
212611 |
} |
|
769 |
|||
770 |
// This method is necessary to work around one specific problem: |
||
771 |
// Before the init() hook runs, if there is one, the BaseObject() constructor |
||
772 |
// registers this object with the Environment for finalization and debugging |
||
773 |
// purposes. |
||
774 |
// If the Environment decides to inspect this object for debugging, it tries to |
||
775 |
// call virtual methods on this object that are only (meaningfully) implemented |
||
776 |
// by the subclasses of AsyncWrap. |
||
777 |
// This could, with bad luck, happen during the AsyncWrap() constructor, |
||
778 |
// because we run JS code as part of it and that in turn can lead to a heapdump |
||
779 |
// being taken, either through the inspector or our programmatic API for it. |
||
780 |
// The object being initialized is not fully constructed at that point, and |
||
781 |
// in particular its virtual function table points to the AsyncWrap one |
||
782 |
// (as the subclass constructor has not yet begun execution at that point). |
||
783 |
// This means that the functions that are used for heap dump memory tracking |
||
784 |
// are not yet available, and trying to call them would crash the process. |
||
785 |
// We use this particular `IsDoneInitializing()` method to tell the Environment |
||
786 |
// that such debugging methods are not yet available. |
||
787 |
// This may be somewhat unreliable when it comes to future changes, because |
||
788 |
// at this point it *only* protects AsyncWrap subclasses, and *only* for cases |
||
789 |
// where heap dumps are being taken while the init() hook is on the call stack. |
||
790 |
// For now, it seems like the best solution, though. |
||
791 |
450242 |
bool AsyncWrap::IsDoneInitializing() const { |
|
792 |
450242 |
return init_hook_ran_; |
|
793 |
} |
||
794 |
|||
795 |
423460 |
AsyncWrap::~AsyncWrap() { |
|
796 |
211730 |
EmitTraceEventDestroy(); |
|
797 |
211730 |
EmitDestroy(true /* from gc */); |
|
798 |
211731 |
} |
|
799 |
|||
800 |
217569 |
void AsyncWrap::EmitTraceEventDestroy() { |
|
801 |
✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✓✓✓ ✓✓✓✗ ✗✗✗✗ ✗✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗ |
217569 |
switch (provider_type()) { |
802 |
#define V(PROVIDER) \ |
||
803 |
case PROVIDER_ ## PROVIDER: \ |
||
804 |
TRACE_EVENT_NESTABLE_ASYNC_END0( \ |
||
805 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
806 |
#PROVIDER, static_cast<int64_t>(get_async_id())); \ |
||
807 |
break; |
||
808 |
✗✗✗✗ ✓✓✗✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✓ ✓✓✓✓ ✓✗✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✓✗ ✓✓✓✓ ✓✓✗✓ ✗✗✓✓ ✗✗✓✓ ✓✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✓✓✓✓ ✗✓✗✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✗ ✗✗✗✓ ✓✓✓✓ ✓✗✓✗ ✓✓✗✓ ✓✓✓✓ ✗✓✓✗ ✓✓✓✓ ✓✓✓✗ ✓✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✓✓✓ ✗✓✓✓ ✗✓✓✓ ✗✓✗✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✗ ✓✓✗✓ ✓✗✓✗ ✓✗✓✓ ✗✓✗✗ ✗✗✓✓ ✓✓✗✗ ✗✗✓✓ ✗✓✓✓ ✗✓✗✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✓ ✓✗ |
435138 |
NODE_ASYNC_PROVIDER_TYPES(V) |
809 |
#undef V |
||
810 |
default: |
||
811 |
UNREACHABLE(); |
||
812 |
} |
||
813 |
217569 |
} |
|
814 |
|||
815 |
288798 |
void AsyncWrap::EmitDestroy(Environment* env, double async_id) { |
|
816 |
✓✓✓✓ ✓✓ |
396627 |
if (env->async_hooks()->fields()[AsyncHooks::kDestroy] == 0 || |
817 |
107829 |
!env->can_call_into_js()) { |
|
818 |
181245 |
return; |
|
819 |
} |
||
820 |
|||
821 |
✓✓ | 107553 |
if (env->destroy_async_id_list()->empty()) { |
822 |
22257 |
env->SetImmediate(&DestroyAsyncIdsCallback, CallbackFlags::kUnrefed); |
|
823 |
} |
||
824 |
|||
825 |
// If the list gets very large empty it faster using a Microtask. |
||
826 |
// Microtasks can't be added in GC context therefore we use an |
||
827 |
// interrupt to get this Microtask scheduled as fast as possible. |
||
828 |
✓✓ | 107553 |
if (env->destroy_async_id_list()->size() == 16384) { |
829 |
2 |
env->RequestInterrupt([](Environment* env) { |
|
830 |
2 |
env->isolate()->EnqueueMicrotask( |
|
831 |
3 |
[](void* arg) { |
|
832 |
1 |
DestroyAsyncIdsCallback(static_cast<Environment*>(arg)); |
|
833 |
4 |
}, env); |
|
834 |
2 |
}); |
|
835 |
} |
||
836 |
|||
837 |
107553 |
env->destroy_async_id_list()->push_back(async_id); |
|
838 |
} |
||
839 |
|||
840 |
// Generalized call for both the constructor and for handles that are pooled |
||
841 |
// and reused over their lifetime. This way a new uid can be assigned when |
||
842 |
// the resource is pulled out of the pool and put back into use. |
||
843 |
215536 |
void AsyncWrap::AsyncReset(Local<Object> resource, double execution_async_id, |
|
844 |
bool silent) { |
||
845 |
✗✓ | 215536 |
CHECK_NE(provider_type(), PROVIDER_NONE); |
846 |
|||
847 |
✓✓ | 215536 |
if (async_id_ != kInvalidAsyncId) { |
848 |
// This instance was in use before, we have already emitted an init with |
||
849 |
// its previous async_id and need to emit a matching destroy for that |
||
850 |
// before generating a new async_id. |
||
851 |
479 |
EmitDestroy(); |
|
852 |
} |
||
853 |
|||
854 |
// Now we can assign a new async_id_ to this instance. |
||
855 |
✓✗ | 215536 |
async_id_ = execution_async_id == kInvalidAsyncId ? env()->new_async_id() |
856 |
: execution_async_id; |
||
857 |
215536 |
trigger_async_id_ = env()->get_default_trigger_async_id(); |
|
858 |
|||
859 |
{ |
||
860 |
431072 |
HandleScope handle_scope(env()->isolate()); |
|
861 |
215536 |
Local<Object> obj = object(); |
|
862 |
✗✓ | 215536 |
CHECK(!obj.IsEmpty()); |
863 |
✓✓ | 215536 |
if (resource != obj) { |
864 |
19539 |
USE(obj->Set(env()->context(), env()->resource_symbol(), resource)); |
|
865 |
} |
||
866 |
} |
||
867 |
|||
868 |
✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✓✓✓ ✓✓✓✗ ✗✗✗✗ ✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✗ ✓✗ |
215536 |
switch (provider_type()) { |
869 |
#define V(PROVIDER) \ |
||
870 |
case PROVIDER_ ## PROVIDER: \ |
||
871 |
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \ |
||
872 |
TRACING_CATEGORY_NODE1(async_hooks))) { \ |
||
873 |
auto data = tracing::TracedValue::Create(); \ |
||
874 |
data->SetInteger("executionAsyncId", \ |
||
875 |
static_cast<int64_t>(env()->execution_async_id())); \ |
||
876 |
data->SetInteger("triggerAsyncId", \ |
||
877 |
static_cast<int64_t>(get_trigger_async_id())); \ |
||
878 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( \ |
||
879 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
880 |
#PROVIDER, static_cast<int64_t>(get_async_id()), \ |
||
881 |
"data", std::move(data)); \ |
||
882 |
} \ |
||
883 |
break; |
||
884 |
✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✗✗✗ ✓✗✗✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✓✗✗✗ ✗✗✓✗ ✗✗✗✗ ✓✗✗✗ ✓✗✗✗ ✗✗✓✗ ✗✗✓✗ ✗✗✗✗ ✓✗✗✗ ✓✗✗✗ ✗✗✓✗ ✗✗✓✗ ✗✗✗✗ ✓✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✓✗✗✗ ✗✗✓✗ ✗✗✓✗ ✗✗✗✗ ✓✗✗✗ ✓✗✗✗ ✗✗✓✗ ✗✗✓✗ ✗✗✗✗ ✓✗✗✗ ✓✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✗✗✗ ✗✓✗✗ ✗✗✓✗ ✗✗✗✗ ✗✓✗✗ ✗✗✗✗ ✓✗✗✗ ✗✗✓✗ ✓✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✗✓ ✓✓✗✗ ✓✗✗✗ ✗✗✗✗ ✓✗✗✗ ✗✗✓✓ ✓✓✓✗ ✗✗✓✗ ✗✗✗✗ ✓✓✓✓ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✓✗✗ ✗✗✗✗ ✗✓✗✗ ✗✗✗✗ ✓✗✗✗ ✗✗✗✓ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✗✓✗ ✗✗✗✗ ✗✓✗✗ ✗✗✗✗ ✓✗✗✗ ✗✗✗✓ ✓✓ |
50 |
NODE_ASYNC_PROVIDER_TYPES(V) |
885 |
#undef V |
||
886 |
default: |
||
887 |
UNREACHABLE(); |
||
888 |
} |
||
889 |
|||
890 |
✓✓ | 215536 |
if (silent) return; |
891 |
|||
892 |
431004 |
EmitAsyncInit(env(), resource, |
|
893 |
215502 |
env()->async_hooks()->provider_string(provider_type()), |
|
894 |
215502 |
async_id_, trigger_async_id_); |
|
895 |
} |
||
896 |
|||
897 |
|||
898 |
216054 |
void AsyncWrap::EmitAsyncInit(Environment* env, |
|
899 |
Local<Object> object, |
||
900 |
Local<String> type, |
||
901 |
double async_id, |
||
902 |
double trigger_async_id) { |
||
903 |
✗✓ | 216054 |
CHECK(!object.IsEmpty()); |
904 |
✗✓ | 216054 |
CHECK(!type.IsEmpty()); |
905 |
216054 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
906 |
|||
907 |
// Nothing to execute, so can continue normally. |
||
908 |
✓✓ | 216054 |
if (async_hooks->fields()[AsyncHooks::kInit] == 0) { |
909 |
178063 |
return; |
|
910 |
} |
||
911 |
|||
912 |
75982 |
HandleScope scope(env->isolate()); |
|
913 |
37991 |
Local<Function> init_fn = env->async_hooks_init_function(); |
|
914 |
|||
915 |
Local<Value> argv[] = { |
||
916 |
Number::New(env->isolate(), async_id), |
||
917 |
type, |
||
918 |
Number::New(env->isolate(), trigger_async_id), |
||
919 |
object, |
||
920 |
189955 |
}; |
|
921 |
|||
922 |
75982 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
923 |
113973 |
USE(init_fn->Call(env->context(), object, arraysize(argv), argv)); |
|
924 |
} |
||
925 |
|||
926 |
|||
927 |
578124 |
MaybeLocal<Value> AsyncWrap::MakeCallback(const Local<Function> cb, |
|
928 |
int argc, |
||
929 |
Local<Value>* argv) { |
||
930 |
578124 |
EmitTraceEventBefore(); |
|
931 |
|||
932 |
578186 |
ProviderType provider = provider_type(); |
|
933 |
578258 |
async_context context { get_async_id(), get_trigger_async_id() }; |
|
934 |
MaybeLocal<Value> ret = InternalMakeCallback( |
||
935 |
578253 |
env(), object(), object(), cb, argc, argv, context); |
|
936 |
|||
937 |
// This is a static call with cached values because the `this` object may |
||
938 |
// no longer be alive at this point. |
||
939 |
578157 |
EmitTraceEventAfter(provider, context.async_id); |
|
940 |
|||
941 |
578144 |
return ret; |
|
942 |
} |
||
943 |
|||
944 |
std::string AsyncWrap::MemoryInfoName() const { |
||
945 |
return provider_names[provider_type()]; |
||
946 |
} |
||
947 |
|||
948 |
std::string AsyncWrap::diagnostic_name() const { |
||
949 |
return MemoryInfoName() + " (" + std::to_string(env()->thread_id()) + ":" + |
||
950 |
std::to_string(static_cast<int64_t>(async_id_)) + ")"; |
||
951 |
} |
||
952 |
|||
953 |
930 |
Local<Object> AsyncWrap::GetOwner() { |
|
954 |
930 |
return GetOwner(env(), object()); |
|
955 |
} |
||
956 |
|||
957 |
930 |
Local<Object> AsyncWrap::GetOwner(Environment* env, Local<Object> obj) { |
|
958 |
930 |
EscapableHandleScope handle_scope(env->isolate()); |
|
959 |
✗✓ | 930 |
CHECK(!obj.IsEmpty()); |
960 |
|||
961 |
1860 |
TryCatchScope ignore_exceptions(env); |
|
962 |
while (true) { |
||
963 |
Local<Value> owner; |
||
964 |
✓✓ | 7384 |
if (!obj->Get(env->context(), |
965 |
✓✗✓✓ |
12922 |
env->owner_symbol()).ToLocal(&owner) || |
966 |
1846 |
!owner->IsObject()) { |
|
967 |
1860 |
return handle_scope.Escape(obj); |
|
968 |
} |
||
969 |
|||
970 |
916 |
obj = owner.As<Object>(); |
|
971 |
916 |
} |
|
972 |
} |
||
973 |
|||
974 |
} // namespace node |
||
975 |
|||
976 |
4683 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(async_wrap, node::AsyncWrap::Initialize) |
|
977 |
✓✗✓✗ |
18687 |
NODE_MODULE_EXTERNAL_REFERENCE(async_wrap, |
978 |
node::AsyncWrap::RegisterExternalReferences) |
Generated by: GCOVR (Version 3.4) |