GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: api/callback.cc Lines: 137 153 89.5 %
Date: 2021-09-30 04:12:30 Branches: 77 94 81.9 %

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
114385
CallbackScope::CallbackScope(Environment* env,
25
                             Local<Object> object,
26
114385
                             async_context asyncContext)
27
  : private_(new InternalCallbackScope(env,
28
                                       object,
29
114385
                                       asyncContext)),
30
228770
    try_catch_(env->isolate()) {
31
114385
  try_catch_.SetVerbose(true);
32
114385
}
33
34
114384
CallbackScope::~CallbackScope() {
35
114384
  if (try_catch_.HasCaught())
36
1
    private_->MarkAsFailed();
37
114384
  delete private_;
38
114384
}
39
40
53028
InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags)
41
    : InternalCallbackScope(async_wrap->env(),
42
                            async_wrap->object(),
43
53028
                            { async_wrap->get_async_id(),
44
53028
                              async_wrap->get_trigger_async_id() },
45
53028
                            flags) {}
46
47
779981
InternalCallbackScope::InternalCallbackScope(Environment* env,
48
                                             Local<Object> object,
49
                                             const async_context& asyncContext,
50
779981
                                             int flags)
51
  : env_(env),
52
    async_context_(asyncContext),
53
    object_(object),
54
779981
    skip_hooks_(flags & kSkipAsyncHooks),
55
1559962
    skip_task_queues_(flags & kSkipTaskQueues) {
56
779981
  CHECK_NOT_NULL(env);
57
779981
  env->PushAsyncCallbackScope();
58
59
779981
  if (!env->can_call_into_js()) {
60
11423
    failed_ = true;
61
11423
    return;
62
  }
63
64
768558
  Isolate* isolate = env->isolate();
65
66
1537116
  HandleScope handle_scope(isolate);
67
  // If you hit this assertion, you forgot to enter the v8::Context first.
68
768558
  CHECK_EQ(Environment::GetCurrent(isolate), env);
69
70
768558
  env->isolate()->SetIdle(false);
71
72
768558
  env->async_hooks()->push_async_context(
73
    async_context_.async_id, async_context_.trigger_async_id, object);
74
75
768558
  pushed_ids_ = true;
76
77

768558
  if (asyncContext.async_id != 0 && !skip_hooks_) {
78
    // No need to check a return value because the application will exit if
79
    // an exception occurs.
80
167403
    AsyncWrap::EmitBefore(env, asyncContext.async_id);
81
  }
82
}
83
84
1558875
InternalCallbackScope::~InternalCallbackScope() {
85
779469
  Close();
86
779406
  env_->PopAsyncCallbackScope();
87
779406
}
88
89
1177420
void InternalCallbackScope::Close() {
90
1776160
  if (closed_) return;
91
779666
  closed_ = true;
92
93
779666
  Isolate* isolate = env_->isolate();
94
1559072
  auto idle = OnScopeLeave([&]() { isolate->SetIdle(true); });
95
96
779666
  if (!env_->can_call_into_js()) return;
97
1453907
  auto perform_stopping_check = [&]() {
98
1453907
    if (env_->is_stopping()) {
99
77
      MarkAsFailed();
100
77
      env_->async_hooks()->clear_async_id_stack();
101
    }
102
2222096
  };
103
768189
  perform_stopping_check();
104
105

768189
  if (!failed_ && async_context_.async_id != 0 && !skip_hooks_) {
106
167391
    AsyncWrap::EmitAfter(env_, async_context_.async_id);
107
  }
108
109
768189
  if (pushed_ids_)
110
768189
    env_->async_hooks()->pop_async_context(async_context_.async_id);
111
112
768188
  if (failed_) return;
113
114

767200
  if (env_->async_callback_scope_depth() > 1 || skip_task_queues_) {
115
82286
    return;
116
  }
117
118
684914
  TickInfo* tick_info = env_->tick_info();
119
120
684914
  if (!env_->can_call_into_js()) return;
121
122
1369559
  auto weakref_cleanup = OnScopeLeave([&]() { env_->RunWeakRefCleanup(); });
123
124
684909
  Local<Context> context = env_->context();
125
684909
  if (!tick_info->has_tick_scheduled()) {
126
505088
    context->GetMicrotaskQueue()->PerformCheckpoint(isolate);
127
128
505052
    perform_stopping_check();
129
  }
130
131
  // Make sure the stack unwound properly. If there are nested MakeCallback's
132
  // then it should return early and not reach this code.
133
684873
  if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
134
67823
    CHECK_EQ(env_->execution_async_id(), 0);
135
67823
    CHECK_EQ(env_->trigger_async_id(), 0);
136
  }
137
138

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

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