diff options
author | Kevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com> | 2018-10-12 11:22:52 -0700 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2018-10-12 14:22:52 -0400 |
commit | 45d3b8955de628db0ef051eeb8e351837b4a3b3e (patch) | |
tree | 252bab755760d86cab48502abdabe6ff0c2b9af2 /libdeno/binding.cc | |
parent | c9f95d51da9f6075b1f28a842dc830ec5fe7a30e (diff) |
Fix promise reject issue (#936)
Diffstat (limited to 'libdeno/binding.cc')
-rw-r--r-- | libdeno/binding.cc | 143 |
1 files changed, 139 insertions, 4 deletions
diff --git a/libdeno/binding.cc b/libdeno/binding.cc index c5f582aae..37d358d78 100644 --- a/libdeno/binding.cc +++ b/libdeno/binding.cc @@ -138,15 +138,59 @@ void HandleException(v8::Local<v8::Context> context, } } -void ExitOnPromiseRejectCallback( - v8::PromiseRejectMessage promise_reject_message) { +const char* PromiseRejectStr(enum v8::PromiseRejectEvent e) { + switch (e) { + case v8::PromiseRejectEvent::kPromiseRejectWithNoHandler: + return "RejectWithNoHandler"; + case v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject: + return "HandlerAddedAfterReject"; + case v8::PromiseRejectEvent::kPromiseResolveAfterResolved: + return "ResolveAfterResolved"; + case v8::PromiseRejectEvent::kPromiseRejectAfterResolved: + return "RejectAfterResolved"; + } +} + +void PromiseRejectCallback(v8::PromiseRejectMessage promise_reject_message) { auto* isolate = v8::Isolate::GetCurrent(); Deno* d = static_cast<Deno*>(isolate->GetData(0)); DCHECK_EQ(d->isolate, isolate); v8::HandleScope handle_scope(d->isolate); auto exception = promise_reject_message.GetValue(); auto context = d->context.Get(d->isolate); - HandleException(context, exception); + auto promise = promise_reject_message.GetPromise(); + auto event = promise_reject_message.GetEvent(); + + v8::Context::Scope context_scope(context); + auto promise_reject_handler = d->promise_reject_handler.Get(isolate); + + if (!promise_reject_handler.IsEmpty()) { + v8::Local<v8::Value> args[3]; + args[1] = v8_str(PromiseRejectStr(event)); + args[2] = promise; + /* error, event, promise */ + if (event == v8::PromiseRejectEvent::kPromiseRejectWithNoHandler) { + d->pending_promise_events++; + // exception only valid for kPromiseRejectWithNoHandler + args[0] = exception; + } else if (event == + v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject) { + d->pending_promise_events--; // unhandled event cancelled + if (d->pending_promise_events < 0) { + d->pending_promise_events = 0; + } + // Placeholder, not actually used + args[0] = v8_str("Promise handler added"); + } else if (event == v8::PromiseRejectEvent::kPromiseResolveAfterResolved) { + d->pending_promise_events++; + args[0] = v8_str("Promise resolved after resolved"); + } else if (event == v8::PromiseRejectEvent::kPromiseRejectAfterResolved) { + d->pending_promise_events++; + args[0] = v8_str("Promise rejected after resolved"); + } + promise_reject_handler->Call(context->Global(), 3, args); + return; + } } void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { @@ -279,6 +323,48 @@ void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args) { d->global_error_handler.Reset(isolate, func); } +// Sets the promise uncaught reject handler +void SetPromiseRejectHandler(const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Isolate* isolate = args.GetIsolate(); + Deno* d = reinterpret_cast<Deno*>(isolate->GetData(0)); + DCHECK_EQ(d->isolate, isolate); + + v8::HandleScope handle_scope(isolate); + + if (!d->promise_reject_handler.IsEmpty()) { + isolate->ThrowException( + v8_str("libdeno.setPromiseRejectHandler already called.")); + return; + } + + v8::Local<v8::Value> v = args[0]; + CHECK(v->IsFunction()); + v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v); + + d->promise_reject_handler.Reset(isolate, func); +} + +// Sets the promise uncaught reject handler +void SetPromiseErrorExaminer(const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Isolate* isolate = args.GetIsolate(); + Deno* d = reinterpret_cast<Deno*>(isolate->GetData(0)); + DCHECK_EQ(d->isolate, isolate); + + v8::HandleScope handle_scope(isolate); + + if (!d->promise_error_examiner.IsEmpty()) { + isolate->ThrowException( + v8_str("libdeno.setPromiseErrorExaminer already called.")); + return; + } + + v8::Local<v8::Value> v = args[0]; + CHECK(v->IsFunction()); + v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v); + + d->promise_error_examiner.Reset(isolate, func); +} + bool ExecuteV8StringSource(v8::Local<v8::Context> context, const char* js_filename, v8::Local<v8::String> source) { @@ -354,6 +440,24 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context, set_global_error_handler_val) .FromJust()); + auto set_promise_reject_handler_tmpl = + v8::FunctionTemplate::New(isolate, SetPromiseRejectHandler); + auto set_promise_reject_handler_val = + set_promise_reject_handler_tmpl->GetFunction(context).ToLocalChecked(); + CHECK(deno_val + ->Set(context, deno::v8_str("setPromiseRejectHandler"), + set_promise_reject_handler_val) + .FromJust()); + + auto set_promise_error_examiner_tmpl = + v8::FunctionTemplate::New(isolate, SetPromiseErrorExaminer); + auto set_promise_error_examiner_val = + set_promise_error_examiner_tmpl->GetFunction(context).ToLocalChecked(); + CHECK(deno_val + ->Set(context, deno::v8_str("setPromiseErrorExaminer"), + set_promise_error_examiner_val) + .FromJust()); + { auto source = deno::v8_str(js_source.c_str()); CHECK( @@ -389,6 +493,7 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context, } void AddIsolate(Deno* d, v8::Isolate* isolate) { + d->pending_promise_events = 0; d->next_req_id = 0; d->isolate = isolate; // Leaving this code here because it will probably be useful later on, but @@ -397,7 +502,7 @@ void AddIsolate(Deno* d, v8::Isolate* isolate) { // d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback); // d->isolate->AddMessageListener(MessageCallback2); // d->isolate->SetFatalErrorHandler(FatalErrorCallback2); - d->isolate->SetPromiseRejectCallback(deno::ExitOnPromiseRejectCallback); + d->isolate->SetPromiseRejectCallback(deno::PromiseRejectCallback); d->isolate->SetData(0, d); } @@ -490,6 +595,36 @@ int deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf) { return 0; } +void deno_check_promise_errors(Deno* d) { + if (d->pending_promise_events > 0) { + auto* isolate = d->isolate; + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + auto context = d->context.Get(d->isolate); + v8::Context::Scope context_scope(context); + + v8::TryCatch try_catch(d->isolate); + auto promise_error_examiner = d->promise_error_examiner.Get(d->isolate); + if (promise_error_examiner.IsEmpty()) { + d->last_exception = + "libdeno.setPromiseErrorExaminer has not been called."; + return; + } + v8::Local<v8::Value> args[0]; + auto result = promise_error_examiner->Call(context->Global(), 0, args); + if (try_catch.HasCaught()) { + deno::HandleException(context, try_catch.Exception()); + } + d->pending_promise_events = 0; // reset + if (!result->BooleanValue(context).FromJust()) { + // Has uncaught promise reject error, exiting... + exit(1); + } + } +} + void deno_delete(Deno* d) { d->isolate->Dispose(); delete d; |