• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::MicrotasksScope;
16 using v8::NewStringType;
17 using v8::Object;
18 using v8::String;
19 using v8::Value;
20 
CallbackScope(Isolate * isolate,Local<Object> object,async_context asyncContext)21 CallbackScope::CallbackScope(Isolate* isolate,
22                              Local<Object> object,
23                              async_context asyncContext)
24   : private_(new InternalCallbackScope(Environment::GetCurrent(isolate),
25                                        object,
26                                        asyncContext)),
27     try_catch_(isolate) {
28   try_catch_.SetVerbose(true);
29 }
30 
~CallbackScope()31 CallbackScope::~CallbackScope() {
32   if (try_catch_.HasCaught())
33     private_->MarkAsFailed();
34   delete private_;
35 }
36 
InternalCallbackScope(AsyncWrap * async_wrap,int flags)37 InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags)
38     : InternalCallbackScope(async_wrap->env(),
39                             async_wrap->object(),
40                             { async_wrap->get_async_id(),
41                               async_wrap->get_trigger_async_id() },
42                             flags) {}
43 
InternalCallbackScope(Environment * env,Local<Object> object,const async_context & asyncContext,int flags)44 InternalCallbackScope::InternalCallbackScope(Environment* env,
45                                              Local<Object> object,
46                                              const async_context& asyncContext,
47                                              int flags)
48   : env_(env),
49     async_context_(asyncContext),
50     object_(object),
51     skip_hooks_(flags & kSkipAsyncHooks),
52     skip_task_queues_(flags & kSkipTaskQueues) {
53   CHECK_NOT_NULL(env);
54   env->PushAsyncCallbackScope();
55 
56   if (!env->can_call_into_js()) {
57     failed_ = true;
58     return;
59   }
60 
61   HandleScope handle_scope(env->isolate());
62   // If you hit this assertion, you forgot to enter the v8::Context first.
63   CHECK_EQ(Environment::GetCurrent(env->isolate()), env);
64 
65   env->async_hooks()->push_async_context(
66     async_context_.async_id, async_context_.trigger_async_id, object);
67 
68   pushed_ids_ = true;
69 
70   if (asyncContext.async_id != 0 && !skip_hooks_) {
71     // No need to check a return value because the application will exit if
72     // an exception occurs.
73     AsyncWrap::EmitBefore(env, asyncContext.async_id);
74   }
75 }
76 
~InternalCallbackScope()77 InternalCallbackScope::~InternalCallbackScope() {
78   Close();
79   env_->PopAsyncCallbackScope();
80 }
81 
Close()82 void InternalCallbackScope::Close() {
83   if (closed_) return;
84   closed_ = true;
85 
86   if (!env_->can_call_into_js()) return;
87   auto perform_stopping_check = [&]() {
88     if (env_->is_stopping()) {
89       MarkAsFailed();
90       env_->async_hooks()->clear_async_id_stack();
91     }
92   };
93   perform_stopping_check();
94 
95   if (!failed_ && async_context_.async_id != 0 && !skip_hooks_) {
96     AsyncWrap::EmitAfter(env_, async_context_.async_id);
97   }
98 
99   if (pushed_ids_)
100     env_->async_hooks()->pop_async_context(async_context_.async_id);
101 
102   if (failed_) return;
103 
104   if (env_->async_callback_scope_depth() > 1 || skip_task_queues_) {
105     return;
106   }
107 
108   TickInfo* tick_info = env_->tick_info();
109 
110   if (!env_->can_call_into_js()) return;
111 
112   auto weakref_cleanup = OnScopeLeave([&]() { env_->RunWeakRefCleanup(); });
113 
114   if (!tick_info->has_tick_scheduled()) {
115     MicrotasksScope::PerformCheckpoint(env_->isolate());
116 
117     perform_stopping_check();
118   }
119 
120   // Make sure the stack unwound properly. If there are nested MakeCallback's
121   // then it should return early and not reach this code.
122   if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
123     CHECK_EQ(env_->execution_async_id(), 0);
124     CHECK_EQ(env_->trigger_async_id(), 0);
125   }
126 
127   if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) {
128     return;
129   }
130 
131   HandleScope handle_scope(env_->isolate());
132   Local<Object> process = env_->process_object();
133 
134   if (!env_->can_call_into_js()) return;
135 
136   Local<Function> tick_callback = env_->tick_callback_function();
137 
138   // The tick is triggered before JS land calls SetTickCallback
139   // to initializes the tick callback during bootstrap.
140   CHECK(!tick_callback.IsEmpty());
141 
142   if (tick_callback->Call(env_->context(), process, 0, nullptr).IsEmpty()) {
143     failed_ = true;
144   }
145   perform_stopping_check();
146 }
147 
InternalMakeCallback(Environment * env,Local<Object> resource,Local<Object> recv,const Local<Function> callback,int argc,Local<Value> argv[],async_context asyncContext)148 MaybeLocal<Value> InternalMakeCallback(Environment* env,
149                                        Local<Object> resource,
150                                        Local<Object> recv,
151                                        const Local<Function> callback,
152                                        int argc,
153                                        Local<Value> argv[],
154                                        async_context asyncContext) {
155   CHECK(!recv.IsEmpty());
156 #ifdef DEBUG
157   for (int i = 0; i < argc; i++)
158     CHECK(!argv[i].IsEmpty());
159 #endif
160 
161   Local<Function> hook_cb = env->async_hooks_callback_trampoline();
162   int flags = InternalCallbackScope::kNoFlags;
163   bool use_async_hooks_trampoline = false;
164   AsyncHooks* async_hooks = env->async_hooks();
165   if (!hook_cb.IsEmpty()) {
166     // Use the callback trampoline if there are any before or after hooks, or
167     // we can expect some kind of usage of async_hooks.executionAsyncResource().
168     flags = InternalCallbackScope::kSkipAsyncHooks;
169     use_async_hooks_trampoline =
170         async_hooks->fields()[AsyncHooks::kBefore] +
171         async_hooks->fields()[AsyncHooks::kAfter] +
172         async_hooks->fields()[AsyncHooks::kUsesExecutionAsyncResource] > 0;
173   }
174 
175   InternalCallbackScope scope(env, resource, asyncContext, flags);
176   if (scope.Failed()) {
177     return MaybeLocal<Value>();
178   }
179 
180   MaybeLocal<Value> ret;
181 
182   if (use_async_hooks_trampoline) {
183     MaybeStackBuffer<Local<Value>, 16> args(3 + argc);
184     args[0] = v8::Number::New(env->isolate(), asyncContext.async_id);
185     args[1] = resource;
186     args[2] = callback;
187     for (int i = 0; i < argc; i++) {
188       args[i + 3] = argv[i];
189     }
190     ret = hook_cb->Call(env->context(), recv, args.length(), &args[0]);
191   } else {
192     ret = callback->Call(env->context(), recv, argc, argv);
193   }
194 
195   if (ret.IsEmpty()) {
196     scope.MarkAsFailed();
197     return MaybeLocal<Value>();
198   }
199 
200   scope.Close();
201   if (scope.Failed()) {
202     return MaybeLocal<Value>();
203   }
204 
205   return ret;
206 }
207 
208 // Public MakeCallback()s
209 
MakeCallback(Isolate * isolate,Local<Object> recv,const char * method,int argc,Local<Value> argv[],async_context asyncContext)210 MaybeLocal<Value> MakeCallback(Isolate* isolate,
211                                Local<Object> recv,
212                                const char* method,
213                                int argc,
214                                Local<Value> argv[],
215                                async_context asyncContext) {
216   Local<String> method_string =
217       String::NewFromUtf8(isolate, method, NewStringType::kNormal)
218           .ToLocalChecked();
219   return MakeCallback(isolate, recv, method_string, argc, argv, asyncContext);
220 }
221 
MakeCallback(Isolate * isolate,Local<Object> recv,Local<String> symbol,int argc,Local<Value> argv[],async_context asyncContext)222 MaybeLocal<Value> MakeCallback(Isolate* isolate,
223                                Local<Object> recv,
224                                Local<String> symbol,
225                                int argc,
226                                Local<Value> argv[],
227                                async_context asyncContext) {
228   // Check can_call_into_js() first because calling Get() might do so.
229   Environment* env = Environment::GetCurrent(recv->CreationContext());
230   CHECK_NOT_NULL(env);
231   if (!env->can_call_into_js()) return Local<Value>();
232 
233   Local<Value> callback_v;
234   if (!recv->Get(isolate->GetCurrentContext(), symbol).ToLocal(&callback_v))
235     return Local<Value>();
236   if (!callback_v->IsFunction()) {
237     // This used to return an empty value, but Undefined() makes more sense
238     // since no exception is pending here.
239     return Undefined(isolate);
240   }
241   Local<Function> callback = callback_v.As<Function>();
242   return MakeCallback(isolate, recv, callback, argc, argv, asyncContext);
243 }
244 
MakeCallback(Isolate * isolate,Local<Object> recv,Local<Function> callback,int argc,Local<Value> argv[],async_context asyncContext)245 MaybeLocal<Value> MakeCallback(Isolate* isolate,
246                                Local<Object> recv,
247                                Local<Function> callback,
248                                int argc,
249                                Local<Value> argv[],
250                                async_context asyncContext) {
251   // Observe the following two subtleties:
252   //
253   // 1. The environment is retrieved from the callback function's context.
254   // 2. The context to enter is retrieved from the environment.
255   //
256   // Because of the AssignToContext() call in src/node_contextify.cc,
257   // the two contexts need not be the same.
258   Environment* env = Environment::GetCurrent(callback->CreationContext());
259   CHECK_NOT_NULL(env);
260   Context::Scope context_scope(env->context());
261   MaybeLocal<Value> ret =
262       InternalMakeCallback(env, recv, recv, callback, argc, argv, asyncContext);
263   if (ret.IsEmpty() && env->async_callback_scope_depth() == 0) {
264     // This is only for legacy compatibility and we may want to look into
265     // removing/adjusting it.
266     return Undefined(env->isolate());
267   }
268   return ret;
269 }
270 
271 // Legacy MakeCallback()s
272 
MakeCallback(Isolate * isolate,Local<Object> recv,const char * method,int argc,Local<Value> * argv)273 Local<Value> MakeCallback(Isolate* isolate,
274                           Local<Object> recv,
275                           const char* method,
276                           int argc,
277                           Local<Value>* argv) {
278   EscapableHandleScope handle_scope(isolate);
279   return handle_scope.Escape(
280       MakeCallback(isolate, recv, method, argc, argv, {0, 0})
281           .FromMaybe(Local<Value>()));
282 }
283 
MakeCallback(Isolate * isolate,Local<Object> recv,Local<String> symbol,int argc,Local<Value> * argv)284 Local<Value> MakeCallback(Isolate* isolate,
285                           Local<Object> recv,
286                           Local<String> symbol,
287                           int argc,
288                           Local<Value>* argv) {
289   EscapableHandleScope handle_scope(isolate);
290   return handle_scope.Escape(
291       MakeCallback(isolate, recv, symbol, argc, argv, {0, 0})
292           .FromMaybe(Local<Value>()));
293 }
294 
MakeCallback(Isolate * isolate,Local<Object> recv,Local<Function> callback,int argc,Local<Value> * argv)295 Local<Value> MakeCallback(Isolate* isolate,
296                           Local<Object> recv,
297                           Local<Function> callback,
298                           int argc,
299                           Local<Value>* argv) {
300   EscapableHandleScope handle_scope(isolate);
301   return handle_scope.Escape(
302       MakeCallback(isolate, recv, callback, argc, argv, {0, 0})
303           .FromMaybe(Local<Value>()));
304 }
305 
306 }  // namespace node
307