diff options
Diffstat (limited to 'libdeno')
-rw-r--r-- | libdeno/api.cc | 1 | ||||
-rw-r--r-- | libdeno/binding.cc | 96 | ||||
-rw-r--r-- | libdeno/exceptions.cc | 28 | ||||
-rw-r--r-- | libdeno/exceptions.h | 7 | ||||
-rw-r--r-- | libdeno/internal.h | 7 | ||||
-rw-r--r-- | libdeno/libdeno_test.cc | 14 | ||||
-rw-r--r-- | libdeno/libdeno_test.js | 47 |
7 files changed, 193 insertions, 7 deletions
diff --git a/libdeno/api.cc b/libdeno/api.cc index e540c95fc..21ece13a6 100644 --- a/libdeno/api.cc +++ b/libdeno/api.cc @@ -9,6 +9,7 @@ #include "third_party/v8/src/base/logging.h" #include "deno.h" +#include "exceptions.h" #include "internal.h" extern "C" { diff --git a/libdeno/binding.cc b/libdeno/binding.cc index 78e4cad29..f7ee977e8 100644 --- a/libdeno/binding.cc +++ b/libdeno/binding.cc @@ -120,6 +120,16 @@ void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { fflush(file); } +void ErrorToJSON(const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(args.Length(), 1); + auto* isolate = args.GetIsolate(); + DenoIsolate* d = DenoIsolate::FromIsolate(isolate); + auto context = d->context_.Get(d->isolate_); + v8::HandleScope handle_scope(isolate); + auto json_string = EncodeExceptionAsJSON(context, args[0]); + args.GetReturnValue().Set(v8_str(json_string.c_str())); +} + v8::Local<v8::Uint8Array> ImportBuf(DenoIsolate* d, deno_buf buf) { if (buf.alloc_ptr == nullptr) { // If alloc_ptr isn't set, we memcpy. @@ -368,6 +378,80 @@ bool Execute(v8::Local<v8::Context> context, const char* js_filename, return true; } +static inline v8::Local<v8::Boolean> v8_bool(bool v) { + return v8::Boolean::New(v8::Isolate::GetCurrent(), v); +} + +void EvalContext(const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Isolate* isolate = args.GetIsolate(); + DenoIsolate* d = DenoIsolate::FromIsolate(isolate); + v8::EscapableHandleScope handleScope(isolate); + auto context = d->context_.Get(isolate); + v8::Context::Scope context_scope(context); + + CHECK(args[0]->IsString()); + auto source = args[0].As<v8::String>(); + + auto output = v8::Array::New(isolate, 2); + /** + * output[0] = result + * output[1] = ErrorInfo | null + * ErrorInfo = { + * thrown: Error | any, + * isNativeError: boolean, + * isCompileError: boolean, + * } + */ + + v8::TryCatch try_catch(isolate); + + auto name = v8_str("<unknown>"); + v8::ScriptOrigin origin(name); + auto script = v8::Script::Compile(context, source, &origin); + + if (script.IsEmpty()) { + DCHECK(try_catch.HasCaught()); + auto exception = try_catch.Exception(); + + output->Set(0, v8::Null(isolate)); + + auto errinfo_obj = v8::Object::New(isolate); + errinfo_obj->Set(v8_str("isCompileError"), v8_bool(true)); + errinfo_obj->Set(v8_str("isNativeError"), + v8_bool(exception->IsNativeError())); + errinfo_obj->Set(v8_str("thrown"), exception); + + output->Set(1, errinfo_obj); + + args.GetReturnValue().Set(output); + return; + } + + auto result = script.ToLocalChecked()->Run(context); + + if (result.IsEmpty()) { + DCHECK(try_catch.HasCaught()); + auto exception = try_catch.Exception(); + + output->Set(0, v8::Null(isolate)); + + auto errinfo_obj = v8::Object::New(isolate); + errinfo_obj->Set(v8_str("isCompileError"), v8_bool(false)); + errinfo_obj->Set(v8_str("isNativeError"), + v8_bool(exception->IsNativeError())); + errinfo_obj->Set(v8_str("thrown"), exception); + + output->Set(1, errinfo_obj); + + args.GetReturnValue().Set(output); + return; + } + + output->Set(0, result.ToLocalChecked()); + output->Set(1, v8::Null(isolate)); + args.GetReturnValue().Set(output); +} + void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) { v8::HandleScope handle_scope(isolate); v8::Context::Scope context_scope(context); @@ -389,6 +473,18 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) { auto send_val = send_tmpl->GetFunction(context).ToLocalChecked(); CHECK(deno_val->Set(context, deno::v8_str("send"), send_val).FromJust()); + auto eval_context_tmpl = v8::FunctionTemplate::New(isolate, EvalContext); + auto eval_context_val = + eval_context_tmpl->GetFunction(context).ToLocalChecked(); + CHECK(deno_val->Set(context, deno::v8_str("evalContext"), eval_context_val) + .FromJust()); + + auto error_to_json_tmpl = v8::FunctionTemplate::New(isolate, ErrorToJSON); + auto error_to_json_val = + error_to_json_tmpl->GetFunction(context).ToLocalChecked(); + CHECK(deno_val->Set(context, deno::v8_str("errorToJSON"), error_to_json_val) + .FromJust()); + CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared) .FromJust()); diff --git a/libdeno/exceptions.cc b/libdeno/exceptions.cc index 0d7bbed8b..51f81bfce 100644 --- a/libdeno/exceptions.cc +++ b/libdeno/exceptions.cc @@ -4,10 +4,10 @@ namespace deno { -std::string EncodeMessageAsJSON(v8::Local<v8::Context> context, - v8::Local<v8::Message> message) { +v8::Local<v8::Object> EncodeMessageAsObject(v8::Local<v8::Context> context, + v8::Local<v8::Message> message) { auto* isolate = context->GetIsolate(); - v8::HandleScope handle_scope(isolate); + v8::EscapableHandleScope handle_scope(isolate); v8::Context::Scope context_scope(context); auto stack_trace = message->GetStackTrace(); @@ -134,12 +134,33 @@ std::string EncodeMessageAsJSON(v8::Local<v8::Context> context, } CHECK(json_obj->Set(context, v8_str("frames"), frames).FromJust()); + json_obj = handle_scope.Escape(json_obj); + return json_obj; +} +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); auto json_string = v8::JSON::Stringify(context, json_obj).ToLocalChecked(); v8::String::Utf8Value json_string_(isolate, json_string); return std::string(ToCString(json_string_)); } +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; +} + std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context, v8::Local<v8::Value> exception) { auto* isolate = context->GetIsolate(); @@ -167,5 +188,4 @@ void HandleExceptionMessage(v8::Local<v8::Context> context, CHECK_NOT_NULL(d); d->last_exception_ = json_str; } - } // namespace deno diff --git a/libdeno/exceptions.h b/libdeno/exceptions.h index 362bbc0e6..e07ff183a 100644 --- a/libdeno/exceptions.h +++ b/libdeno/exceptions.h @@ -2,10 +2,17 @@ #ifndef EXCEPTIONS_H_ #define EXCEPTIONS_H_ +#include <string> #include "third_party/v8/include/v8.h" namespace deno { +v8::Local<v8::Object> EncodeExceptionAsObject(v8::Local<v8::Context> context, + v8::Local<v8::Value> exception); + +std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context, + v8::Local<v8::Value> exception); + void HandleException(v8::Local<v8::Context> context, v8::Local<v8::Value> exception); diff --git a/libdeno/internal.h b/libdeno/internal.h index 0cd50162c..a87ec0fdc 100644 --- a/libdeno/internal.h +++ b/libdeno/internal.h @@ -133,6 +133,8 @@ static inline v8::Local<v8::String> v8_str(const char* x) { void Print(const v8::FunctionCallbackInfo<v8::Value>& args); void Recv(const v8::FunctionCallbackInfo<v8::Value>& args); void Send(const v8::FunctionCallbackInfo<v8::Value>& args); +void EvalContext(const v8::FunctionCallbackInfo<v8::Value>& args); +void ErrorToJSON(const v8::FunctionCallbackInfo<v8::Value>& args); void Shared(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info); void BuiltinModules(v8::Local<v8::Name> property, @@ -142,6 +144,8 @@ static intptr_t external_references[] = { reinterpret_cast<intptr_t>(Print), reinterpret_cast<intptr_t>(Recv), reinterpret_cast<intptr_t>(Send), + reinterpret_cast<intptr_t>(EvalContext), + reinterpret_cast<intptr_t>(ErrorToJSON), reinterpret_cast<intptr_t>(Shared), reinterpret_cast<intptr_t>(BuiltinModules), reinterpret_cast<intptr_t>(MessageCallback), @@ -153,9 +157,6 @@ Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb); void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context); -void HandleException(v8::Local<v8::Context> context, - v8::Local<v8::Value> exception); - void DeserializeInternalFields(v8::Local<v8::Object> holder, int index, v8::StartupData payload, void* data); diff --git a/libdeno/libdeno_test.cc b/libdeno/libdeno_test.cc index 0936c53b4..3193e7677 100644 --- a/libdeno/libdeno_test.cc +++ b/libdeno/libdeno_test.cc @@ -290,6 +290,20 @@ TEST(LibDenoTest, Utf8Bug) { deno_delete(d); } +TEST(LibDenoTest, LibDenoEvalContext) { + Deno* d = deno_new(deno_config{0, snapshot, empty, nullptr}); + deno_execute(d, nullptr, "a.js", "LibDenoEvalContext();"); + EXPECT_EQ(nullptr, deno_last_exception(d)); + deno_delete(d); +} + +TEST(LibDenoTest, LibDenoEvalContextError) { + Deno* d = deno_new(deno_config{0, snapshot, empty, nullptr}); + deno_execute(d, nullptr, "a.js", "LibDenoEvalContextError();"); + EXPECT_EQ(nullptr, deno_last_exception(d)); + deno_delete(d); +} + TEST(LibDenoTest, SharedAtomics) { int32_t s[] = {0, 1, 2}; deno_buf shared = {nullptr, 0, reinterpret_cast<uint8_t*>(s), sizeof s}; diff --git a/libdeno/libdeno_test.js b/libdeno/libdeno_test.js index 50f5c03d4..d6ea5f983 100644 --- a/libdeno/libdeno_test.js +++ b/libdeno/libdeno_test.js @@ -147,3 +147,50 @@ global.Shared = () => { ui8[1] = 43; ui8[2] = 44; }; + +global.LibDenoEvalContext = () => { + const [result, errInfo] = libdeno.evalContext("let a = 1; a"); + assert(result === 1); + assert(!errInfo); + const [result2, errInfo2] = libdeno.evalContext("a = a + 1; a"); + assert(result2 === 2); + assert(!errInfo2); +}; + +global.LibDenoEvalContextError = () => { + const [result, errInfo] = libdeno.evalContext("not_a_variable"); + assert(!result); + assert(!!errInfo); + assert(errInfo.isNativeError); // is a native error (ReferenceError) + assert(!errInfo.isCompileError); // is NOT a compilation error + assert(errInfo.thrown.message === "not_a_variable is not defined"); + + const [result2, errInfo2] = libdeno.evalContext("throw 1"); + assert(!result2); + assert(!!errInfo2); + assert(!errInfo2.isNativeError); // is NOT a native error + assert(!errInfo2.isCompileError); // is NOT a compilation error + assert(errInfo2.thrown === 1); + + const [result3, errInfo3] = + libdeno.evalContext("class AError extends Error {}; throw new AError('e')"); + assert(!result3); + assert(!!errInfo3); + assert(errInfo3.isNativeError); // extend from native error, still native error + assert(!errInfo3.isCompileError); // is NOT a compilation error + assert(errInfo3.thrown.message === "e"); + + const [result4, errInfo4] = libdeno.evalContext("{"); + assert(!result4); + assert(!!errInfo4); + assert(errInfo4.isNativeError); // is a native error (SyntaxError) + assert(errInfo4.isCompileError); // is a compilation error! (braces not closed) + assert(errInfo4.thrown.message === "Unexpected end of input"); + + const [result5, errInfo5] = libdeno.evalContext("eval('{')"); + assert(!result5); + assert(!!errInfo5); + assert(errInfo5.isNativeError); // is a native error (SyntaxError) + assert(!errInfo5.isCompileError); // is NOT a compilation error! (just eval) + assert(errInfo5.thrown.message === "Unexpected end of input"); +}; |