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::Local; |
||
43 |
using v8::MaybeLocal; |
||
44 |
using v8::Nothing; |
||
45 |
using v8::Number; |
||
46 |
using v8::Object; |
||
47 |
using v8::PropertyAttribute; |
||
48 |
using v8::ReadOnly; |
||
49 |
using v8::String; |
||
50 |
using v8::Undefined; |
||
51 |
using v8::Value; |
||
52 |
using v8::WeakCallbackInfo; |
||
53 |
using v8::WeakCallbackType; |
||
54 |
|||
55 |
using TryCatchScope = node::errors::TryCatchScope; |
||
56 |
|||
57 |
namespace node { |
||
58 |
|||
59 |
static const char* const provider_names[] = { |
||
60 |
#define V(PROVIDER) \ |
||
61 |
#PROVIDER, |
||
62 |
NODE_ASYNC_PROVIDER_TYPES(V) |
||
63 |
#undef V |
||
64 |
}; |
||
65 |
|||
66 |
24845 |
void AsyncWrap::DestroyAsyncIdsCallback(Environment* env) { |
|
67 |
24845 |
Local<Function> fn = env->async_hooks_destroy_function(); |
|
68 |
|||
69 |
24845 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
70 |
|||
71 |
4 |
do { |
|
72 |
24849 |
std::vector<double> destroy_async_id_list; |
|
73 |
24849 |
destroy_async_id_list.swap(*env->destroy_async_id_list()); |
|
74 |
✗✓ | 24849 |
if (!env->can_call_into_js()) return; |
75 |
✓✓ | 204703 |
for (auto async_id : destroy_async_id_list) { |
76 |
// Want each callback to be cleaned up after itself, instead of cleaning |
||
77 |
// them all up after the while() loop completes. |
||
78 |
179856 |
HandleScope scope(env->isolate()); |
|
79 |
359712 |
Local<Value> async_id_value = Number::New(env->isolate(), async_id); |
|
80 |
MaybeLocal<Value> ret = fn->Call( |
||
81 |
359712 |
env->context(), Undefined(env->isolate()), 1, &async_id_value); |
|
82 |
|||
83 |
✗✓ | 179854 |
if (ret.IsEmpty()) |
84 |
return; |
||
85 |
} |
||
86 |
✓✓ | 24847 |
} while (!env->destroy_async_id_list()->empty()); |
87 |
} |
||
88 |
|||
89 |
1024982 |
void Emit(Environment* env, double async_id, AsyncHooks::Fields type, |
|
90 |
Local<Function> fn) { |
||
91 |
1024982 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
92 |
|||
93 |
✓✓✗✓ ✓✓ |
1024982 |
if (async_hooks->fields()[type] == 0 || !env->can_call_into_js()) |
94 |
1024680 |
return; |
|
95 |
|||
96 |
604 |
HandleScope handle_scope(env->isolate()); |
|
97 |
302 |
Local<Value> async_id_value = Number::New(env->isolate(), async_id); |
|
98 |
302 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
99 |
604 |
USE(fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value)); |
|
100 |
} |
||
101 |
|||
102 |
|||
103 |
void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) { |
||
104 |
Emit(env, async_id, AsyncHooks::kPromiseResolve, |
||
105 |
env->async_hooks_promise_resolve_function()); |
||
106 |
} |
||
107 |
|||
108 |
|||
109 |
381919 |
void AsyncWrap::EmitTraceEventBefore() { |
|
110 |
✗✗✗✗ ✓✗✓✓ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
381919 |
switch (provider_type()) { |
111 |
#define V(PROVIDER) \ |
||
112 |
case PROVIDER_ ## PROVIDER: \ |
||
113 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( \ |
||
114 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
115 |
#PROVIDER "_CALLBACK", static_cast<int64_t>(get_async_id())); \ |
||
116 |
break; |
||
117 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✗✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ |
773754 |
NODE_ASYNC_PROVIDER_TYPES(V) |
118 |
#undef V |
||
119 |
default: |
||
120 |
UNREACHABLE(); |
||
121 |
} |
||
122 |
381919 |
} |
|
123 |
|||
124 |
|||
125 |
512531 |
void AsyncWrap::EmitBefore(Environment* env, double async_id) { |
|
126 |
512531 |
Emit(env, async_id, AsyncHooks::kBefore, |
|
127 |
env->async_hooks_before_function()); |
||
128 |
512531 |
} |
|
129 |
|||
130 |
|||
131 |
381751 |
void AsyncWrap::EmitTraceEventAfter(ProviderType type, double async_id) { |
|
132 |
✗✗✗✗ ✓✗✓✓ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
381751 |
switch (type) { |
133 |
#define V(PROVIDER) \ |
||
134 |
case PROVIDER_ ## PROVIDER: \ |
||
135 |
TRACE_EVENT_NESTABLE_ASYNC_END0( \ |
||
136 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
137 |
#PROVIDER "_CALLBACK", static_cast<int64_t>(async_id)); \ |
||
138 |
break; |
||
139 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✗✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ |
773354 |
NODE_ASYNC_PROVIDER_TYPES(V) |
140 |
#undef V |
||
141 |
default: |
||
142 |
UNREACHABLE(); |
||
143 |
} |
||
144 |
381751 |
} |
|
145 |
|||
146 |
|||
147 |
512451 |
void AsyncWrap::EmitAfter(Environment* env, double async_id) { |
|
148 |
// If the user's callback failed then the after() hooks will be called at the |
||
149 |
// end of _fatalException(). |
||
150 |
512451 |
Emit(env, async_id, AsyncHooks::kAfter, |
|
151 |
env->async_hooks_after_function()); |
||
152 |
512451 |
} |
|
153 |
|||
154 |
827 |
static void SetupHooks(const FunctionCallbackInfo<Value>& args) { |
|
155 |
827 |
Environment* env = Environment::GetCurrent(args); |
|
156 |
|||
157 |
✗✓ | 827 |
CHECK(args[0]->IsObject()); |
158 |
|||
159 |
// All of init, before, after, destroy, and promise_resolve are supplied by |
||
160 |
// async_hooks internally, so this should only ever be called once. At which |
||
161 |
// time all the functions should be set. Detect this by checking if |
||
162 |
// init !IsEmpty(). |
||
163 |
✗✓ | 1654 |
CHECK(env->async_hooks_init_function().IsEmpty()); |
164 |
|||
165 |
1654 |
Local<Object> fn_obj = args[0].As<Object>(); |
|
166 |
|||
167 |
#define SET_HOOK_FN(name) \ |
||
168 |
do { \ |
||
169 |
Local<Value> v = \ |
||
170 |
fn_obj->Get(env->context(), \ |
||
171 |
FIXED_ONE_BYTE_STRING(env->isolate(), #name)) \ |
||
172 |
.ToLocalChecked(); \ |
||
173 |
CHECK(v->IsFunction()); \ |
||
174 |
env->set_async_hooks_##name##_function(v.As<Function>()); \ |
||
175 |
} while (0) |
||
176 |
|||
177 |
✗✓ | 3308 |
SET_HOOK_FN(init); |
178 |
✗✓ | 4135 |
SET_HOOK_FN(before); |
179 |
✗✓ | 3308 |
SET_HOOK_FN(after); |
180 |
✗✓ | 3308 |
SET_HOOK_FN(destroy); |
181 |
✗✓ | 3308 |
SET_HOOK_FN(promise_resolve); |
182 |
#undef SET_HOOK_FN |
||
183 |
827 |
} |
|
184 |
|||
185 |
16638 |
static void SetPromiseHooks(const FunctionCallbackInfo<Value>& args) { |
|
186 |
16638 |
Environment* env = Environment::GetCurrent(args); |
|
187 |
|||
188 |
✓✓✓✓ ✓✓✓✓ |
75325 |
env->ResetPromiseHooks( |
189 |
25411 |
args[0]->IsFunction() ? args[0].As<Function>() : Local<Function>(), |
|
190 |
25125 |
args[1]->IsFunction() ? args[1].As<Function>() : Local<Function>(), |
|
191 |
24812 |
args[2]->IsFunction() ? args[2].As<Function>() : Local<Function>(), |
|
192 |
16863 |
args[3]->IsFunction() ? args[3].As<Function>() : Local<Function>()); |
|
193 |
16638 |
} |
|
194 |
|||
195 |
class DestroyParam { |
||
196 |
public: |
||
197 |
double asyncId; |
||
198 |
Environment* env; |
||
199 |
Global<Object> target; |
||
200 |
Global<Object> propBag; |
||
201 |
}; |
||
202 |
|||
203 |
32814 |
static void DestroyParamCleanupHook(void* ptr) { |
|
204 |
✓✗ | 32814 |
delete static_cast<DestroyParam*>(ptr); |
205 |
32814 |
} |
|
206 |
|||
207 |
67687 |
void AsyncWrap::WeakCallback(const WeakCallbackInfo<DestroyParam>& info) { |
|
208 |
67687 |
HandleScope scope(info.GetIsolate()); |
|
209 |
|||
210 |
67687 |
std::unique_ptr<DestroyParam> p{info.GetParameter()}; |
|
211 |
Local<Object> prop_bag = PersistentToLocal::Default(info.GetIsolate(), |
||
212 |
67687 |
p->propBag); |
|
213 |
Local<Value> val; |
||
214 |
|||
215 |
67687 |
p->env->RemoveCleanupHook(DestroyParamCleanupHook, p.get()); |
|
216 |
|||
217 |
✓✓ | 69979 |
if (!prop_bag.IsEmpty() && |
218 |
✗✓ | 72271 |
!prop_bag->Get(p->env->context(), p->env->destroyed_string()) |
219 |
✗✓ | 2292 |
.ToLocal(&val)) { |
220 |
return; |
||
221 |
} |
||
222 |
|||
223 |
✓✓✓✓ ✓✓ |
69979 |
if (val.IsEmpty() || val->IsFalse()) { |
224 |
67681 |
AsyncWrap::EmitDestroy(p->env, p->asyncId); |
|
225 |
} |
||
226 |
// unique_ptr goes out of scope here and pointer is deleted. |
||
227 |
} |
||
228 |
|||
229 |
|||
230 |
✓✗ | 101569 |
static void RegisterDestroyHook(const FunctionCallbackInfo<Value>& args) { |
231 |
✗✓ | 101569 |
CHECK(args[0]->IsObject()); |
232 |
✗✓ | 101569 |
CHECK(args[1]->IsNumber()); |
233 |
✓✓✗✓ ✗✓ |
105007 |
CHECK(args.Length() == 2 || args[2]->IsObject()); |
234 |
|||
235 |
101569 |
Isolate* isolate = args.GetIsolate(); |
|
236 |
✓✗ | 101569 |
DestroyParam* p = new DestroyParam(); |
237 |
203138 |
p->asyncId = args[1].As<Number>()->Value(); |
|
238 |
101569 |
p->env = Environment::GetCurrent(args); |
|
239 |
✓✗ | 406276 |
p->target.Reset(isolate, args[0].As<Object>()); |
240 |
✓✓ | 101569 |
if (args.Length() > 2) { |
241 |
✓✗ | 13752 |
p->propBag.Reset(isolate, args[2].As<Object>()); |
242 |
} |
||
243 |
101569 |
p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter); |
|
244 |
101569 |
p->env->AddCleanupHook(DestroyParamCleanupHook, p); |
|
245 |
101569 |
} |
|
246 |
|||
247 |
101852 |
void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) { |
|
248 |
AsyncWrap* wrap; |
||
249 |
203704 |
args.GetReturnValue().Set(kInvalidAsyncId); |
|
250 |
✓✓ | 101852 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
251 |
203664 |
args.GetReturnValue().Set(wrap->get_async_id()); |
|
252 |
} |
||
253 |
|||
254 |
|||
255 |
4 |
void AsyncWrap::PushAsyncContext(const FunctionCallbackInfo<Value>& args) { |
|
256 |
4 |
Environment* env = Environment::GetCurrent(args); |
|
257 |
// No need for CHECK(IsNumber()) on args because if FromJust() doesn't fail |
||
258 |
// then the checks in push_async_ids() and pop_async_id() will. |
||
259 |
✓✗ | 8 |
double async_id = args[0]->NumberValue(env->context()).FromJust(); |
260 |
4 |
double trigger_async_id = args[1]->NumberValue(env->context()).FromJust(); |
|
261 |
4 |
env->async_hooks()->push_async_context(async_id, trigger_async_id, {}); |
|
262 |
4 |
} |
|
263 |
|||
264 |
|||
265 |
3 |
void AsyncWrap::PopAsyncContext(const FunctionCallbackInfo<Value>& args) { |
|
266 |
3 |
Environment* env = Environment::GetCurrent(args); |
|
267 |
6 |
double async_id = args[0]->NumberValue(env->context()).FromJust(); |
|
268 |
✗✗ | 3 |
args.GetReturnValue().Set(env->async_hooks()->pop_async_context(async_id)); |
269 |
} |
||
270 |
|||
271 |
|||
272 |
2411 |
void AsyncWrap::ExecutionAsyncResource( |
|
273 |
const FunctionCallbackInfo<Value>& args) { |
||
274 |
2411 |
Environment* env = Environment::GetCurrent(args); |
|
275 |
uint32_t index; |
||
276 |
✗✓ | 4822 |
if (!args[0]->Uint32Value(env->context()).To(&index)) return; |
277 |
4822 |
args.GetReturnValue().Set( |
|
278 |
env->async_hooks()->native_execution_async_resource(index)); |
||
279 |
} |
||
280 |
|||
281 |
|||
282 |
1278 |
void AsyncWrap::ClearAsyncIdStack(const FunctionCallbackInfo<Value>& args) { |
|
283 |
1278 |
Environment* env = Environment::GetCurrent(args); |
|
284 |
1278 |
env->async_hooks()->clear_async_id_stack(); |
|
285 |
1278 |
} |
|
286 |
|||
287 |
|||
288 |
✓✗ | 421 |
void AsyncWrap::AsyncReset(const FunctionCallbackInfo<Value>& args) { |
289 |
✗✓ | 421 |
CHECK(args[0]->IsObject()); |
290 |
|||
291 |
AsyncWrap* wrap; |
||
292 |
✗✓ | 421 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
293 |
|||
294 |
✓✗ | 842 |
Local<Object> resource = args[0].As<Object>(); |
295 |
double execution_async_id = |
||
296 |
✗✓ | 421 |
args[1]->IsNumber() ? args[1].As<Number>()->Value() : kInvalidAsyncId; |
297 |
421 |
wrap->AsyncReset(resource, execution_async_id); |
|
298 |
} |
||
299 |
|||
300 |
|||
301 |
421 |
void AsyncWrap::GetProviderType(const FunctionCallbackInfo<Value>& args) { |
|
302 |
AsyncWrap* wrap; |
||
303 |
842 |
args.GetReturnValue().Set(AsyncWrap::PROVIDER_NONE); |
|
304 |
✗✓ | 421 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
305 |
842 |
args.GetReturnValue().Set(wrap->provider_type()); |
|
306 |
} |
||
307 |
|||
308 |
|||
309 |
219991 |
void AsyncWrap::EmitDestroy(bool from_gc) { |
|
310 |
219991 |
AsyncWrap::EmitDestroy(env(), async_id_); |
|
311 |
// Ensure no double destroy is emitted via AsyncReset(). |
||
312 |
219991 |
async_id_ = kInvalidAsyncId; |
|
313 |
|||
314 |
✓✓✓✓ ✓✓ |
219991 |
if (!persistent().IsEmpty() && !from_gc) { |
315 |
6925 |
HandleScope handle_scope(env()->isolate()); |
|
316 |
27700 |
USE(object()->Set(env()->context(), env()->resource_symbol(), object())); |
|
317 |
} |
||
318 |
219991 |
} |
|
319 |
|||
320 |
✓✗ | 102163 |
void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo<Value>& args) { |
321 |
✗✓ | 102163 |
CHECK(args[0]->IsNumber()); |
322 |
102163 |
AsyncWrap::EmitDestroy( |
|
323 |
Environment::GetCurrent(args), |
||
324 |
204326 |
args[0].As<Number>()->Value()); |
|
325 |
102163 |
} |
|
326 |
|||
327 |
920 |
void AsyncWrap::SetCallbackTrampoline(const FunctionCallbackInfo<Value>& args) { |
|
328 |
920 |
Environment* env = Environment::GetCurrent(args); |
|
329 |
|||
330 |
✓✓ | 920 |
if (args[0]->IsFunction()) { |
331 |
1228 |
env->set_async_hooks_callback_trampoline(args[0].As<Function>()); |
|
332 |
} else { |
||
333 |
306 |
env->set_async_hooks_callback_trampoline(Local<Function>()); |
|
334 |
} |
||
335 |
920 |
} |
|
336 |
|||
337 |
168042 |
Local<FunctionTemplate> AsyncWrap::GetConstructorTemplate( |
|
338 |
IsolateData* isolate_data) { |
||
339 |
168042 |
Local<FunctionTemplate> tmpl = isolate_data->async_wrap_ctor_template(); |
|
340 |
✓✓ | 168042 |
if (tmpl.IsEmpty()) { |
341 |
831 |
Isolate* isolate = isolate_data->isolate(); |
|
342 |
831 |
tmpl = NewFunctionTemplate(isolate, nullptr); |
|
343 |
831 |
tmpl->SetClassName( |
|
344 |
FIXED_ONE_BYTE_STRING(isolate_data->isolate(), "AsyncWrap")); |
||
345 |
831 |
tmpl->Inherit(BaseObject::GetConstructorTemplate(isolate_data)); |
|
346 |
831 |
SetProtoMethod(isolate, tmpl, "getAsyncId", AsyncWrap::GetAsyncId); |
|
347 |
831 |
SetProtoMethod(isolate, tmpl, "asyncReset", AsyncWrap::AsyncReset); |
|
348 |
831 |
SetProtoMethod( |
|
349 |
isolate, tmpl, "getProviderType", AsyncWrap::GetProviderType); |
||
350 |
831 |
isolate_data->set_async_wrap_ctor_template(tmpl); |
|
351 |
} |
||
352 |
168042 |
return tmpl; |
|
353 |
} |
||
354 |
|||
355 |
827 |
void AsyncWrap::Initialize(Local<Object> target, |
|
356 |
Local<Value> unused, |
||
357 |
Local<Context> context, |
||
358 |
void* priv) { |
||
359 |
827 |
Environment* env = Environment::GetCurrent(context); |
|
360 |
827 |
Isolate* isolate = env->isolate(); |
|
361 |
1654 |
HandleScope scope(isolate); |
|
362 |
|||
363 |
827 |
SetMethod(context, target, "setupHooks", SetupHooks); |
|
364 |
827 |
SetMethod(context, target, "setCallbackTrampoline", SetCallbackTrampoline); |
|
365 |
827 |
SetMethod(context, target, "pushAsyncContext", PushAsyncContext); |
|
366 |
827 |
SetMethod(context, target, "popAsyncContext", PopAsyncContext); |
|
367 |
827 |
SetMethod(context, target, "executionAsyncResource", ExecutionAsyncResource); |
|
368 |
827 |
SetMethod(context, target, "clearAsyncIdStack", ClearAsyncIdStack); |
|
369 |
827 |
SetMethod(context, target, "queueDestroyAsyncId", QueueDestroyAsyncId); |
|
370 |
827 |
SetMethod(context, target, "setPromiseHooks", SetPromiseHooks); |
|
371 |
827 |
SetMethod(context, target, "registerDestroyHook", RegisterDestroyHook); |
|
372 |
|||
373 |
827 |
PropertyAttribute ReadOnlyDontDelete = |
|
374 |
static_cast<PropertyAttribute>(ReadOnly | DontDelete); |
||
375 |
|||
376 |
#define FORCE_SET_TARGET_FIELD(obj, str, field) \ |
||
377 |
(obj)->DefineOwnProperty(context, \ |
||
378 |
FIXED_ONE_BYTE_STRING(isolate, str), \ |
||
379 |
field, \ |
||
380 |
ReadOnlyDontDelete).FromJust() |
||
381 |
|||
382 |
// Attach the uint32_t[] where each slot contains the count of the number of |
||
383 |
// callbacks waiting to be called on a particular event. It can then be |
||
384 |
// incremented/decremented from JS quickly to communicate to C++ if there are |
||
385 |
// any callbacks waiting to be called. |
||
386 |
3308 |
FORCE_SET_TARGET_FIELD(target, |
|
387 |
"async_hook_fields", |
||
388 |
env->async_hooks()->fields().GetJSArray()); |
||
389 |
|||
390 |
// The following v8::Float64Array has 5 fields. These fields are shared in |
||
391 |
// this way to allow JS and C++ to read/write each value as quickly as |
||
392 |
// possible. The fields are represented as follows: |
||
393 |
// |
||
394 |
// kAsyncIdCounter: Maintains the state of the next unique id to be assigned. |
||
395 |
// |
||
396 |
// kDefaultTriggerAsyncId: Write the id of the resource responsible for a |
||
397 |
// handle's creation just before calling the new handle's constructor. |
||
398 |
// After the new handle is constructed kDefaultTriggerAsyncId is set back |
||
399 |
// to kInvalidAsyncId. |
||
400 |
3308 |
FORCE_SET_TARGET_FIELD(target, |
|
401 |
"async_id_fields", |
||
402 |
env->async_hooks()->async_id_fields().GetJSArray()); |
||
403 |
|||
404 |
3308 |
FORCE_SET_TARGET_FIELD(target, |
|
405 |
"execution_async_resources", |
||
406 |
env->async_hooks()->js_execution_async_resources()); |
||
407 |
|||
408 |
827 |
target->Set(context, |
|
409 |
env->async_ids_stack_string(), |
||
410 |
2481 |
env->async_hooks()->async_ids_stack().GetJSArray()).Check(); |
|
411 |
|||
412 |
827 |
Local<Object> constants = Object::New(isolate); |
|
413 |
#define SET_HOOKS_CONSTANT(name) \ |
||
414 |
FORCE_SET_TARGET_FIELD( \ |
||
415 |
constants, #name, Integer::New(isolate, AsyncHooks::name)) |
||
416 |
|||
417 |
3308 |
SET_HOOKS_CONSTANT(kInit); |
|
418 |
3308 |
SET_HOOKS_CONSTANT(kBefore); |
|
419 |
3308 |
SET_HOOKS_CONSTANT(kAfter); |
|
420 |
3308 |
SET_HOOKS_CONSTANT(kDestroy); |
|
421 |
3308 |
SET_HOOKS_CONSTANT(kPromiseResolve); |
|
422 |
3308 |
SET_HOOKS_CONSTANT(kTotals); |
|
423 |
3308 |
SET_HOOKS_CONSTANT(kCheck); |
|
424 |
3308 |
SET_HOOKS_CONSTANT(kExecutionAsyncId); |
|
425 |
3308 |
SET_HOOKS_CONSTANT(kTriggerAsyncId); |
|
426 |
3308 |
SET_HOOKS_CONSTANT(kAsyncIdCounter); |
|
427 |
3308 |
SET_HOOKS_CONSTANT(kDefaultTriggerAsyncId); |
|
428 |
3308 |
SET_HOOKS_CONSTANT(kUsesExecutionAsyncResource); |
|
429 |
3308 |
SET_HOOKS_CONSTANT(kStackLength); |
|
430 |
#undef SET_HOOKS_CONSTANT |
||
431 |
1654 |
FORCE_SET_TARGET_FIELD(target, "constants", constants); |
|
432 |
|||
433 |
827 |
Local<Object> async_providers = Object::New(isolate); |
|
434 |
#define V(p) \ |
||
435 |
FORCE_SET_TARGET_FIELD( \ |
||
436 |
async_providers, #p, Integer::New(isolate, AsyncWrap::PROVIDER_ ## p)); |
||
437 |
144725 |
NODE_ASYNC_PROVIDER_TYPES(V) |
|
438 |
#undef V |
||
439 |
2481 |
FORCE_SET_TARGET_FIELD(target, "Providers", async_providers); |
|
440 |
|||
441 |
#undef FORCE_SET_TARGET_FIELD |
||
442 |
|||
443 |
827 |
env->set_async_hooks_init_function(Local<Function>()); |
|
444 |
827 |
env->set_async_hooks_before_function(Local<Function>()); |
|
445 |
827 |
env->set_async_hooks_after_function(Local<Function>()); |
|
446 |
827 |
env->set_async_hooks_destroy_function(Local<Function>()); |
|
447 |
827 |
env->set_async_hooks_promise_resolve_function(Local<Function>()); |
|
448 |
827 |
env->set_async_hooks_callback_trampoline(Local<Function>()); |
|
449 |
827 |
env->set_async_hooks_binding(target); |
|
450 |
827 |
} |
|
451 |
|||
452 |
5716 |
void AsyncWrap::RegisterExternalReferences( |
|
453 |
ExternalReferenceRegistry* registry) { |
||
454 |
5716 |
registry->Register(SetupHooks); |
|
455 |
5716 |
registry->Register(SetCallbackTrampoline); |
|
456 |
5716 |
registry->Register(PushAsyncContext); |
|
457 |
5716 |
registry->Register(PopAsyncContext); |
|
458 |
5716 |
registry->Register(ExecutionAsyncResource); |
|
459 |
5716 |
registry->Register(ClearAsyncIdStack); |
|
460 |
5716 |
registry->Register(QueueDestroyAsyncId); |
|
461 |
5716 |
registry->Register(SetPromiseHooks); |
|
462 |
5716 |
registry->Register(RegisterDestroyHook); |
|
463 |
5716 |
registry->Register(AsyncWrap::GetAsyncId); |
|
464 |
5716 |
registry->Register(AsyncWrap::AsyncReset); |
|
465 |
5716 |
registry->Register(AsyncWrap::GetProviderType); |
|
466 |
5716 |
} |
|
467 |
|||
468 |
208980 |
AsyncWrap::AsyncWrap(Environment* env, |
|
469 |
Local<Object> object, |
||
470 |
ProviderType provider, |
||
471 |
208980 |
double execution_async_id) |
|
472 |
208980 |
: AsyncWrap(env, object, provider, execution_async_id, false) {} |
|
473 |
|||
474 |
208980 |
AsyncWrap::AsyncWrap(Environment* env, |
|
475 |
Local<Object> object, |
||
476 |
ProviderType provider, |
||
477 |
double execution_async_id, |
||
478 |
208980 |
bool silent) |
|
479 |
208980 |
: AsyncWrap(env, object) { |
|
480 |
✗✓ | 208980 |
CHECK_NE(provider, PROVIDER_NONE); |
481 |
208980 |
provider_type_ = provider; |
|
482 |
|||
483 |
// Use AsyncReset() call to execute the init() callbacks. |
||
484 |
208980 |
AsyncReset(object, execution_async_id, silent); |
|
485 |
208980 |
init_hook_ran_ = true; |
|
486 |
208980 |
} |
|
487 |
|||
488 |
AsyncWrap::AsyncWrap(Environment* env, |
||
489 |
Local<Object> object, |
||
490 |
ProviderType provider, |
||
491 |
double execution_async_id, |
||
492 |
double trigger_async_id) |
||
493 |
: AsyncWrap(env, object, provider, execution_async_id, true) { |
||
494 |
trigger_async_id_ = trigger_async_id; |
||
495 |
} |
||
496 |
|||
497 |
214271 |
AsyncWrap::AsyncWrap(Environment* env, Local<Object> object) |
|
498 |
214271 |
: BaseObject(env, object) { |
|
499 |
214271 |
} |
|
500 |
|||
501 |
// This method is necessary to work around one specific problem: |
||
502 |
// Before the init() hook runs, if there is one, the BaseObject() constructor |
||
503 |
// registers this object with the Environment for finalization and debugging |
||
504 |
// purposes. |
||
505 |
// If the Environment decides to inspect this object for debugging, it tries to |
||
506 |
// call virtual methods on this object that are only (meaningfully) implemented |
||
507 |
// by the subclasses of AsyncWrap. |
||
508 |
// This could, with bad luck, happen during the AsyncWrap() constructor, |
||
509 |
// because we run JS code as part of it and that in turn can lead to a heapdump |
||
510 |
// being taken, either through the inspector or our programmatic API for it. |
||
511 |
// The object being initialized is not fully constructed at that point, and |
||
512 |
// in particular its virtual function table points to the AsyncWrap one |
||
513 |
// (as the subclass constructor has not yet begun execution at that point). |
||
514 |
// This means that the functions that are used for heap dump memory tracking |
||
515 |
// are not yet available, and trying to call them would crash the process. |
||
516 |
// We use this particular `IsDoneInitializing()` method to tell the Environment |
||
517 |
// that such debugging methods are not yet available. |
||
518 |
// This may be somewhat unreliable when it comes to future changes, because |
||
519 |
// at this point it *only* protects AsyncWrap subclasses, and *only* for cases |
||
520 |
// where heap dumps are being taken while the init() hook is on the call stack. |
||
521 |
// For now, it seems like the best solution, though. |
||
522 |
190283 |
bool AsyncWrap::IsDoneInitializing() const { |
|
523 |
190283 |
return init_hook_ran_; |
|
524 |
} |
||
525 |
|||
526 |
426132 |
AsyncWrap::~AsyncWrap() { |
|
527 |
426132 |
EmitTraceEventDestroy(); |
|
528 |
426132 |
EmitDestroy(true /* from gc */); |
|
529 |
} |
||
530 |
|||
531 |
219373 |
void AsyncWrap::EmitTraceEventDestroy() { |
|
532 |
✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
219373 |
switch (provider_type()) { |
533 |
#define V(PROVIDER) \ |
||
534 |
case PROVIDER_ ## PROVIDER: \ |
||
535 |
TRACE_EVENT_NESTABLE_ASYNC_END0( \ |
||
536 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
537 |
#PROVIDER, static_cast<int64_t>(get_async_id())); \ |
||
538 |
break; |
||
539 |
✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✓✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ |
452400 |
NODE_ASYNC_PROVIDER_TYPES(V) |
540 |
#undef V |
||
541 |
default: |
||
542 |
UNREACHABLE(); |
||
543 |
} |
||
544 |
219373 |
} |
|
545 |
|||
546 |
390386 |
void AsyncWrap::EmitDestroy(Environment* env, double async_id) { |
|
547 |
✓✓✓✓ |
571743 |
if (env->async_hooks()->fields()[AsyncHooks::kDestroy] == 0 || |
548 |
✓✓ | 181357 |
!env->can_call_into_js()) { |
549 |
209219 |
return; |
|
550 |
} |
||
551 |
|||
552 |
✓✓ | 181167 |
if (env->destroy_async_id_list()->empty()) { |
553 |
24906 |
env->SetImmediate(&DestroyAsyncIdsCallback, CallbackFlags::kUnrefed); |
|
554 |
} |
||
555 |
|||
556 |
// If the list gets very large empty it faster using a Microtask. |
||
557 |
// Microtasks can't be added in GC context therefore we use an |
||
558 |
// interrupt to get this Microtask scheduled as fast as possible. |
||
559 |
✓✓ | 181167 |
if (env->destroy_async_id_list()->size() == 16384) { |
560 |
1 |
env->RequestInterrupt([](Environment* env) { |
|
561 |
3 |
env->context()->GetMicrotaskQueue()->EnqueueMicrotask( |
|
562 |
env->isolate(), |
||
563 |
1 |
[](void* arg) { |
|
564 |
1 |
DestroyAsyncIdsCallback(static_cast<Environment*>(arg)); |
|
565 |
1 |
}, env); |
|
566 |
1 |
}); |
|
567 |
} |
||
568 |
|||
569 |
181167 |
env->destroy_async_id_list()->push_back(async_id); |
|
570 |
} |
||
571 |
|||
572 |
// Generalized call for both the constructor and for handles that are pooled |
||
573 |
// and reused over their lifetime. This way a new uid can be assigned when |
||
574 |
// the resource is pulled out of the pool and put back into use. |
||
575 |
216106 |
void AsyncWrap::AsyncReset(Local<Object> resource, double execution_async_id, |
|
576 |
bool silent) { |
||
577 |
✗✓ | 216106 |
CHECK_NE(provider_type(), PROVIDER_NONE); |
578 |
|||
579 |
✓✓ | 216106 |
if (async_id_ != kInvalidAsyncId) { |
580 |
// This instance was in use before, we have already emitted an init with |
||
581 |
// its previous async_id and need to emit a matching destroy for that |
||
582 |
// before generating a new async_id. |
||
583 |
618 |
EmitDestroy(); |
|
584 |
} |
||
585 |
|||
586 |
// Now we can assign a new async_id_ to this instance. |
||
587 |
✓✗ | 216106 |
async_id_ = execution_async_id == kInvalidAsyncId ? env()->new_async_id() |
588 |
: execution_async_id; |
||
589 |
216106 |
trigger_async_id_ = env()->get_default_trigger_async_id(); |
|
590 |
|||
591 |
{ |
||
592 |
432212 |
HandleScope handle_scope(env()->isolate()); |
|
593 |
216106 |
Local<Object> obj = object(); |
|
594 |
✗✓ | 216106 |
CHECK(!obj.IsEmpty()); |
595 |
✓✓ | 216106 |
if (resource != obj) { |
596 |
14252 |
USE(obj->Set(env()->context(), env()->resource_symbol(), resource)); |
|
597 |
} |
||
598 |
} |
||
599 |
|||
600 |
✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
216106 |
switch (provider_type()) { |
601 |
#define V(PROVIDER) \ |
||
602 |
case PROVIDER_ ## PROVIDER: \ |
||
603 |
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \ |
||
604 |
TRACING_CATEGORY_NODE1(async_hooks))) { \ |
||
605 |
auto data = tracing::TracedValue::Create(); \ |
||
606 |
data->SetInteger("executionAsyncId", \ |
||
607 |
static_cast<int64_t>(env()->execution_async_id())); \ |
||
608 |
data->SetInteger("triggerAsyncId", \ |
||
609 |
static_cast<int64_t>(get_trigger_async_id())); \ |
||
610 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( \ |
||
611 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
612 |
#PROVIDER, static_cast<int64_t>(get_async_id()), \ |
||
613 |
"data", std::move(data)); \ |
||
614 |
} \ |
||
615 |
break; |
||
616 |
✗✗✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✓✓ ✓✓✓✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✓✓✓✓ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✓✓ ✓✗✓✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✓ ✗✗✗✗ |
216165 |
NODE_ASYNC_PROVIDER_TYPES(V) |
617 |
#undef V |
||
618 |
default: |
||
619 |
UNREACHABLE(); |
||
620 |
} |
||
621 |
|||
622 |
✗✓ | 216106 |
if (silent) return; |
623 |
|||
624 |
432212 |
EmitAsyncInit(env(), resource, |
|
625 |
216106 |
env()->async_hooks()->provider_string(provider_type()), |
|
626 |
async_id_, trigger_async_id_); |
||
627 |
} |
||
628 |
|||
629 |
|||
630 |
216661 |
void AsyncWrap::EmitAsyncInit(Environment* env, |
|
631 |
Local<Object> object, |
||
632 |
Local<String> type, |
||
633 |
double async_id, |
||
634 |
double trigger_async_id) { |
||
635 |
✗✓ | 216661 |
CHECK(!object.IsEmpty()); |
636 |
✗✓ | 216661 |
CHECK(!type.IsEmpty()); |
637 |
216661 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
638 |
|||
639 |
// Nothing to execute, so can continue normally. |
||
640 |
✓✓ | 216661 |
if (async_hooks->fields()[AsyncHooks::kInit] == 0) { |
641 |
201069 |
return; |
|
642 |
} |
||
643 |
|||
644 |
31184 |
HandleScope scope(env->isolate()); |
|
645 |
15592 |
Local<Function> init_fn = env->async_hooks_init_function(); |
|
646 |
|||
647 |
Local<Value> argv[] = { |
||
648 |
Number::New(env->isolate(), async_id), |
||
649 |
type, |
||
650 |
Number::New(env->isolate(), trigger_async_id), |
||
651 |
object, |
||
652 |
46776 |
}; |
|
653 |
|||
654 |
15592 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
655 |
15592 |
USE(init_fn->Call(env->context(), object, arraysize(argv), argv)); |
|
656 |
} |
||
657 |
|||
658 |
|||
659 |
381919 |
MaybeLocal<Value> AsyncWrap::MakeCallback(const Local<Function> cb, |
|
660 |
int argc, |
||
661 |
Local<Value>* argv) { |
||
662 |
381919 |
EmitTraceEventBefore(); |
|
663 |
|||
664 |
381919 |
ProviderType provider = provider_type(); |
|
665 |
381919 |
async_context context { get_async_id(), get_trigger_async_id() }; |
|
666 |
MaybeLocal<Value> ret = InternalMakeCallback( |
||
667 |
381919 |
env(), object(), object(), cb, argc, argv, context); |
|
668 |
|||
669 |
// This is a static call with cached values because the `this` object may |
||
670 |
// no longer be alive at this point. |
||
671 |
381751 |
EmitTraceEventAfter(provider, context.async_id); |
|
672 |
|||
673 |
381751 |
return ret; |
|
674 |
} |
||
675 |
|||
676 |
std::string AsyncWrap::MemoryInfoName() const { |
||
677 |
return provider_names[provider_type()]; |
||
678 |
} |
||
679 |
|||
680 |
std::string AsyncWrap::diagnostic_name() const { |
||
681 |
return MemoryInfoName() + " (" + std::to_string(env()->thread_id()) + ":" + |
||
682 |
std::to_string(static_cast<int64_t>(async_id_)) + ")"; |
||
683 |
} |
||
684 |
|||
685 |
1028 |
Local<Object> AsyncWrap::GetOwner() { |
|
686 |
1028 |
return GetOwner(env(), object()); |
|
687 |
} |
||
688 |
|||
689 |
1028 |
Local<Object> AsyncWrap::GetOwner(Environment* env, Local<Object> obj) { |
|
690 |
1028 |
EscapableHandleScope handle_scope(env->isolate()); |
|
691 |
✗✓ | 1028 |
CHECK(!obj.IsEmpty()); |
692 |
|||
693 |
1028 |
TryCatchScope ignore_exceptions(env); |
|
694 |
while (true) { |
||
695 |
Local<Value> owner; |
||
696 |
6126 |
if (!obj->Get(env->context(), |
|
697 |
✓✗✓✓ |
8168 |
env->owner_symbol()).ToLocal(&owner) || |
698 |
✓✓ | 2042 |
!owner->IsObject()) { |
699 |
1028 |
return handle_scope.Escape(obj); |
|
700 |
} |
||
701 |
|||
702 |
1014 |
obj = owner.As<Object>(); |
|
703 |
1014 |
} |
|
704 |
} |
||
705 |
|||
706 |
} // namespace node |
||
707 |
|||
708 |
5787 |
NODE_BINDING_CONTEXT_AWARE_INTERNAL(async_wrap, node::AsyncWrap::Initialize) |
|
709 |
5716 |
NODE_BINDING_EXTERNAL_REFERENCE(async_wrap, |
|
710 |
node::AsyncWrap::RegisterExternalReferences) |
Generated by: GCOVR (Version 4.2) |