1 #include "env-inl.h"
2 #include "node.h"
3 #include "node_errors.h"
4 #include "node_internals.h"
5 #include "node_process-inl.h"
6 #include "util-inl.h"
7 #include "v8.h"
8
9 #include <atomic>
10
11 namespace node {
12
13 using errors::TryCatchScope;
14 using v8::Context;
15 using v8::Function;
16 using v8::FunctionCallbackInfo;
17 using v8::Isolate;
18 using v8::kPromiseHandlerAddedAfterReject;
19 using v8::kPromiseRejectAfterResolved;
20 using v8::kPromiseRejectWithNoHandler;
21 using v8::kPromiseResolveAfterResolved;
22 using v8::Local;
23 using v8::MicrotasksScope;
24 using v8::Number;
25 using v8::Object;
26 using v8::Promise;
27 using v8::PromiseRejectEvent;
28 using v8::PromiseRejectMessage;
29 using v8::Value;
30
PromiseRejectCallback(PromiseRejectMessage message)31 void PromiseRejectCallback(PromiseRejectMessage message) {
32 static std::atomic<uint64_t> unhandledRejections{0};
33 static std::atomic<uint64_t> rejectionsHandledAfter{0};
34
35 Local<Promise> promise = message.GetPromise();
36 Isolate* isolate = promise->GetIsolate();
37 PromiseRejectEvent event = message.GetEvent();
38
39 Environment* env = Environment::GetCurrent(isolate);
40
41 if (env == nullptr) return;
42
43 Local<Function> callback = env->promise_reject_callback();
44 // The promise is rejected before JS land calls SetPromiseRejectCallback
45 // to initializes the promise reject callback during bootstrap.
46 CHECK(!callback.IsEmpty());
47
48 Local<Value> value;
49 Local<Value> type = Number::New(env->isolate(), event);
50
51 if (event == kPromiseRejectWithNoHandler) {
52 value = message.GetValue();
53 unhandledRejections++;
54 TRACE_COUNTER2(TRACING_CATEGORY_NODE2(promises, rejections),
55 "rejections",
56 "unhandled", unhandledRejections,
57 "handledAfter", rejectionsHandledAfter);
58 } else if (event == kPromiseHandlerAddedAfterReject) {
59 value = Undefined(isolate);
60 rejectionsHandledAfter++;
61 TRACE_COUNTER2(TRACING_CATEGORY_NODE2(promises, rejections),
62 "rejections",
63 "unhandled", unhandledRejections,
64 "handledAfter", rejectionsHandledAfter);
65 } else if (event == kPromiseResolveAfterResolved) {
66 value = message.GetValue();
67 } else if (event == kPromiseRejectAfterResolved) {
68 value = message.GetValue();
69 } else {
70 return;
71 }
72
73 if (value.IsEmpty()) {
74 value = Undefined(isolate);
75 }
76
77 Local<Value> args[] = { type, promise, value };
78
79 // V8 does not expect this callback to have a scheduled exceptions once it
80 // returns, so we print them out in a best effort to do something about it
81 // without failing silently and without crashing the process.
82 TryCatchScope try_catch(env);
83 USE(callback->Call(
84 env->context(), Undefined(isolate), arraysize(args), args));
85 if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
86 fprintf(stderr, "Exception in PromiseRejectCallback:\n");
87 PrintCaughtException(isolate, env->context(), try_catch);
88 }
89 }
90 namespace task_queue {
91
EnqueueMicrotask(const FunctionCallbackInfo<Value> & args)92 static void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
93 Environment* env = Environment::GetCurrent(args);
94 Isolate* isolate = env->isolate();
95
96 CHECK(args[0]->IsFunction());
97
98 isolate->EnqueueMicrotask(args[0].As<Function>());
99 }
100
RunMicrotasks(const FunctionCallbackInfo<Value> & args)101 static void RunMicrotasks(const FunctionCallbackInfo<Value>& args) {
102 MicrotasksScope::PerformCheckpoint(args.GetIsolate());
103 }
104
SetTickCallback(const FunctionCallbackInfo<Value> & args)105 static void SetTickCallback(const FunctionCallbackInfo<Value>& args) {
106 Environment* env = Environment::GetCurrent(args);
107 CHECK(args[0]->IsFunction());
108 env->set_tick_callback_function(args[0].As<Function>());
109 }
110
SetPromiseRejectCallback(const FunctionCallbackInfo<Value> & args)111 static void SetPromiseRejectCallback(
112 const FunctionCallbackInfo<Value>& args) {
113 Environment* env = Environment::GetCurrent(args);
114
115 CHECK(args[0]->IsFunction());
116 env->set_promise_reject_callback(args[0].As<Function>());
117 }
118
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)119 static void Initialize(Local<Object> target,
120 Local<Value> unused,
121 Local<Context> context,
122 void* priv) {
123 Environment* env = Environment::GetCurrent(context);
124 Isolate* isolate = env->isolate();
125
126 env->SetMethod(target, "enqueueMicrotask", EnqueueMicrotask);
127 env->SetMethod(target, "setTickCallback", SetTickCallback);
128 env->SetMethod(target, "runMicrotasks", RunMicrotasks);
129 target->Set(env->context(),
130 FIXED_ONE_BYTE_STRING(isolate, "tickInfo"),
131 env->tick_info()->fields().GetJSArray()).Check();
132
133 Local<Object> events = Object::New(isolate);
134 NODE_DEFINE_CONSTANT(events, kPromiseRejectWithNoHandler);
135 NODE_DEFINE_CONSTANT(events, kPromiseHandlerAddedAfterReject);
136 NODE_DEFINE_CONSTANT(events, kPromiseResolveAfterResolved);
137 NODE_DEFINE_CONSTANT(events, kPromiseRejectAfterResolved);
138
139 target->Set(env->context(),
140 FIXED_ONE_BYTE_STRING(isolate, "promiseRejectEvents"),
141 events).Check();
142 env->SetMethod(target,
143 "setPromiseRejectCallback",
144 SetPromiseRejectCallback);
145 }
146
147 } // namespace task_queue
148 } // namespace node
149
150 NODE_MODULE_CONTEXT_AWARE_INTERNAL(task_queue, node::task_queue::Initialize)
151