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 |
24210 |
void AsyncWrap::DestroyAsyncIdsCallback(Environment* env) { |
|
67 |
24210 |
Local<Function> fn = env->async_hooks_destroy_function(); |
|
68 |
|||
69 |
24210 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
70 |
|||
71 |
4 |
do { |
|
72 |
24214 |
std::vector<double> destroy_async_id_list; |
|
73 |
24214 |
destroy_async_id_list.swap(*env->destroy_async_id_list()); |
|
74 |
✗✓ | 24214 |
if (!env->can_call_into_js()) return; |
75 |
✓✓ | 144043 |
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 |
119831 |
HandleScope scope(env->isolate()); |
|
79 |
239662 |
Local<Value> async_id_value = Number::New(env->isolate(), async_id); |
|
80 |
MaybeLocal<Value> ret = fn->Call( |
||
81 |
239662 |
env->context(), Undefined(env->isolate()), 1, &async_id_value); |
|
82 |
|||
83 |
✗✓ | 119829 |
if (ret.IsEmpty()) |
84 |
return; |
||
85 |
} |
||
86 |
✓✓ | 24212 |
} while (!env->destroy_async_id_list()->empty()); |
87 |
} |
||
88 |
|||
89 |
985751 |
void Emit(Environment* env, double async_id, AsyncHooks::Fields type, |
|
90 |
Local<Function> fn) { |
||
91 |
985751 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
92 |
|||
93 |
✓✓✗✓ ✓✓ |
985751 |
if (async_hooks->fields()[type] == 0 || !env->can_call_into_js()) |
94 |
985448 |
return; |
|
95 |
|||
96 |
606 |
HandleScope handle_scope(env->isolate()); |
|
97 |
303 |
Local<Value> async_id_value = Number::New(env->isolate(), async_id); |
|
98 |
303 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
99 |
606 |
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 |
365744 |
void AsyncWrap::EmitTraceEventBefore() { |
|
110 |
✗✗✗✗ ✓✗✓✓ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
365744 |
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 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✗✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✗✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ |
740810 |
NODE_ASYNC_PROVIDER_TYPES(V) |
118 |
#undef V |
||
119 |
default: |
||
120 |
UNREACHABLE(); |
||
121 |
} |
||
122 |
365744 |
} |
|
123 |
|||
124 |
|||
125 |
492917 |
void AsyncWrap::EmitBefore(Environment* env, double async_id) { |
|
126 |
492917 |
Emit(env, async_id, AsyncHooks::kBefore, |
|
127 |
env->async_hooks_before_function()); |
||
128 |
492917 |
} |
|
129 |
|||
130 |
|||
131 |
365515 |
void AsyncWrap::EmitTraceEventAfter(ProviderType type, double async_id) { |
|
132 |
✗✗✗✗ ✓✗✓✓ ✓✗✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
365515 |
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 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✗✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✗✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ |
740303 |
NODE_ASYNC_PROVIDER_TYPES(V) |
140 |
#undef V |
||
141 |
default: |
||
142 |
UNREACHABLE(); |
||
143 |
} |
||
144 |
365515 |
} |
|
145 |
|||
146 |
|||
147 |
492834 |
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 |
492834 |
Emit(env, async_id, AsyncHooks::kAfter, |
|
151 |
env->async_hooks_after_function()); |
||
152 |
492834 |
} |
|
153 |
|||
154 |
848 |
static void SetupHooks(const FunctionCallbackInfo<Value>& args) { |
|
155 |
848 |
Environment* env = Environment::GetCurrent(args); |
|
156 |
|||
157 |
✗✓ | 848 |
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 |
✗✓ | 1696 |
CHECK(env->async_hooks_init_function().IsEmpty()); |
164 |
|||
165 |
1696 |
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 |
✗✓ | 3392 |
SET_HOOK_FN(init); |
178 |
✗✓ | 4240 |
SET_HOOK_FN(before); |
179 |
✗✓ | 3392 |
SET_HOOK_FN(after); |
180 |
✗✓ | 3392 |
SET_HOOK_FN(destroy); |
181 |
✗✓ | 3392 |
SET_HOOK_FN(promise_resolve); |
182 |
#undef SET_HOOK_FN |
||
183 |
848 |
} |
|
184 |
|||
185 |
15861 |
static void SetPromiseHooks(const FunctionCallbackInfo<Value>& args) { |
|
186 |
15861 |
Environment* env = Environment::GetCurrent(args); |
|
187 |
|||
188 |
✓✓✓✓ ✓✓✓✓ |
87661 |
env->async_hooks()->SetJSPromiseHooks( |
189 |
24217 |
args[0]->IsFunction() ? args[0].As<Function>() : Local<Function>(), |
|
190 |
23950 |
args[1]->IsFunction() ? args[1].As<Function>() : Local<Function>(), |
|
191 |
23656 |
args[2]->IsFunction() ? args[2].As<Function>() : Local<Function>(), |
|
192 |
16086 |
args[3]->IsFunction() ? args[3].As<Function>() : Local<Function>()); |
|
193 |
15861 |
} |
|
194 |
|||
195 |
class DestroyParam { |
||
196 |
public: |
||
197 |
double asyncId; |
||
198 |
Environment* env; |
||
199 |
Global<Object> target; |
||
200 |
Global<Object> propBag; |
||
201 |
}; |
||
202 |
|||
203 |
427 |
static void DestroyParamCleanupHook(void* ptr) { |
|
204 |
✓✗ | 427 |
delete static_cast<DestroyParam*>(ptr); |
205 |
427 |
} |
|
206 |
|||
207 |
25253 |
void AsyncWrap::WeakCallback(const WeakCallbackInfo<DestroyParam>& info) { |
|
208 |
25253 |
HandleScope scope(info.GetIsolate()); |
|
209 |
|||
210 |
25253 |
std::unique_ptr<DestroyParam> p{info.GetParameter()}; |
|
211 |
Local<Object> prop_bag = PersistentToLocal::Default(info.GetIsolate(), |
||
212 |
25253 |
p->propBag); |
|
213 |
Local<Value> val; |
||
214 |
|||
215 |
25253 |
p->env->RemoveCleanupHook(DestroyParamCleanupHook, p.get()); |
|
216 |
|||
217 |
✓✓ | 27544 |
if (!prop_bag.IsEmpty() && |
218 |
✗✓ | 29835 |
!prop_bag->Get(p->env->context(), p->env->destroyed_string()) |
219 |
✗✓ | 2291 |
.ToLocal(&val)) { |
220 |
return; |
||
221 |
} |
||
222 |
|||
223 |
✓✓✓✓ ✓✓ |
27544 |
if (val.IsEmpty() || val->IsFalse()) { |
224 |
25248 |
AsyncWrap::EmitDestroy(p->env, p->asyncId); |
|
225 |
} |
||
226 |
// unique_ptr goes out of scope here and pointer is deleted. |
||
227 |
} |
||
228 |
|||
229 |
|||
230 |
✓✗ | 26285 |
static void RegisterDestroyHook(const FunctionCallbackInfo<Value>& args) { |
231 |
✗✓ | 26285 |
CHECK(args[0]->IsObject()); |
232 |
✗✓ | 26285 |
CHECK(args[1]->IsNumber()); |
233 |
✓✓✗✓ ✗✓ |
28715 |
CHECK(args.Length() == 2 || args[2]->IsObject()); |
234 |
|||
235 |
26285 |
Isolate* isolate = args.GetIsolate(); |
|
236 |
✓✗ | 26285 |
DestroyParam* p = new DestroyParam(); |
237 |
52570 |
p->asyncId = args[1].As<Number>()->Value(); |
|
238 |
26285 |
p->env = Environment::GetCurrent(args); |
|
239 |
✓✗ | 105140 |
p->target.Reset(isolate, args[0].As<Object>()); |
240 |
✓✓ | 26285 |
if (args.Length() > 2) { |
241 |
✓✗ | 9720 |
p->propBag.Reset(isolate, args[2].As<Object>()); |
242 |
} |
||
243 |
26285 |
p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter); |
|
244 |
26285 |
p->env->AddCleanupHook(DestroyParamCleanupHook, p); |
|
245 |
26285 |
} |
|
246 |
|||
247 |
101288 |
void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) { |
|
248 |
AsyncWrap* wrap; |
||
249 |
202576 |
args.GetReturnValue().Set(kInvalidAsyncId); |
|
250 |
✓✓ | 101288 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
251 |
202534 |
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 |
2403 |
void AsyncWrap::ExecutionAsyncResource( |
|
273 |
const FunctionCallbackInfo<Value>& args) { |
||
274 |
2403 |
Environment* env = Environment::GetCurrent(args); |
|
275 |
uint32_t index; |
||
276 |
✗✓ | 4806 |
if (!args[0]->Uint32Value(env->context()).To(&index)) return; |
277 |
4806 |
args.GetReturnValue().Set( |
|
278 |
env->async_hooks()->native_execution_async_resource(index)); |
||
279 |
} |
||
280 |
|||
281 |
|||
282 |
1262 |
void AsyncWrap::ClearAsyncIdStack(const FunctionCallbackInfo<Value>& args) { |
|
283 |
1262 |
Environment* env = Environment::GetCurrent(args); |
|
284 |
1262 |
env->async_hooks()->clear_async_id_stack(); |
|
285 |
1262 |
} |
|
286 |
|||
287 |
|||
288 |
✓✗ | 288 |
void AsyncWrap::AsyncReset(const FunctionCallbackInfo<Value>& args) { |
289 |
✗✓ | 288 |
CHECK(args[0]->IsObject()); |
290 |
|||
291 |
AsyncWrap* wrap; |
||
292 |
✗✓ | 288 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
293 |
|||
294 |
✓✗ | 576 |
Local<Object> resource = args[0].As<Object>(); |
295 |
double execution_async_id = |
||
296 |
✗✓ | 288 |
args[1]->IsNumber() ? args[1].As<Number>()->Value() : kInvalidAsyncId; |
297 |
288 |
wrap->AsyncReset(resource, execution_async_id); |
|
298 |
} |
||
299 |
|||
300 |
|||
301 |
288 |
void AsyncWrap::GetProviderType(const FunctionCallbackInfo<Value>& args) { |
|
302 |
AsyncWrap* wrap; |
||
303 |
576 |
args.GetReturnValue().Set(AsyncWrap::PROVIDER_NONE); |
|
304 |
✗✓ | 288 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
305 |
576 |
args.GetReturnValue().Set(wrap->provider_type()); |
|
306 |
} |
||
307 |
|||
308 |
|||
309 |
219265 |
void AsyncWrap::EmitDestroy(bool from_gc) { |
|
310 |
219265 |
AsyncWrap::EmitDestroy(env(), async_id_); |
|
311 |
// Ensure no double destroy is emitted via AsyncReset(). |
||
312 |
219265 |
async_id_ = kInvalidAsyncId; |
|
313 |
|||
314 |
✓✓✓✓ ✓✓ |
219265 |
if (!persistent().IsEmpty() && !from_gc) { |
315 |
6846 |
HandleScope handle_scope(env()->isolate()); |
|
316 |
27384 |
USE(object()->Set(env()->context(), env()->resource_symbol(), object())); |
|
317 |
} |
||
318 |
219265 |
} |
|
319 |
|||
320 |
✓✗ | 79300 |
void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo<Value>& args) { |
321 |
✗✓ | 79300 |
CHECK(args[0]->IsNumber()); |
322 |
79300 |
AsyncWrap::EmitDestroy( |
|
323 |
Environment::GetCurrent(args), |
||
324 |
158600 |
args[0].As<Number>()->Value()); |
|
325 |
79300 |
} |
|
326 |
|||
327 |
667 |
void AsyncWrap::SetCallbackTrampoline(const FunctionCallbackInfo<Value>& args) { |
|
328 |
667 |
Environment* env = Environment::GetCurrent(args); |
|
329 |
|||
330 |
✓✓ | 667 |
if (args[0]->IsFunction()) { |
331 |
962 |
env->set_async_hooks_callback_trampoline(args[0].As<Function>()); |
|
332 |
} else { |
||
333 |
186 |
env->set_async_hooks_callback_trampoline(Local<Function>()); |
|
334 |
} |
||
335 |
667 |
} |
|
336 |
|||
337 |
58856 |
Local<FunctionTemplate> AsyncWrap::GetConstructorTemplate(Environment* env) { |
|
338 |
58856 |
Local<FunctionTemplate> tmpl = env->async_wrap_ctor_template(); |
|
339 |
✓✓ | 58856 |
if (tmpl.IsEmpty()) { |
340 |
848 |
tmpl = env->NewFunctionTemplate(nullptr); |
|
341 |
848 |
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "AsyncWrap")); |
|
342 |
848 |
tmpl->Inherit(BaseObject::GetConstructorTemplate(env)); |
|
343 |
848 |
env->SetProtoMethod(tmpl, "getAsyncId", AsyncWrap::GetAsyncId); |
|
344 |
848 |
env->SetProtoMethod(tmpl, "asyncReset", AsyncWrap::AsyncReset); |
|
345 |
848 |
env->SetProtoMethod(tmpl, "getProviderType", AsyncWrap::GetProviderType); |
|
346 |
848 |
env->set_async_wrap_ctor_template(tmpl); |
|
347 |
} |
||
348 |
58856 |
return tmpl; |
|
349 |
} |
||
350 |
|||
351 |
848 |
void AsyncWrap::Initialize(Local<Object> target, |
|
352 |
Local<Value> unused, |
||
353 |
Local<Context> context, |
||
354 |
void* priv) { |
||
355 |
848 |
Environment* env = Environment::GetCurrent(context); |
|
356 |
848 |
Isolate* isolate = env->isolate(); |
|
357 |
1696 |
HandleScope scope(isolate); |
|
358 |
|||
359 |
848 |
env->SetMethod(target, "setupHooks", SetupHooks); |
|
360 |
848 |
env->SetMethod(target, "setCallbackTrampoline", SetCallbackTrampoline); |
|
361 |
848 |
env->SetMethod(target, "pushAsyncContext", PushAsyncContext); |
|
362 |
848 |
env->SetMethod(target, "popAsyncContext", PopAsyncContext); |
|
363 |
848 |
env->SetMethod(target, "executionAsyncResource", ExecutionAsyncResource); |
|
364 |
848 |
env->SetMethod(target, "clearAsyncIdStack", ClearAsyncIdStack); |
|
365 |
848 |
env->SetMethod(target, "queueDestroyAsyncId", QueueDestroyAsyncId); |
|
366 |
848 |
env->SetMethod(target, "setPromiseHooks", SetPromiseHooks); |
|
367 |
848 |
env->SetMethod(target, "registerDestroyHook", RegisterDestroyHook); |
|
368 |
|||
369 |
848 |
PropertyAttribute ReadOnlyDontDelete = |
|
370 |
static_cast<PropertyAttribute>(ReadOnly | DontDelete); |
||
371 |
|||
372 |
#define FORCE_SET_TARGET_FIELD(obj, str, field) \ |
||
373 |
(obj)->DefineOwnProperty(context, \ |
||
374 |
FIXED_ONE_BYTE_STRING(isolate, str), \ |
||
375 |
field, \ |
||
376 |
ReadOnlyDontDelete).FromJust() |
||
377 |
|||
378 |
// Attach the uint32_t[] where each slot contains the count of the number of |
||
379 |
// callbacks waiting to be called on a particular event. It can then be |
||
380 |
// incremented/decremented from JS quickly to communicate to C++ if there are |
||
381 |
// any callbacks waiting to be called. |
||
382 |
3392 |
FORCE_SET_TARGET_FIELD(target, |
|
383 |
"async_hook_fields", |
||
384 |
env->async_hooks()->fields().GetJSArray()); |
||
385 |
|||
386 |
// The following v8::Float64Array has 5 fields. These fields are shared in |
||
387 |
// this way to allow JS and C++ to read/write each value as quickly as |
||
388 |
// possible. The fields are represented as follows: |
||
389 |
// |
||
390 |
// kAsyncIdCounter: Maintains the state of the next unique id to be assigned. |
||
391 |
// |
||
392 |
// kDefaultTriggerAsyncId: Write the id of the resource responsible for a |
||
393 |
// handle's creation just before calling the new handle's constructor. |
||
394 |
// After the new handle is constructed kDefaultTriggerAsyncId is set back |
||
395 |
// to kInvalidAsyncId. |
||
396 |
3392 |
FORCE_SET_TARGET_FIELD(target, |
|
397 |
"async_id_fields", |
||
398 |
env->async_hooks()->async_id_fields().GetJSArray()); |
||
399 |
|||
400 |
3392 |
FORCE_SET_TARGET_FIELD(target, |
|
401 |
"execution_async_resources", |
||
402 |
env->async_hooks()->js_execution_async_resources()); |
||
403 |
|||
404 |
848 |
target->Set(context, |
|
405 |
env->async_ids_stack_string(), |
||
406 |
2544 |
env->async_hooks()->async_ids_stack().GetJSArray()).Check(); |
|
407 |
|||
408 |
848 |
Local<Object> constants = Object::New(isolate); |
|
409 |
#define SET_HOOKS_CONSTANT(name) \ |
||
410 |
FORCE_SET_TARGET_FIELD( \ |
||
411 |
constants, #name, Integer::New(isolate, AsyncHooks::name)) |
||
412 |
|||
413 |
3392 |
SET_HOOKS_CONSTANT(kInit); |
|
414 |
3392 |
SET_HOOKS_CONSTANT(kBefore); |
|
415 |
3392 |
SET_HOOKS_CONSTANT(kAfter); |
|
416 |
3392 |
SET_HOOKS_CONSTANT(kDestroy); |
|
417 |
3392 |
SET_HOOKS_CONSTANT(kPromiseResolve); |
|
418 |
3392 |
SET_HOOKS_CONSTANT(kTotals); |
|
419 |
3392 |
SET_HOOKS_CONSTANT(kCheck); |
|
420 |
3392 |
SET_HOOKS_CONSTANT(kExecutionAsyncId); |
|
421 |
3392 |
SET_HOOKS_CONSTANT(kTriggerAsyncId); |
|
422 |
3392 |
SET_HOOKS_CONSTANT(kAsyncIdCounter); |
|
423 |
3392 |
SET_HOOKS_CONSTANT(kDefaultTriggerAsyncId); |
|
424 |
3392 |
SET_HOOKS_CONSTANT(kUsesExecutionAsyncResource); |
|
425 |
3392 |
SET_HOOKS_CONSTANT(kStackLength); |
|
426 |
#undef SET_HOOKS_CONSTANT |
||
427 |
1696 |
FORCE_SET_TARGET_FIELD(target, "constants", constants); |
|
428 |
|||
429 |
848 |
Local<Object> async_providers = Object::New(isolate); |
|
430 |
#define V(p) \ |
||
431 |
FORCE_SET_TARGET_FIELD( \ |
||
432 |
async_providers, #p, Integer::New(isolate, AsyncWrap::PROVIDER_ ## p)); |
||
433 |
148400 |
NODE_ASYNC_PROVIDER_TYPES(V) |
|
434 |
#undef V |
||
435 |
2544 |
FORCE_SET_TARGET_FIELD(target, "Providers", async_providers); |
|
436 |
|||
437 |
#undef FORCE_SET_TARGET_FIELD |
||
438 |
|||
439 |
848 |
env->set_async_hooks_init_function(Local<Function>()); |
|
440 |
848 |
env->set_async_hooks_before_function(Local<Function>()); |
|
441 |
848 |
env->set_async_hooks_after_function(Local<Function>()); |
|
442 |
848 |
env->set_async_hooks_destroy_function(Local<Function>()); |
|
443 |
848 |
env->set_async_hooks_promise_resolve_function(Local<Function>()); |
|
444 |
848 |
env->set_async_hooks_callback_trampoline(Local<Function>()); |
|
445 |
848 |
env->set_async_hooks_binding(target); |
|
446 |
848 |
} |
|
447 |
|||
448 |
5184 |
void AsyncWrap::RegisterExternalReferences( |
|
449 |
ExternalReferenceRegistry* registry) { |
||
450 |
5184 |
registry->Register(SetupHooks); |
|
451 |
5184 |
registry->Register(SetCallbackTrampoline); |
|
452 |
5184 |
registry->Register(PushAsyncContext); |
|
453 |
5184 |
registry->Register(PopAsyncContext); |
|
454 |
5184 |
registry->Register(ExecutionAsyncResource); |
|
455 |
5184 |
registry->Register(ClearAsyncIdStack); |
|
456 |
5184 |
registry->Register(QueueDestroyAsyncId); |
|
457 |
5184 |
registry->Register(SetPromiseHooks); |
|
458 |
5184 |
registry->Register(RegisterDestroyHook); |
|
459 |
5184 |
registry->Register(AsyncWrap::GetAsyncId); |
|
460 |
5184 |
registry->Register(AsyncWrap::AsyncReset); |
|
461 |
5184 |
registry->Register(AsyncWrap::GetProviderType); |
|
462 |
5184 |
} |
|
463 |
|||
464 |
208309 |
AsyncWrap::AsyncWrap(Environment* env, |
|
465 |
Local<Object> object, |
||
466 |
ProviderType provider, |
||
467 |
208309 |
double execution_async_id) |
|
468 |
208309 |
: AsyncWrap(env, object, provider, execution_async_id, false) {} |
|
469 |
|||
470 |
208309 |
AsyncWrap::AsyncWrap(Environment* env, |
|
471 |
Local<Object> object, |
||
472 |
ProviderType provider, |
||
473 |
double execution_async_id, |
||
474 |
208309 |
bool silent) |
|
475 |
208309 |
: AsyncWrap(env, object) { |
|
476 |
✗✓ | 208309 |
CHECK_NE(provider, PROVIDER_NONE); |
477 |
208309 |
provider_type_ = provider; |
|
478 |
|||
479 |
// Use AsyncReset() call to execute the init() callbacks. |
||
480 |
208309 |
AsyncReset(object, execution_async_id, silent); |
|
481 |
208309 |
init_hook_ran_ = true; |
|
482 |
208309 |
} |
|
483 |
|||
484 |
AsyncWrap::AsyncWrap(Environment* env, |
||
485 |
Local<Object> object, |
||
486 |
ProviderType provider, |
||
487 |
double execution_async_id, |
||
488 |
double trigger_async_id) |
||
489 |
: AsyncWrap(env, object, provider, execution_async_id, true) { |
||
490 |
trigger_async_id_ = trigger_async_id; |
||
491 |
} |
||
492 |
|||
493 |
213468 |
AsyncWrap::AsyncWrap(Environment* env, Local<Object> object) |
|
494 |
213468 |
: BaseObject(env, object) { |
|
495 |
213468 |
} |
|
496 |
|||
497 |
// This method is necessary to work around one specific problem: |
||
498 |
// Before the init() hook runs, if there is one, the BaseObject() constructor |
||
499 |
// registers this object with the Environment for finalization and debugging |
||
500 |
// purposes. |
||
501 |
// If the Environment decides to inspect this object for debugging, it tries to |
||
502 |
// call virtual methods on this object that are only (meaningfully) implemented |
||
503 |
// by the subclasses of AsyncWrap. |
||
504 |
// This could, with bad luck, happen during the AsyncWrap() constructor, |
||
505 |
// because we run JS code as part of it and that in turn can lead to a heapdump |
||
506 |
// being taken, either through the inspector or our programmatic API for it. |
||
507 |
// The object being initialized is not fully constructed at that point, and |
||
508 |
// in particular its virtual function table points to the AsyncWrap one |
||
509 |
// (as the subclass constructor has not yet begun execution at that point). |
||
510 |
// This means that the functions that are used for heap dump memory tracking |
||
511 |
// are not yet available, and trying to call them would crash the process. |
||
512 |
// We use this particular `IsDoneInitializing()` method to tell the Environment |
||
513 |
// that such debugging methods are not yet available. |
||
514 |
// This may be somewhat unreliable when it comes to future changes, because |
||
515 |
// at this point it *only* protects AsyncWrap subclasses, and *only* for cases |
||
516 |
// where heap dumps are being taken while the init() hook is on the call stack. |
||
517 |
// For now, it seems like the best solution, though. |
||
518 |
207991 |
bool AsyncWrap::IsDoneInitializing() const { |
|
519 |
207991 |
return init_hook_ran_; |
|
520 |
} |
||
521 |
|||
522 |
424838 |
AsyncWrap::~AsyncWrap() { |
|
523 |
424838 |
EmitTraceEventDestroy(); |
|
524 |
424838 |
EmitDestroy(true /* from gc */); |
|
525 |
} |
||
526 |
|||
527 |
218780 |
void AsyncWrap::EmitTraceEventDestroy() { |
|
528 |
✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
218780 |
switch (provider_type()) { |
529 |
#define V(PROVIDER) \ |
||
530 |
case PROVIDER_ ## PROVIDER: \ |
||
531 |
TRACE_EVENT_NESTABLE_ASYNC_END0( \ |
||
532 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
533 |
#PROVIDER, static_cast<int64_t>(get_async_id())); \ |
||
534 |
break; |
||
535 |
✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✓✗✓ ✓✓✗✓ ✓✓✓✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✗✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✓✓ ✓✗✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✓✓✗✓ ✗✗✗✗ ✓✓✗✓ |
450024 |
NODE_ASYNC_PROVIDER_TYPES(V) |
536 |
#undef V |
||
537 |
default: |
||
538 |
UNREACHABLE(); |
||
539 |
} |
||
540 |
218780 |
} |
|
541 |
|||
542 |
324362 |
void AsyncWrap::EmitDestroy(Environment* env, double async_id) { |
|
543 |
✓✓✓✓ |
445549 |
if (env->async_hooks()->fields()[AsyncHooks::kDestroy] == 0 || |
544 |
✓✓ | 121187 |
!env->can_call_into_js()) { |
545 |
203342 |
return; |
|
546 |
} |
||
547 |
|||
548 |
✓✓ | 121020 |
if (env->destroy_async_id_list()->empty()) { |
549 |
24266 |
env->SetImmediate(&DestroyAsyncIdsCallback, CallbackFlags::kUnrefed); |
|
550 |
} |
||
551 |
|||
552 |
// If the list gets very large empty it faster using a Microtask. |
||
553 |
// Microtasks can't be added in GC context therefore we use an |
||
554 |
// interrupt to get this Microtask scheduled as fast as possible. |
||
555 |
✓✓ | 121020 |
if (env->destroy_async_id_list()->size() == 16384) { |
556 |
1 |
env->RequestInterrupt([](Environment* env) { |
|
557 |
3 |
env->context()->GetMicrotaskQueue()->EnqueueMicrotask( |
|
558 |
env->isolate(), |
||
559 |
1 |
[](void* arg) { |
|
560 |
1 |
DestroyAsyncIdsCallback(static_cast<Environment*>(arg)); |
|
561 |
1 |
}, env); |
|
562 |
1 |
}); |
|
563 |
} |
||
564 |
|||
565 |
121020 |
env->destroy_async_id_list()->push_back(async_id); |
|
566 |
} |
||
567 |
|||
568 |
// Generalized call for both the constructor and for handles that are pooled |
||
569 |
// and reused over their lifetime. This way a new uid can be assigned when |
||
570 |
// the resource is pulled out of the pool and put back into use. |
||
571 |
215350 |
void AsyncWrap::AsyncReset(Local<Object> resource, double execution_async_id, |
|
572 |
bool silent) { |
||
573 |
✗✓ | 215350 |
CHECK_NE(provider_type(), PROVIDER_NONE); |
574 |
|||
575 |
✓✓ | 215350 |
if (async_id_ != kInvalidAsyncId) { |
576 |
// This instance was in use before, we have already emitted an init with |
||
577 |
// its previous async_id and need to emit a matching destroy for that |
||
578 |
// before generating a new async_id. |
||
579 |
485 |
EmitDestroy(); |
|
580 |
} |
||
581 |
|||
582 |
// Now we can assign a new async_id_ to this instance. |
||
583 |
✓✗ | 215350 |
async_id_ = execution_async_id == kInvalidAsyncId ? env()->new_async_id() |
584 |
: execution_async_id; |
||
585 |
215350 |
trigger_async_id_ = env()->get_default_trigger_async_id(); |
|
586 |
|||
587 |
{ |
||
588 |
430700 |
HandleScope handle_scope(env()->isolate()); |
|
589 |
215350 |
Local<Object> obj = object(); |
|
590 |
✗✓ | 215350 |
CHECK(!obj.IsEmpty()); |
591 |
✓✓ | 215350 |
if (resource != obj) { |
592 |
14082 |
USE(obj->Set(env()->context(), env()->resource_symbol(), resource)); |
|
593 |
} |
||
594 |
} |
||
595 |
|||
596 |
✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✓✓ ✓✓✗✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✗✓✗ |
215350 |
switch (provider_type()) { |
597 |
#define V(PROVIDER) \ |
||
598 |
case PROVIDER_ ## PROVIDER: \ |
||
599 |
if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \ |
||
600 |
TRACING_CATEGORY_NODE1(async_hooks))) { \ |
||
601 |
auto data = tracing::TracedValue::Create(); \ |
||
602 |
data->SetInteger("executionAsyncId", \ |
||
603 |
static_cast<int64_t>(env()->execution_async_id())); \ |
||
604 |
data->SetInteger("triggerAsyncId", \ |
||
605 |
static_cast<int64_t>(get_trigger_async_id())); \ |
||
606 |
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( \ |
||
607 |
TRACING_CATEGORY_NODE1(async_hooks), \ |
||
608 |
#PROVIDER, static_cast<int64_t>(get_async_id()), \ |
||
609 |
"data", std::move(data)); \ |
||
610 |
} \ |
||
611 |
break; |
||
612 |
✗✗✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✓✓ ✓✓✓✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✓✓✓✓ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✓✓ ✓✗✓✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✓ ✗✗✗✗ |
215409 |
NODE_ASYNC_PROVIDER_TYPES(V) |
613 |
#undef V |
||
614 |
default: |
||
615 |
UNREACHABLE(); |
||
616 |
} |
||
617 |
|||
618 |
✗✓ | 215350 |
if (silent) return; |
619 |
|||
620 |
430700 |
EmitAsyncInit(env(), resource, |
|
621 |
215350 |
env()->async_hooks()->provider_string(provider_type()), |
|
622 |
async_id_, trigger_async_id_); |
||
623 |
} |
||
624 |
|||
625 |
|||
626 |
215903 |
void AsyncWrap::EmitAsyncInit(Environment* env, |
|
627 |
Local<Object> object, |
||
628 |
Local<String> type, |
||
629 |
double async_id, |
||
630 |
double trigger_async_id) { |
||
631 |
✗✓ | 215903 |
CHECK(!object.IsEmpty()); |
632 |
✗✓ | 215903 |
CHECK(!type.IsEmpty()); |
633 |
215903 |
AsyncHooks* async_hooks = env->async_hooks(); |
|
634 |
|||
635 |
// Nothing to execute, so can continue normally. |
||
636 |
✓✓ | 215903 |
if (async_hooks->fields()[AsyncHooks::kInit] == 0) { |
637 |
198991 |
return; |
|
638 |
} |
||
639 |
|||
640 |
33824 |
HandleScope scope(env->isolate()); |
|
641 |
16912 |
Local<Function> init_fn = env->async_hooks_init_function(); |
|
642 |
|||
643 |
Local<Value> argv[] = { |
||
644 |
Number::New(env->isolate(), async_id), |
||
645 |
type, |
||
646 |
Number::New(env->isolate(), trigger_async_id), |
||
647 |
object, |
||
648 |
50736 |
}; |
|
649 |
|||
650 |
16912 |
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); |
|
651 |
16912 |
USE(init_fn->Call(env->context(), object, arraysize(argv), argv)); |
|
652 |
} |
||
653 |
|||
654 |
|||
655 |
365744 |
MaybeLocal<Value> AsyncWrap::MakeCallback(const Local<Function> cb, |
|
656 |
int argc, |
||
657 |
Local<Value>* argv) { |
||
658 |
365744 |
EmitTraceEventBefore(); |
|
659 |
|||
660 |
365744 |
ProviderType provider = provider_type(); |
|
661 |
365744 |
async_context context { get_async_id(), get_trigger_async_id() }; |
|
662 |
MaybeLocal<Value> ret = InternalMakeCallback( |
||
663 |
365744 |
env(), object(), object(), cb, argc, argv, context); |
|
664 |
|||
665 |
// This is a static call with cached values because the `this` object may |
||
666 |
// no longer be alive at this point. |
||
667 |
365515 |
EmitTraceEventAfter(provider, context.async_id); |
|
668 |
|||
669 |
365515 |
return ret; |
|
670 |
} |
||
671 |
|||
672 |
std::string AsyncWrap::MemoryInfoName() const { |
||
673 |
return provider_names[provider_type()]; |
||
674 |
} |
||
675 |
|||
676 |
std::string AsyncWrap::diagnostic_name() const { |
||
677 |
return MemoryInfoName() + " (" + std::to_string(env()->thread_id()) + ":" + |
||
678 |
std::to_string(static_cast<int64_t>(async_id_)) + ")"; |
||
679 |
} |
||
680 |
|||
681 |
1015 |
Local<Object> AsyncWrap::GetOwner() { |
|
682 |
1015 |
return GetOwner(env(), object()); |
|
683 |
} |
||
684 |
|||
685 |
1015 |
Local<Object> AsyncWrap::GetOwner(Environment* env, Local<Object> obj) { |
|
686 |
1015 |
EscapableHandleScope handle_scope(env->isolate()); |
|
687 |
✗✓ | 1015 |
CHECK(!obj.IsEmpty()); |
688 |
|||
689 |
1015 |
TryCatchScope ignore_exceptions(env); |
|
690 |
while (true) { |
||
691 |
Local<Value> owner; |
||
692 |
6048 |
if (!obj->Get(env->context(), |
|
693 |
✓✗✓✓ |
8064 |
env->owner_symbol()).ToLocal(&owner) || |
694 |
✓✓ | 2016 |
!owner->IsObject()) { |
695 |
1015 |
return handle_scope.Escape(obj); |
|
696 |
} |
||
697 |
|||
698 |
1001 |
obj = owner.As<Object>(); |
|
699 |
1001 |
} |
|
700 |
} |
||
701 |
|||
702 |
} // namespace node |
||
703 |
|||
704 |
5252 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(async_wrap, node::AsyncWrap::Initialize) |
|
705 |
5184 |
NODE_MODULE_EXTERNAL_REFERENCE(async_wrap, |
|
706 |
node::AsyncWrap::RegisterExternalReferences) |
Generated by: GCOVR (Version 4.2) |