summaryrefslogtreecommitdiff
path: root/libdeno
diff options
context:
space:
mode:
Diffstat (limited to 'libdeno')
-rw-r--r--libdeno/api.cc1
-rw-r--r--libdeno/binding.cc96
-rw-r--r--libdeno/exceptions.cc28
-rw-r--r--libdeno/exceptions.h7
-rw-r--r--libdeno/internal.h7
-rw-r--r--libdeno/libdeno_test.cc14
-rw-r--r--libdeno/libdeno_test.js47
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");
+};