2019-01-29 21:31:59 -05:00
|
|
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
|
|
|
#include "exceptions.h"
|
|
|
|
#include <string>
|
2019-01-29 11:32:40 -05:00
|
|
|
|
|
|
|
namespace deno {
|
|
|
|
|
2019-02-09 16:55:40 -05:00
|
|
|
v8::Local<v8::Object> EncodeMessageAsObject(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Message> message) {
|
2019-01-29 11:32:40 -05:00
|
|
|
auto* isolate = context->GetIsolate();
|
2019-02-09 16:55:40 -05:00
|
|
|
v8::EscapableHandleScope handle_scope(isolate);
|
2019-01-29 11:32:40 -05:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
auto stack_trace = message->GetStackTrace();
|
|
|
|
|
|
|
|
// Encode the exception into a JS object, which we will then turn into JSON.
|
|
|
|
auto json_obj = v8::Object::New(isolate);
|
|
|
|
auto exception_str = message->Get();
|
|
|
|
CHECK(json_obj->Set(context, v8_str("message"), exception_str).FromJust());
|
|
|
|
|
|
|
|
auto maybe_source_line = message->GetSourceLine(context);
|
|
|
|
if (!maybe_source_line.IsEmpty()) {
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("sourceLine"),
|
|
|
|
maybe_source_line.ToLocalChecked())
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("scriptResourceName"),
|
|
|
|
message->GetScriptResourceName())
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
auto maybe_line_number = message->GetLineNumber(context);
|
|
|
|
if (maybe_line_number.IsJust()) {
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("lineNumber"),
|
|
|
|
v8::Integer::New(isolate, maybe_line_number.FromJust()))
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("startPosition"),
|
|
|
|
v8::Integer::New(isolate, message->GetStartPosition()))
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("endPosition"),
|
|
|
|
v8::Integer::New(isolate, message->GetEndPosition()))
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("errorLevel"),
|
|
|
|
v8::Integer::New(isolate, message->ErrorLevel()))
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
auto maybe_start_column = message->GetStartColumn(context);
|
|
|
|
if (maybe_start_column.IsJust()) {
|
|
|
|
auto start_column =
|
|
|
|
v8::Integer::New(isolate, maybe_start_column.FromJust());
|
|
|
|
CHECK(
|
|
|
|
json_obj->Set(context, v8_str("startColumn"), start_column).FromJust());
|
|
|
|
}
|
|
|
|
|
|
|
|
auto maybe_end_column = message->GetEndColumn(context);
|
|
|
|
if (maybe_end_column.IsJust()) {
|
|
|
|
auto end_column = v8::Integer::New(isolate, maybe_end_column.FromJust());
|
|
|
|
CHECK(json_obj->Set(context, v8_str("endColumn"), end_column).FromJust());
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("isSharedCrossOrigin"),
|
|
|
|
v8::Boolean::New(isolate, message->IsSharedCrossOrigin()))
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
CHECK(json_obj
|
|
|
|
->Set(context, v8_str("isOpaque"),
|
|
|
|
v8::Boolean::New(isolate, message->IsOpaque()))
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
v8::Local<v8::Array> frames;
|
|
|
|
if (!stack_trace.IsEmpty()) {
|
|
|
|
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);
|
|
|
|
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());
|
2019-07-09 14:03:06 -04:00
|
|
|
|
|
|
|
auto function_name = frame->GetFunctionName();
|
|
|
|
if (!function_name.IsEmpty()) {
|
|
|
|
CHECK(frame_obj->Set(context, v8_str("functionName"), function_name)
|
|
|
|
.FromJust());
|
|
|
|
}
|
2019-01-29 11:32:40 -05:00
|
|
|
// scriptName can be empty in special conditions e.g. eval
|
|
|
|
auto scriptName = frame->GetScriptNameOrSourceURL();
|
|
|
|
if (scriptName.IsEmpty()) {
|
|
|
|
scriptName = v8_str("<unknown>");
|
|
|
|
}
|
|
|
|
CHECK(
|
|
|
|
frame_obj->Set(context, v8_str("scriptName"), scriptName).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());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 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());
|
2019-02-09 16:55:40 -05:00
|
|
|
json_obj = handle_scope.Escape(json_obj);
|
|
|
|
return json_obj;
|
|
|
|
}
|
2019-01-29 11:32:40 -05:00
|
|
|
|
2019-02-09 16:55:40 -05:00
|
|
|
std::string EncodeMessageAsJSON(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Message> message) {
|
|
|
|
auto* isolate = context->GetIsolate();
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
auto json_obj = EncodeMessageAsObject(context, message);
|
2019-01-29 11:32:40 -05:00
|
|
|
auto json_string = v8::JSON::Stringify(context, json_obj).ToLocalChecked();
|
|
|
|
v8::String::Utf8Value json_string_(isolate, json_string);
|
|
|
|
return std::string(ToCString(json_string_));
|
|
|
|
}
|
|
|
|
|
2019-02-09 16:55:40 -05:00
|
|
|
v8::Local<v8::Object> EncodeExceptionAsObject(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Value> exception) {
|
|
|
|
auto* isolate = context->GetIsolate();
|
|
|
|
v8::EscapableHandleScope handle_scope(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
auto message = v8::Exception::CreateMessage(isolate, exception);
|
|
|
|
auto json_obj = EncodeMessageAsObject(context, message);
|
|
|
|
json_obj = handle_scope.Escape(json_obj);
|
|
|
|
return json_obj;
|
|
|
|
}
|
|
|
|
|
2019-01-29 11:32:40 -05:00
|
|
|
std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Value> exception) {
|
|
|
|
auto* isolate = context->GetIsolate();
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
auto message = v8::Exception::CreateMessage(isolate, exception);
|
|
|
|
return EncodeMessageAsJSON(context, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HandleException(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Value> exception) {
|
|
|
|
v8::Isolate* isolate = context->GetIsolate();
|
2019-03-21 09:48:19 -04:00
|
|
|
|
|
|
|
// TerminateExecution was called
|
|
|
|
if (isolate->IsExecutionTerminating()) {
|
|
|
|
// cancel exception termination so that the exception can be created
|
|
|
|
isolate->CancelTerminateExecution();
|
|
|
|
|
|
|
|
// maybe make a new exception object
|
|
|
|
if (exception->IsNullOrUndefined()) {
|
|
|
|
exception = v8::Exception::Error(v8_str("execution terminated"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle the exception as if it is a regular exception
|
|
|
|
HandleException(context, exception);
|
|
|
|
|
|
|
|
// re-enable exception termination
|
|
|
|
isolate->TerminateExecution();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-29 11:32:40 -05:00
|
|
|
DenoIsolate* d = DenoIsolate::FromIsolate(isolate);
|
|
|
|
std::string json_str = EncodeExceptionAsJSON(context, exception);
|
2019-02-04 11:53:40 -05:00
|
|
|
CHECK_NOT_NULL(d);
|
2019-01-29 11:32:40 -05:00
|
|
|
d->last_exception_ = json_str;
|
2019-07-23 15:12:49 -04:00
|
|
|
d->last_exception_handle_.Reset(isolate, exception);
|
2019-01-29 11:32:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void HandleExceptionMessage(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Message> message) {
|
|
|
|
v8::Isolate* isolate = context->GetIsolate();
|
2019-03-21 09:48:19 -04:00
|
|
|
|
|
|
|
// TerminateExecution was called
|
|
|
|
if (isolate->IsExecutionTerminating()) {
|
|
|
|
HandleException(context, v8::Undefined(isolate));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-29 11:32:40 -05:00
|
|
|
DenoIsolate* d = DenoIsolate::FromIsolate(isolate);
|
|
|
|
std::string json_str = EncodeMessageAsJSON(context, message);
|
2019-02-04 11:53:40 -05:00
|
|
|
CHECK_NOT_NULL(d);
|
2019-01-29 11:32:40 -05:00
|
|
|
d->last_exception_ = json_str;
|
|
|
|
}
|
2019-06-06 21:51:04 -04:00
|
|
|
|
2019-07-23 15:12:49 -04:00
|
|
|
void ClearException(v8::Local<v8::Context> context) {
|
|
|
|
v8::Isolate* isolate = context->GetIsolate();
|
|
|
|
DenoIsolate* d = DenoIsolate::FromIsolate(isolate);
|
|
|
|
CHECK_NOT_NULL(d);
|
|
|
|
|
|
|
|
d->last_exception_.clear();
|
|
|
|
d->last_exception_handle_.Reset();
|
|
|
|
}
|
|
|
|
|
2019-06-06 21:51:04 -04:00
|
|
|
void ThrowInvalidArgument(v8::Isolate* isolate) {
|
|
|
|
isolate->ThrowException(v8::Exception::TypeError(v8_str("Invalid Argument")));
|
|
|
|
}
|
|
|
|
|
2019-01-29 11:32:40 -05:00
|
|
|
} // namespace deno
|