summaryrefslogtreecommitdiff
path: root/libdeno
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2018-12-06 23:05:36 -0500
committerGitHub <noreply@github.com>2018-12-06 23:05:36 -0500
commitc113df1bb8a0c7d0c560ad32c0291c918c7da7b4 (patch)
tree0d15de448be602c22aecb2ec65ac7667c437a209 /libdeno
parent568ac0c9026b6f4012e2511a026bb5eb31a06020 (diff)
Process source maps in Rust instead of JS (#1280)
- Improves speed and binary size significantly. - Makes deno_last_exception() output a JSON structure. - Isolate::execute and Isolate::event_loop now return structured, mapped JSError objects on errors. - Removes libdeno functions: libdeno.setGlobalErrorHandler() libdeno.setPromiseRejectHandler() libdeno.setPromiseErrorExaminer() In collaboration with Ryan Dahl.
Diffstat (limited to 'libdeno')
-rw-r--r--libdeno/api.cc37
-rw-r--r--libdeno/binding.cc312
-rw-r--r--libdeno/deno.h14
-rw-r--r--libdeno/internal.h19
-rw-r--r--libdeno/libdeno_test.cc65
-rw-r--r--libdeno/libdeno_test.js48
-rw-r--r--libdeno/snapshot_creator.cc4
7 files changed, 168 insertions, 331 deletions
diff --git a/libdeno/api.cc b/libdeno/api.cc
index 6f4ac826f..388ab6146 100644
--- a/libdeno/api.cc
+++ b/libdeno/api.cc
@@ -38,7 +38,7 @@ Deno* deno_new(deno_buf snapshot, deno_config config) {
if (!snapshot.data_ptr) {
// If no snapshot is provided, we initialize the context with empty
// main source code and source maps.
- deno::InitializeContext(isolate, context, "", "", "");
+ deno::InitializeContext(isolate, context, "", "");
}
d->context_.Reset(isolate, context);
}
@@ -47,7 +47,7 @@ Deno* deno_new(deno_buf snapshot, deno_config config) {
}
Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
- const char* js_source, const char* source_map) {
+ const char* js_source) {
auto* creator = new v8::SnapshotCreator(deno::external_references);
auto* isolate = creator->GetIsolate();
auto* d = new deno::DenoIsolate(deno::empty_buf, config);
@@ -61,8 +61,7 @@ Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
creator->SetDefaultContext(context,
v8::SerializeInternalFieldsCallback(
deno::SerializeInternalFields, nullptr));
- deno::InitializeContext(isolate, context, js_filename, js_source,
- source_map);
+ deno::InitializeContext(isolate, context, js_filename, js_source);
}
return reinterpret_cast<Deno*>(d);
}
@@ -96,7 +95,11 @@ void deno_set_v8_flags(int* argc, char** argv) {
const char* deno_last_exception(Deno* d_) {
auto* d = unwrap(d_);
- return d->last_exception_.c_str();
+ if (d->last_exception_.length() > 0) {
+ return d->last_exception_.c_str();
+ } else {
+ return nullptr;
+ }
}
int deno_execute(Deno* d_, void* user_data, const char* js_filename,
@@ -154,31 +157,19 @@ int deno_respond(Deno* d_, void* user_data, int32_t req_id, deno_buf buf) {
void deno_check_promise_errors(Deno* d_) {
auto* d = unwrap(d_);
- if (d->pending_promise_events_ > 0) {
+ if (d->pending_promise_map_.size() > 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);
+ auto it = d->pending_promise_map_.begin();
+ while (it != d->pending_promise_map_.end()) {
+ auto error = it->second.Get(isolate);
+ deno::HandleException(context, error);
+ it = d->pending_promise_map_.erase(it);
}
}
}
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);
}
diff --git a/libdeno/deno.h b/libdeno/deno.h
index fb0b13746..48356bc86 100644
--- a/libdeno/deno.h
+++ b/libdeno/deno.h
@@ -37,7 +37,7 @@ typedef struct {
Deno* deno_new(deno_buf snapshot, deno_config config);
Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
- const char* js_source, const char* source_map);
+ const char* js_source);
// Generate a snapshot. The resulting buf can be used with deno_new.
// The caller must free the returned data by calling delete[] buf.data_ptr.
@@ -48,6 +48,12 @@ void deno_delete(Deno* d);
// Returns false on error.
// Get error text with deno_last_exception().
// 0 = fail, 1 = success
+//
+// TODO change return value to be const char*. On success the return
+// value is nullptr, on failure it is the JSON exception text that
+// is returned by deno_last_exception(). Remove deno_last_exception().
+// The return string is valid until the next execution of deno_execute or
+// deno_respond (as deno_last_exception is now).
int deno_execute(Deno* d, void* user_data, const char* js_filename,
const char* js_source);
@@ -69,6 +75,12 @@ int deno_execute(Deno* d, void* user_data, const char* js_filename,
//
// A non-zero return value, means a JS exception was encountered during the
// libdeno.recv() callback. Check deno_last_exception() for exception text.
+//
+// TODO change return value to be const char*. On success the return
+// value is nullptr, on failure it is the JSON exception text that
+// is returned by deno_last_exception(). Remove deno_last_exception().
+// The return string is valid until the next execution of deno_execute or
+// deno_respond (as deno_last_exception is now).
int deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf);
void deno_check_promise_errors(Deno* d);
diff --git a/libdeno/internal.h b/libdeno/internal.h
index 8a366d515..3dc4d8d0e 100644
--- a/libdeno/internal.h
+++ b/libdeno/internal.h
@@ -19,7 +19,6 @@ class DenoIsolate {
current_args_(nullptr),
snapshot_creator_(nullptr),
global_import_buf_ptr_(nullptr),
- pending_promise_events_(0),
recv_cb_(config.recv_cb),
next_req_id_(0),
user_data_(nullptr) {
@@ -47,13 +46,13 @@ class DenoIsolate {
const v8::FunctionCallbackInfo<v8::Value>* current_args_;
v8::SnapshotCreator* snapshot_creator_;
void* global_import_buf_ptr_;
- int32_t pending_promise_events_;
deno_recv_cb recv_cb_;
int32_t next_req_id_;
void* user_data_;
v8::Persistent<v8::Context> context_;
std::map<int32_t, v8::Persistent<v8::Value>> async_data_map_;
+ std::map<int, v8::Persistent<v8::Value>> pending_promise_map_;
std::string last_exception_;
v8::Persistent<v8::Function> recv_;
v8::Persistent<v8::Function> global_error_handler_;
@@ -91,26 +90,16 @@ void Recv(const v8::FunctionCallbackInfo<v8::Value>& args);
void Send(const v8::FunctionCallbackInfo<v8::Value>& args);
void Shared(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info);
-void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
-void SetPromiseRejectHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
-void SetPromiseErrorExaminer(const v8::FunctionCallbackInfo<v8::Value>& args);
static intptr_t external_references[] = {
- reinterpret_cast<intptr_t>(Print),
- reinterpret_cast<intptr_t>(Recv),
- reinterpret_cast<intptr_t>(Send),
- reinterpret_cast<intptr_t>(Shared),
- reinterpret_cast<intptr_t>(SetGlobalErrorHandler),
- reinterpret_cast<intptr_t>(SetPromiseRejectHandler),
- reinterpret_cast<intptr_t>(SetPromiseErrorExaminer),
- 0};
+ reinterpret_cast<intptr_t>(Print), reinterpret_cast<intptr_t>(Recv),
+ reinterpret_cast<intptr_t>(Send), reinterpret_cast<intptr_t>(Shared), 0};
static const deno_buf empty_buf = {nullptr, 0, nullptr, 0};
Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb);
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);
void HandleException(v8::Local<v8::Context> context,
v8::Local<v8::Value> exception);
diff --git a/libdeno/libdeno_test.cc b/libdeno/libdeno_test.cc
index f7173210a..33a4702ae 100644
--- a/libdeno/libdeno_test.cc
+++ b/libdeno/libdeno_test.cc
@@ -15,14 +15,14 @@ TEST(LibDenoTest, InitializesCorrectlyWithoutSnapshot) {
}
TEST(LibDenoTest, SnapshotterInitializesCorrectly) {
- Deno* d = deno_new_snapshotter(deno_config{empty, nullptr}, "a.js",
- "a = 1 + 2", nullptr);
+ Deno* d =
+ deno_new_snapshotter(deno_config{empty, nullptr}, "a.js", "a = 1 + 2");
deno_delete(d);
}
TEST(LibDenoTest, Snapshotter) {
- Deno* d1 = deno_new_snapshotter(deno_config{empty, nullptr}, "a.js",
- "a = 1 + 2", nullptr);
+ Deno* d1 =
+ deno_new_snapshotter(deno_config{empty, nullptr}, "a.js", "a = 1 + 2");
deno_buf test_snapshot = deno_get_snapshot(d1);
deno_delete(d1);
@@ -182,23 +182,18 @@ TEST(LibDenoTest, SnapshotBug) {
}
TEST(LibDenoTest, GlobalErrorHandling) {
- static int count = 0;
- auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) {
- assert_null(data_buf);
- count++;
- EXPECT_EQ(static_cast<size_t>(1), buf.data_len);
- EXPECT_EQ(buf.data_ptr[0], 42);
- };
- Deno* d = deno_new(snapshot, deno_config{empty, recv_cb});
- EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "GlobalErrorHandling()"));
- EXPECT_EQ(count, 1);
- deno_delete(d);
-}
-
-TEST(LibDenoTest, DoubleGlobalErrorHandlingFails) {
Deno* d = deno_new(snapshot, deno_config{empty, nullptr});
- EXPECT_FALSE(
- deno_execute(d, nullptr, "a.js", "DoubleGlobalErrorHandlingFails()"));
+ EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "GlobalErrorHandling()"));
+ // We only check that it starts with this string, so we don't have to check
+ // the second frame, which contains line numbers in libdeno_test.js and may
+ // change over time.
+ std::string expected =
+ "{\"message\":\"ReferenceError: notdefined is not defined\","
+ "\"frames\":[{\"line\":3,\"column\":2,\"functionName\":\"\","
+ "\"scriptName\":\"helloworld.js\",\"isEval\":true,"
+ "\"isConstructor\":false,\"isWasm\":false},";
+ std::string actual(deno_last_exception(d), 0, expected.length());
+ EXPECT_STREQ(expected.c_str(), actual.c_str());
deno_delete(d);
}
@@ -225,17 +220,31 @@ TEST(LibDenoTest, DataBuf) {
deno_delete(d);
}
-TEST(LibDenoTest, PromiseRejectCatchHandling) {
+TEST(LibDenoTest, CheckPromiseErrors) {
static int count = 0;
- auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) {
- // If no error, nothing should be sent, and count should
- // not increment
- count++;
- };
+ auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) { count++; };
Deno* d = deno_new(snapshot, deno_config{empty, recv_cb});
- EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "PromiseRejectCatchHandling()"));
+ EXPECT_EQ(deno_last_exception(d), nullptr);
+ EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "CheckPromiseErrors()"));
+ EXPECT_EQ(deno_last_exception(d), nullptr);
+ EXPECT_EQ(count, 1);
+ // We caught the exception. So still no errors after calling
+ // deno_check_promise_errors().
+ deno_check_promise_errors(d);
+ EXPECT_EQ(deno_last_exception(d), nullptr);
+ deno_delete(d);
+}
- EXPECT_EQ(count, 0);
+TEST(LibDenoTest, LastException) {
+ Deno* d = deno_new(empty, deno_config{empty, nullptr});
+ EXPECT_EQ(deno_last_exception(d), nullptr);
+ EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "\n\nthrow Error('boo');\n\n"));
+ EXPECT_STREQ(deno_last_exception(d),
+ "{\"message\":\"Error: boo\","
+ "\"frames\":[{\"line\":3,\"column\":7,"
+ "\"functionName\":\"\",\"scriptName\":\"a.js\","
+ "\"isEval\":false,"
+ "\"isConstructor\":false,\"isWasm\":false}]}");
deno_delete(d);
}
diff --git a/libdeno/libdeno_test.js b/libdeno/libdeno_test.js
index c9eaaa0ca..bb36b02d9 100644
--- a/libdeno/libdeno_test.js
+++ b/libdeno/libdeno_test.js
@@ -99,23 +99,9 @@ global.SnapshotBug = () => {
};
global.GlobalErrorHandling = () => {
- libdeno.setGlobalErrorHandler((message, source, line, col, error) => {
- libdeno.print(`line ${line} col ${col}`, true);
- assert("ReferenceError: notdefined is not defined" === message);
- assert(source === "helloworld.js");
- assert(line === 3);
- assert(col === 1);
- assert(error instanceof Error);
- libdeno.send(new Uint8Array([42]));
- });
eval("\n\n notdefined()\n//# sourceURL=helloworld.js");
};
-global.DoubleGlobalErrorHandlingFails = () => {
- libdeno.setGlobalErrorHandler((message, source, line, col, error) => {});
- libdeno.setGlobalErrorHandler((message, source, line, col, error) => {});
-};
-
// Allocate this buf at the top level to avoid GC.
const dataBuf = new Uint8Array([3, 4]);
@@ -134,33 +120,7 @@ global.DataBuf = () => {
b[1] = 8;
};
-global.PromiseRejectCatchHandling = () => {
- let count = 0;
- let promiseRef = null;
- // When we have an error, libdeno sends something
- function assertOrSend(cond) {
- if (!cond) {
- libdeno.send(new Uint8Array([42]));
- }
- }
- libdeno.setPromiseErrorExaminer(() => {
- assertOrSend(count === 2);
- });
- libdeno.setPromiseRejectHandler((error, event, promise) => {
- count++;
- if (event === "RejectWithNoHandler") {
- assertOrSend(error instanceof Error);
- assertOrSend(error.message === "message");
- assertOrSend(count === 1);
- promiseRef = promise;
- } else if (event === "HandlerAddedAfterReject") {
- assertOrSend(count === 2);
- assertOrSend(promiseRef === promise);
- }
- // Should never reach 3!
- assertOrSend(count !== 3);
- });
-
+global.CheckPromiseErrors = () => {
async function fn() {
throw new Error("message");
}
@@ -169,10 +129,10 @@ global.PromiseRejectCatchHandling = () => {
try {
await fn();
} catch (e) {
- assertOrSend(count === 2);
+ libdeno.send(new Uint8Array([42]));
}
})();
-}
+};
global.Shared = () => {
const ab = libdeno.shared;
@@ -185,4 +145,4 @@ global.Shared = () => {
ui8[0] = 42;
ui8[1] = 43;
ui8[2] = 44;
-}
+};
diff --git a/libdeno/snapshot_creator.cc b/libdeno/snapshot_creator.cc
index cfdd35804..fe9011c9d 100644
--- a/libdeno/snapshot_creator.cc
+++ b/libdeno/snapshot_creator.cc
@@ -30,9 +30,7 @@ int main(int argc, char** argv) {
deno_init();
deno_config config = {deno::empty_buf, nullptr};
- Deno* d = deno_new_snapshotter(
- config, js_fn, js_source.c_str(),
- source_map_fn != nullptr ? source_map.c_str() : nullptr);
+ Deno* d = deno_new_snapshotter(config, js_fn, js_source.c_str());
auto snapshot = deno_get_snapshot(d);