|
| 1 | +#include "async_wrap.h" |
1 | 2 | #include "env-inl.h" |
2 | 3 | #include "node.h" |
3 | 4 | #include "node_errors.h" |
@@ -16,18 +17,54 @@ using v8::Context; |
16 | 17 | using v8::Function; |
17 | 18 | using v8::FunctionCallbackInfo; |
18 | 19 | using v8::Isolate; |
| 20 | +using v8::Just; |
19 | 21 | using v8::kPromiseHandlerAddedAfterReject; |
20 | 22 | using v8::kPromiseRejectAfterResolved; |
21 | 23 | using v8::kPromiseRejectWithNoHandler; |
22 | 24 | using v8::kPromiseResolveAfterResolved; |
23 | 25 | using v8::Local; |
| 26 | +using v8::Maybe; |
24 | 27 | using v8::Number; |
25 | 28 | using v8::Object; |
26 | 29 | using v8::Promise; |
27 | 30 | using v8::PromiseRejectEvent; |
28 | 31 | using v8::PromiseRejectMessage; |
29 | 32 | using v8::Value; |
30 | 33 |
|
| 34 | +static Maybe<double> GetAssignedPromiseAsyncId(Environment* env, |
| 35 | + Local<Promise> promise, |
| 36 | + Local<Value> id_symbol) { |
| 37 | + Local<Value> maybe_async_id; |
| 38 | + if (!promise->Get(env->context(), id_symbol).ToLocal(&maybe_async_id)) { |
| 39 | + return v8::Just(AsyncWrap::kInvalidAsyncId); |
| 40 | + } |
| 41 | + return maybe_async_id->IsNumber() |
| 42 | + ? maybe_async_id->NumberValue(env->context()) |
| 43 | + : v8::Just(AsyncWrap::kInvalidAsyncId); |
| 44 | +} |
| 45 | + |
| 46 | +static Maybe<double> GetAssignedPromiseWrapAsyncId(Environment* env, |
| 47 | + Local<Promise> promise, |
| 48 | + Local<Value> id_symbol) { |
| 49 | + // This check is imperfect. If the internal field is set, it should |
| 50 | + // be an object. If it's not, we just ignore it. Ideally v8 would |
| 51 | + // have had GetInternalField returning a MaybeLocal but this works |
| 52 | + // for now. |
| 53 | + Local<Value> promiseWrap = promise->GetInternalField(0); |
| 54 | + if (promiseWrap->IsObject()) { |
| 55 | + Local<Value> maybe_async_id; |
| 56 | + if (!promiseWrap.As<Object>()->Get(env->context(), id_symbol) |
| 57 | + .ToLocal(&maybe_async_id)) { |
| 58 | + return v8::Just(AsyncWrap::kInvalidAsyncId); |
| 59 | + } |
| 60 | + return maybe_async_id->IsNumber() |
| 61 | + ? maybe_async_id->NumberValue(env->context()) |
| 62 | + : v8::Just(AsyncWrap::kInvalidAsyncId); |
| 63 | + } else { |
| 64 | + return v8::Just(AsyncWrap::kInvalidAsyncId); |
| 65 | + } |
| 66 | +} |
| 67 | + |
31 | 68 | void PromiseRejectCallback(PromiseRejectMessage message) { |
32 | 69 | static std::atomic<uint64_t> unhandledRejections{0}; |
33 | 70 | static std::atomic<uint64_t> rejectionsHandledAfter{0}; |
@@ -76,12 +113,46 @@ void PromiseRejectCallback(PromiseRejectMessage message) { |
76 | 113 |
|
77 | 114 | Local<Value> args[] = { type, promise, value }; |
78 | 115 |
|
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. |
| 116 | + double async_id = AsyncWrap::kInvalidAsyncId; |
| 117 | + double trigger_async_id = AsyncWrap::kInvalidAsyncId; |
82 | 118 | TryCatchScope try_catch(env); |
| 119 | + |
| 120 | + if (!GetAssignedPromiseAsyncId(env, promise, env->async_id_symbol()) |
| 121 | + .To(&async_id)) return; |
| 122 | + if (!GetAssignedPromiseAsyncId(env, promise, env->trigger_async_id_symbol()) |
| 123 | + .To(&trigger_async_id)) return; |
| 124 | + |
| 125 | + if (async_id == AsyncWrap::kInvalidAsyncId && |
| 126 | + trigger_async_id == AsyncWrap::kInvalidAsyncId) { |
| 127 | + // That means that promise might be a PromiseWrap, so we'll |
| 128 | + // check there as well. |
| 129 | + if (!GetAssignedPromiseWrapAsyncId(env, promise, env->async_id_symbol()) |
| 130 | + .To(&async_id)) return; |
| 131 | + if (!GetAssignedPromiseWrapAsyncId( |
| 132 | + env, promise, env->trigger_async_id_symbol()) |
| 133 | + .To(&trigger_async_id)) return; |
| 134 | + } |
| 135 | + |
| 136 | + if (async_id != AsyncWrap::kInvalidAsyncId && |
| 137 | + trigger_async_id != AsyncWrap::kInvalidAsyncId) { |
| 138 | + env->async_hooks()->push_async_context( |
| 139 | + async_id, trigger_async_id, promise); |
| 140 | + } |
| 141 | + |
83 | 142 | USE(callback->Call( |
84 | 143 | env->context(), Undefined(isolate), arraysize(args), args)); |
| 144 | + |
| 145 | + if (async_id != AsyncWrap::kInvalidAsyncId && |
| 146 | + trigger_async_id != AsyncWrap::kInvalidAsyncId && |
| 147 | + env->execution_async_id() == async_id) { |
| 148 | + // This condition might not be true if async_hooks was enabled during |
| 149 | + // the promise callback execution. |
| 150 | + env->async_hooks()->pop_async_context(async_id); |
| 151 | + } |
| 152 | + |
| 153 | + // V8 does not expect this callback to have a scheduled exceptions once it |
| 154 | + // returns, so we print them out in a best effort to do something about it |
| 155 | + // without failing silently and without crashing the process. |
85 | 156 | if (try_catch.HasCaught() && !try_catch.HasTerminated()) { |
86 | 157 | fprintf(stderr, "Exception in PromiseRejectCallback:\n"); |
87 | 158 | PrintCaughtException(isolate, env->context(), try_catch); |
|