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