summaryrefslogtreecommitdiff
path: root/libdeno/binding.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libdeno/binding.cc')
-rw-r--r--libdeno/binding.cc312
1 files changed, 95 insertions, 217 deletions
diff --git a/libdeno/binding.cc b/libdeno/binding.cc
index b5d7ecafa..2a68d89aa 100644
--- a/libdeno/binding.cc
+++ b/libdeno/binding.cc
@@ -72,95 +72,94 @@ static inline v8::Local<v8::String> v8_str(const char* x) {
.ToLocalChecked();
}
-void HandleExceptionStr(v8::Local<v8::Context> context,
- v8::Local<v8::Value> exception,
- std::string* exception_str) {
+std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> exception) {
auto* isolate = context->GetIsolate();
- DenoIsolate* d = FromIsolate(isolate);
-
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(context);
auto message = v8::Exception::CreateMessage(isolate, exception);
auto stack_trace = message->GetStackTrace();
- auto line =
- v8::Integer::New(isolate, message->GetLineNumber(context).FromJust());
- auto column =
- v8::Integer::New(isolate, message->GetStartColumn(context).FromJust());
-
- auto global_error_handler_ = d->global_error_handler_.Get(isolate);
-
- if (!global_error_handler_.IsEmpty()) {
- // global_error_handler_ is set so we try to handle the exception in
- // javascript.
- v8::Local<v8::Value> args[5];
- args[0] = exception->ToString(context).ToLocalChecked();
- args[1] = message->GetScriptResourceName();
- args[2] = line;
- args[3] = column;
- args[4] = exception;
- global_error_handler_->Call(context->Global(), 5, args);
- /* message, source, lineno, colno, error */
- return;
- }
+ // Encode the exception into a JS object, which we will then turn into JSON.
+ auto json_obj = v8::Object::New(isolate);
+
+ auto exception_str = exception->ToString(context).ToLocalChecked();
+ // Alternate and very similar string. Not sure which is appropriate.
+ // auto exception_str = message->Get();
+ CHECK(json_obj->Set(context, v8_str("message"), exception_str).FromJust());
- char buf[12 * 1024];
+ v8::Local<v8::Array> frames;
if (!stack_trace.IsEmpty()) {
- // No javascript error handler, but we do have a stack trace. Format it
- // into a string and add to last_exception_.
- std::string msg;
- v8::String::Utf8Value exceptionStr(isolate, exception);
- msg += ToCString(exceptionStr);
- msg += "\n";
-
- for (int i = 0; i < stack_trace->GetFrameCount(); ++i) {
+ uint32_t count = static_cast<uint32_t>(stack_trace->GetFrameCount());
+ frames = v8::Array::New(isolate, count);
+
+ for (uint32_t i = 0; i < count; ++i) {
auto frame = stack_trace->GetFrame(isolate, i);
- v8::String::Utf8Value script_name(isolate, frame->GetScriptName());
- int l = frame->GetLineNumber();
- int c = frame->GetColumn();
- snprintf(buf, sizeof(buf), "%s %d:%d\n", ToCString(script_name), l, c);
- msg += buf;
+ auto frame_obj = v8::Object::New(isolate);
+ CHECK(frames->Set(context, i, frame_obj).FromJust());
+ auto line = v8::Integer::New(isolate, frame->GetLineNumber());
+ auto column = v8::Integer::New(isolate, frame->GetColumn());
+ CHECK(frame_obj->Set(context, v8_str("line"), line).FromJust());
+ CHECK(frame_obj->Set(context, v8_str("column"), column).FromJust());
+ CHECK(frame_obj
+ ->Set(context, v8_str("functionName"), frame->GetFunctionName())
+ .FromJust());
+ CHECK(frame_obj
+ ->Set(context, v8_str("scriptName"),
+ frame->GetScriptNameOrSourceURL())
+ .FromJust());
+ CHECK(frame_obj
+ ->Set(context, v8_str("isEval"),
+ v8::Boolean::New(isolate, frame->IsEval()))
+ .FromJust());
+ CHECK(frame_obj
+ ->Set(context, v8_str("isConstructor"),
+ v8::Boolean::New(isolate, frame->IsConstructor()))
+ .FromJust());
+ CHECK(frame_obj
+ ->Set(context, v8_str("isWasm"),
+ v8::Boolean::New(isolate, frame->IsWasm()))
+ .FromJust());
}
- *exception_str += msg;
} else {
- // No javascript error handler, no stack trace. Format the little info we
- // have into a string and add to last_exception_.
- v8::String::Utf8Value exceptionStr(isolate, exception);
- v8::String::Utf8Value script_name(isolate,
- message->GetScriptResourceName());
- v8::String::Utf8Value line_str(isolate, line);
- v8::String::Utf8Value col_str(isolate, column);
- snprintf(buf, sizeof(buf), "%s\n%s %s:%s\n", ToCString(exceptionStr),
- ToCString(script_name), ToCString(line_str), ToCString(col_str));
- *exception_str += buf;
+ // No stack trace. We only have one stack frame of info..
+ frames = v8::Array::New(isolate, 1);
+
+ auto frame_obj = v8::Object::New(isolate);
+ CHECK(frames->Set(context, 0, frame_obj).FromJust());
+
+ auto line =
+ v8::Integer::New(isolate, message->GetLineNumber(context).FromJust());
+ auto column =
+ v8::Integer::New(isolate, message->GetStartColumn(context).FromJust());
+
+ CHECK(frame_obj->Set(context, v8_str("line"), line).FromJust());
+ CHECK(frame_obj->Set(context, v8_str("column"), column).FromJust());
+ CHECK(frame_obj
+ ->Set(context, v8_str("scriptName"),
+ message->GetScriptResourceName())
+ .FromJust());
}
+
+ CHECK(json_obj->Set(context, v8_str("frames"), frames).FromJust());
+
+ auto json_string = v8::JSON::Stringify(context, json_obj).ToLocalChecked();
+ v8::String::Utf8Value json_string_(isolate, json_string);
+ return std::string(ToCString(json_string_));
}
void HandleException(v8::Local<v8::Context> context,
v8::Local<v8::Value> exception) {
v8::Isolate* isolate = context->GetIsolate();
DenoIsolate* d = FromIsolate(isolate);
- std::string exception_str;
- HandleExceptionStr(context, exception, &exception_str);
+ std::string json_str = EncodeExceptionAsJSON(context, exception);
if (d != nullptr) {
- d->last_exception_ = exception_str;
+ d->last_exception_ = json_str;
} else {
- std::cerr << "Pre-Deno Exception " << exception_str << std::endl;
- exit(1);
- }
-}
-
-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";
+ // This shouldn't happen in normal circumstances. Added for debugging.
+ std::cerr << "Pre-Deno Exception " << json_str << std::endl;
+ CHECK(false);
}
}
@@ -169,40 +168,35 @@ void PromiseRejectCallback(v8::PromiseRejectMessage promise_reject_message) {
DenoIsolate* d = static_cast<DenoIsolate*>(isolate->GetData(0));
DCHECK_EQ(d->isolate_, isolate);
v8::HandleScope handle_scope(d->isolate_);
- auto exception = promise_reject_message.GetValue();
+ auto error = promise_reject_message.GetValue();
auto context = d->context_.Get(d->isolate_);
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;
+
+ int promise_id = promise->GetIdentityHash();
+ switch (promise_reject_message.GetEvent()) {
+ case v8::kPromiseRejectWithNoHandler:
+ // Insert the error into the pending_promise_map_ using the promise's id
+ // as the key.
+ d->pending_promise_map_.emplace(std::piecewise_construct,
+ std::make_tuple(promise_id),
+ std::make_tuple(d->isolate_, error));
+ break;
+
+ case v8::kPromiseHandlerAddedAfterReject:
+ d->pending_promise_map_.erase(promise_id);
+ break;
+
+ case v8::kPromiseRejectAfterResolved:
+ break;
+
+ case v8::kPromiseResolveAfterResolved:
+ // Should not warn. See #1272
+ break;
+
+ default:
+ CHECK(false && "unreachable");
}
}
@@ -359,69 +353,6 @@ void Shared(v8::Local<v8::Name> property,
info.GetReturnValue().Set(ab);
}
-// Sets the global error handler.
-void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Isolate* isolate = args.GetIsolate();
- DenoIsolate* d = FromIsolate(isolate);
- DCHECK_EQ(d->isolate_, isolate);
-
- v8::HandleScope handle_scope(isolate);
-
- if (!d->global_error_handler_.IsEmpty()) {
- isolate->ThrowException(
- v8_str("libdeno.setGlobalErrorHandler 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->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();
- DenoIsolate* d = FromIsolate(isolate);
- 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();
- DenoIsolate* d = FromIsolate(isolate);
- 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) {
@@ -466,8 +397,7 @@ bool Execute(v8::Local<v8::Context> context, const char* js_filename,
}
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
- const char* js_filename, const char* js_source,
- const char* source_map) {
+ const char* js_filename, const char* js_source) {
CHECK_NE(js_source, nullptr);
CHECK_NE(js_filename, nullptr);
v8::HandleScope handle_scope(isolate);
@@ -493,61 +423,8 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared)
.FromJust());
- auto set_global_error_handler_tmpl =
- v8::FunctionTemplate::New(isolate, SetGlobalErrorHandler);
- auto set_global_error_handler_val =
- set_global_error_handler_tmpl->GetFunction(context).ToLocalChecked();
- CHECK(deno_val
- ->Set(context, deno::v8_str("setGlobalErrorHandler"),
- 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());
-
{
- if (source_map != nullptr) {
- v8::TryCatch try_catch(isolate);
- v8::ScriptOrigin origin(v8_str("set_source_map.js"));
- std::string source_map_parens =
- std::string("(") + std::string(source_map) + std::string(")");
- auto source_map_v8_str = deno::v8_str(source_map_parens.c_str());
- auto script = v8::Script::Compile(context, source_map_v8_str, &origin);
- if (script.IsEmpty()) {
- DCHECK(try_catch.HasCaught());
- HandleException(context, try_catch.Exception());
- return;
- }
- auto source_map_obj = script.ToLocalChecked()->Run(context);
- if (source_map_obj.IsEmpty()) {
- DCHECK(try_catch.HasCaught());
- HandleException(context, try_catch.Exception());
- return;
- }
- CHECK(deno_val
- ->Set(context, deno::v8_str("mainSourceMap"),
- source_map_obj.ToLocalChecked())
- .FromJust());
- }
-
auto source = deno::v8_str(js_source);
- CHECK(
- deno_val->Set(context, deno::v8_str("mainSource"), source).FromJust());
bool r = deno::ExecuteV8StringSource(context, js_filename, source);
CHECK(r);
@@ -558,10 +435,11 @@ void DenoIsolate::AddIsolate(v8::Isolate* isolate) {
isolate_ = isolate;
// Leaving this code here because it will probably be useful later on, but
// disabling it now as I haven't got tests for the desired behavior.
- // d->isolate->SetCaptureStackTraceForUncaughtExceptions(true);
// d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback);
// d->isolate->AddMessageListener(MessageCallback2);
// d->isolate->SetFatalErrorHandler(FatalErrorCallback2);
+ isolate_->SetCaptureStackTraceForUncaughtExceptions(
+ true, 10, v8::StackTrace::kDetailed);
isolate_->SetPromiseRejectCallback(deno::PromiseRejectCallback);
isolate_->SetData(0, this);
}