GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: api/callback.cc Lines: 139 155 89.7 %
Date: 2022-03-04 04:14:58 Branches: 80 96 83.3 %

Line Branch Exec Source
1
#include "node.h"
2
#include "async_wrap-inl.h"
3
#include "env-inl.h"
4
#include "v8.h"
5
6
namespace node {
7
8
using v8::Context;
9
using v8::EscapableHandleScope;
10
using v8::Function;
11
using v8::HandleScope;
12
using v8::Isolate;
13
using v8::Local;
14
using v8::MaybeLocal;
15
using v8::Object;
16
using v8::String;
17
using v8::Value;
18
19
4
CallbackScope::CallbackScope(Isolate* isolate,
20
                             Local<Object> object,
21
4
                             async_context async_context)
22
4
  : CallbackScope(Environment::GetCurrent(isolate), object, async_context) {}
23
24
115546
CallbackScope::CallbackScope(Environment* env,
25
                             Local<Object> object,
26
115546
                             async_context asyncContext)
27
  : private_(new InternalCallbackScope(env,
28
                                       object,
29
115546
                                       asyncContext)),
30
231092
    try_catch_(env->isolate()) {
31
115546
  try_catch_.SetVerbose(true);
32
115546
}
33
34
115545
CallbackScope::~CallbackScope() {
35
115545
  if (try_catch_.HasCaught())
36
1
    private_->MarkAsFailed();
37
115545
  delete private_;
38
115545
}
39
40
54365
InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags)
41
    : InternalCallbackScope(async_wrap->env(),
42
                            async_wrap->object(),
43
54365
                            { async_wrap->get_async_id(),
44
54365
                              async_wrap->get_trigger_async_id() },
45
54365
                            flags) {}
46
47
796297
InternalCallbackScope::InternalCallbackScope(Environment* env,
48
                                             Local<Object> object,
49
                                             const async_context& asyncContext,
50
796297
                                             int flags)
51
  : env_(env),
52
    async_context_(asyncContext),
53
    object_(object),
54
796297
    skip_hooks_(flags & kSkipAsyncHooks),
55
1592594
    skip_task_queues_(flags & kSkipTaskQueues) {
56
796297
  CHECK_NOT_NULL(env);
57
796297
  env->PushAsyncCallbackScope();
58
59
796297
  if (!env->can_call_into_js()) {
60
11900
    failed_ = true;
61
11900
    return;
62
  }
63
64
784397
  Isolate* isolate = env->isolate();
65
66
1568794
  HandleScope handle_scope(isolate);
67
784397
  Local<Context> current_context = isolate->GetCurrentContext();
68
  // If you hit this assertion, the caller forgot to enter the right Node.js
69
  // Environment's v8::Context first.
70
  // We first check `env->context() != current_context` because the contexts
71
  // likely *are* the same, in which case we can skip the slightly more
72
  // expensive Environment::GetCurrent() call.
73
1568794
  if (UNLIKELY(env->context() != current_context)) {
74
6
    CHECK_EQ(Environment::GetCurrent(isolate), env);
75
  }
76
77
784397
  isolate->SetIdle(false);
78
79
784397
  env->async_hooks()->push_async_context(
80
    async_context_.async_id, async_context_.trigger_async_id, object);
81
82
784397
  pushed_ids_ = true;
83
84

784397
  if (asyncContext.async_id != 0 && !skip_hooks_) {
85
    // No need to check a return value because the application will exit if
86
    // an exception occurs.
87
496172
    AsyncWrap::EmitBefore(env, asyncContext.async_id);
88
  }
89
}
90
91
1591686
InternalCallbackScope::~InternalCallbackScope() {
92
795882
  Close();
93
795804
  env_->PopAsyncCallbackScope();
94
795804
}
95
96
1203886
void InternalCallbackScope::Close() {
97
1807075
  if (closed_) return;
98
795993
  closed_ = true;
99
100
795993
  Isolate* isolate = env_->isolate();
101
1591797
  auto idle = OnScopeLeave([&]() { isolate->SetIdle(true); });
102
103
795993
  if (!env_->can_call_into_js()) return;
104
1484729
  auto perform_stopping_check = [&]() {
105
1484729
    if (env_->is_stopping()) {
106
86
      MarkAsFailed();
107
86
      env_->async_hooks()->clear_async_id_stack();
108
    }
109
2268774
  };
110
784045
  perform_stopping_check();
111
112

784045
  if (!failed_ && async_context_.async_id != 0 && !skip_hooks_) {
113
496102
    AsyncWrap::EmitAfter(env_, async_context_.async_id);
114
  }
115
116
784045
  if (pushed_ids_)
117
784045
    env_->async_hooks()->pop_async_context(async_context_.async_id);
118
119
784044
  if (failed_) return;
120
121

783057
  if (env_->async_callback_scope_depth() > 1 || skip_task_queues_) {
122
83344
    return;
123
  }
124
125
699713
  TickInfo* tick_info = env_->tick_info();
126
127
699713
  if (!env_->can_call_into_js()) return;
128
129
1399236
  auto weakref_cleanup = OnScopeLeave([&]() { env_->RunWeakRefCleanup(); });
130
131
699712
  Local<Context> context = env_->context();
132
699712
  if (!tick_info->has_tick_scheduled()) {
133
508110
    context->GetMicrotaskQueue()->PerformCheckpoint(isolate);
134
135
508069
    perform_stopping_check();
136
  }
137
138
  // Make sure the stack unwound properly. If there are nested MakeCallback's
139
  // then it should return early and not reach this code.
140
699671
  if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
141
69375
    CHECK_EQ(env_->execution_async_id(), 0);
142
69375
    CHECK_EQ(env_->trigger_async_id(), 0);
143
  }
