• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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