144
145

699671
  if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) {
146
506909
    return;
147
  }
148
149
192762
  HandleScope handle_scope(isolate);
150
192762
  Local<Object> process = env_->process_object();
151
152
192762
  if (!env_->can_call_into_js()) return;
153
154
192762
  Local<Function> tick_callback = env_->tick_callback_function();
155
156
  // The tick is triggered before JS land calls SetTickCallback
157
  // to initializes the tick callback during bootstrap.
158
192762
  CHECK(!tick_callback.IsEmpty());
159
160
385377
  if (tick_callback->Call(context, process, 0, nullptr).IsEmpty()) {
161
233
    failed_ = true;
162
  }
163
192615
  perform_stopping_check();
164
}
165
166
410380
MaybeLocal<Value> InternalMakeCallback(Environment* env,
167
                                       Local<Object> resource,
168
                                       Local<Object> recv,
169
                                       const Local<Function> callback,
170
                                       int argc,
171
                                       Local<Value> argv[],
172
                                       async_context asyncContext) {
173
410380
  CHECK(!recv.IsEmpty());
174
#ifdef DEBUG
175
  for (int i = 0; i < argc; i++)
176
    CHECK(!argv[i].IsEmpty());
177
#endif
178
179
410380
  Local<Function> hook_cb = env->async_hooks_callback_trampoline();
180
410380
  int flags = InternalCallbackScope::kNoFlags;
181
410380
  bool use_async_hooks_trampoline = false;
182
410380
  AsyncHooks* async_hooks = env->async_hooks();
183
410380
  if (!hook_cb.IsEmpty()) {
184
    // Use the callback trampoline if there are any before or after hooks, or
185
    // we can expect some kind of usage of async_hooks.executionAsyncResource().
186
52481
    flags = InternalCallbackScope::kSkipAsyncHooks;
187
52481
    use_async_hooks_trampoline =
188
52481
        async_hooks->fields()[AsyncHooks::kBefore] +
189
52481
        async_hooks->fields()[AsyncHooks::kAfter] +
190
52481
        async_hooks->fields()[AsyncHooks::kUsesExecutionAsyncResource] > 0;
191
  }
192
193
820602
  InternalCallbackScope scope(env, resource, asyncContext, flags);
194
410380
  if (scope.Failed()) {
195
1326
    return MaybeLocal<Value>();
196
  }
197
198
  MaybeLocal<Value> ret;
199
200
409054
  Local<Context> context = env->context();
201
409054
  if (use_async_hooks_trampoline) {
202
30884
    MaybeStackBuffer<Local<Value>, 16> args(3 + argc);
203
61768
    args[0] = v8::Number::New(env->isolate(), asyncContext.async_id);
204
30884
    args[1] = resource;
205
30884
    args[2] = callback;
206
42293
    for (int i = 0; i < argc; i++) {
207
11409
      args[i + 3] = argv[i];
208
    }
209
30884
    ret = hook_cb->Call(context, recv, args.length(), &args[0]);
210
  } else {
211
378170
    ret = callback->Call(context, recv, argc, argv);
212
  }
213
214
409007
  if (ret.IsEmpty()) {
215
1003
    scope.MarkAsFailed();
216
1003
    return MaybeLocal<Value>();
217
  }
218
219
408004
  scope.Close();
220
407893
  if (scope.Failed()) {
221
144
    return MaybeLocal<Value>();
222
  }
223
224
407749
  return ret;
225
}
226
227
// Public MakeCallback()s
228
229
9873
MaybeLocal<Value> MakeCallback(Isolate* isolate,
230
                               Local<Object> recv,
231
                               const char* method,
232
                               int argc,
233
                               Local<Value> argv[],
234
                               async_context asyncContext) {
235
  Local<String> method_string =
236
9873
      String::NewFromUtf8(isolate, method).ToLocalChecked();
237
9873
  return MakeCallback(isolate, recv, method_string, argc, argv, asyncContext);
238
}
239
240
9882
MaybeLocal<Value> MakeCallback(Isolate* isolate,
241
                               Local<Object> recv,
242
                               Local<String> symbol,
243
                               int argc,
244
                               Local<Value> argv[],
245
                               async_context asyncContext) {
246
  // Check can_call_into_js() first because calling Get() might do so.
247
  Environment* env =
248
19764
      Environment::GetCurrent(recv->GetCreationContext().ToLocalChecked());
249
9882
  CHECK_NOT_NULL(env);
250
9893
  if (!env->can_call_into_js()) return Local<Value>();
251
252
  Local<Value> callback_v;
253
19742
  if (!recv->Get(isolate->GetCurrentContext(), symbol).ToLocal(&callback_v))
254
    return Local<Value>();
255
9871
  if (!callback_v->IsFunction()) {
256
    // This used to return an empty value, but Undefined() makes more sense
257
    // since no exception is pending here.
258
    return Undefined(isolate);
259
  }
260
9871
  Local<Function> callback = callback_v.As<Function>();
261
9871
  return MakeCallback(isolate, recv, callback, argc, argv, asyncContext);
262
}
263
264
53237
MaybeLocal<Value> MakeCallback(Isolate* isolate,
265
                               Local<Object> recv,
266
                               Local<Function> callback,
267
                               int argc,
268
                               Local<Value> argv[],
269
                               async_context asyncContext) {
270
  // Observe the following two subtleties:
271
  //
272
  // 1. The environment is retrieved from the callback function's context.
273
  // 2. The context to enter is retrieved from the environment.
274
  //
275
  // Because of the AssignToContext() call in src/node_contextify.cc,
276
  // the two contexts need not be the same.
277
  Environment* env =
278
106474
      Environment::GetCurrent(callback->GetCreationContext().ToLocalChecked());
279
53237
  CHECK_NOT_NULL(env);
280
53237
  Context::Scope context_scope(env->context());
281
  MaybeLocal<Value> ret =
282
53237
      InternalMakeCallback(env, recv, recv, callback, argc, argv, asyncContext);
283

53218
  if (ret.IsEmpty() && env->async_callback_scope_depth() == 0) {
284
    // This is only for legacy compatibility and we may want to look into
285
    // removing/adjusting it.
286
1002
    return Undefined(isolate);
287
  }
288
52216
  return ret;
289
}
290
291
// Use this if you just want to safely invoke some JS callback and
292
// would like to retain the currently active async_context, if any.
293
// In case none is available, a fixed default context will be
294
// installed otherwise.
295
16
MaybeLocal<Value> MakeSyncCallback(Isolate* isolate,
296
                                   Local<Object> recv,
297
                                   Local<Function> callback,
298
                                   int argc,
299
                                   Local<Value> argv[]) {
300
  Environment* env =
301
32
      Environment::GetCurrent(callback->GetCreationContext().ToLocalChecked());
302
16
  CHECK_NOT_NULL(env);
303
16
  if (!env->can_call_into_js()) return Local<Value>();
304
305
16
  Local<Context> context = env->context();
306
16
  Context::Scope context_scope(context);
307
16
  if (env->async_callback_scope_depth()) {
308
    // There's another MakeCallback() on the stack, piggy back on it.
309
    // In particular, retain the current async_context.
310
16
    return callback->Call(context, recv, argc, argv);
311
  }
312
313
  // This is a toplevel invocation and the caller (intentionally)
314
  // didn't provide any async_context to run in. Install a default context.
315
  MaybeLocal<Value> ret =
316
    InternalMakeCallback(env, env->process_object(), recv, callback, argc, argv,
317
                         async_context{0, 0});
318
  return ret;
319
}
320
321
// Legacy MakeCallback()s
322
323
Local<Value> MakeCallback(Isolate* isolate,
324
                          Local<Object> recv,
325
                          const char* method,
326
                          int argc,
327
                          Local<Value>* argv) {
328
  EscapableHandleScope handle_scope(isolate);
329
  return handle_scope.Escape(
330
      MakeCallback(isolate, recv, method, argc, argv, {0, 0})
331
          .FromMaybe(Local<Value>()));
332
}
333
334
Local<Value> MakeCallback(Isolate* isolate,
335
                          Local<Object> recv,
336
                          Local<String> symbol,
337
                          int argc,
338
                          Local<Value>* argv) {
339
  EscapableHandleScope handle_scope(isolate);
340
  return handle_scope.Escape(
341
      MakeCallback(isolate, recv, symbol, argc, argv, {0, 0})
342
          .FromMaybe(Local<Value>()));
343
}
344
345
Local<Value> MakeCallback(Isolate* isolate,
346
                          Local<Object> recv,
347
                          Local<Function> callback,
348
                          int argc,
349
                          Local<Value>* argv) {
350
  EscapableHandleScope handle_scope(isolate);
351
  return handle_scope.Escape(
352
      MakeCallback(isolate, recv, callback, argc, argv, {0, 0})
353
          .FromMaybe(Local<Value>()));
354
}
355
356
}  // namespace node