diff options
author | Yoshiya Hinosawa <stibium121@gmail.com> | 2018-08-19 16:44:10 +0900 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2018-08-19 11:27:47 -0400 |
commit | aaabc853e84a03f72208c0e80634bd986c3ca4e2 (patch) | |
tree | 24cb4daf07c7b52b9260e041289ab2e79f25f5a8 /libdeno/binding.cc | |
parent | 146bc93b81ef1775a3a22784c74cbf0c8bbd1c9e (diff) |
chore: move libdeno files to //libdeno/
Diffstat (limited to 'libdeno/binding.cc')
-rw-r--r-- | libdeno/binding.cc | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/libdeno/binding.cc b/libdeno/binding.cc new file mode 100644 index 000000000..0a107c95f --- /dev/null +++ b/libdeno/binding.cc @@ -0,0 +1,415 @@ +// Copyright 2018 the Deno authors. All rights reserved. MIT license. +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <string> + +#include "third_party/v8/include/libplatform/libplatform.h" +#include "third_party/v8/include/v8.h" +#include "third_party/v8/src/base/logging.h" + +#include "deno.h" +#include "internal.h" + +namespace deno { + +static bool skip_onerror = false; + +Deno* FromIsolate(v8::Isolate* isolate) { + return static_cast<Deno*>(isolate->GetData(0)); +} + +// Extracts a C string from a v8::V8 Utf8Value. +const char* ToCString(const v8::String::Utf8Value& value) { + return *value ? *value : "<string conversion failed>"; +} + +static inline v8::Local<v8::String> v8_str(const char* x) { + return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x, + v8::NewStringType::kNormal) + .ToLocalChecked(); +} + +void HandleExceptionStr(v8::Local<v8::Context> context, + v8::Local<v8::Value> exception, + std::string* exception_str) { + auto* isolate = context->GetIsolate(); + v8::HandleScope handle_scope(isolate); + v8::Context::Scope context_scope(context); + + auto message = v8::Exception::CreateMessage(isolate, exception); + auto onerrorStr = v8::String::NewFromUtf8(isolate, "onerror"); + auto onerror = context->Global()->Get(onerrorStr); + 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()); + + if (skip_onerror == false) { + if (onerror->IsFunction()) { + // window.onerror is set so we try to handle the exception in javascript. + auto func = v8::Local<v8::Function>::Cast(onerror); + v8::Local<v8::Value> args[5]; + args[0] = exception->ToString(); + args[1] = message->GetScriptResourceName(); + args[2] = line; + args[3] = column; + args[4] = exception; + func->Call(context->Global(), 5, args); + /* message, source, lineno, colno, error */ + } + } + + char buf[12 * 1024]; + if (!stack_trace.IsEmpty()) { + // No javascript onerror 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) { + auto frame = stack_trace->GetFrame(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; + } + *exception_str += msg; + } else { + // No javascript onerror 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; + } +} + +void HandleException(v8::Local<v8::Context> context, + v8::Local<v8::Value> exception) { + v8::Isolate* isolate = context->GetIsolate(); + Deno* d = FromIsolate(isolate); + std::string exception_str; + HandleExceptionStr(context, exception, &exception_str); + if (d != nullptr) { + d->last_exception = exception_str; + } else { + printf("Pre-Deno Exception %s\n", exception_str.c_str()); + exit(1); + } +} + +/* +bool AbortOnUncaughtExceptionCallback(v8::Isolate* isolate) { + return true; +} + +void MessageCallback2(Local<Message> message, v8::Local<v8::Value> data) { + printf("MessageCallback2\n\n"); +} + +void FatalErrorCallback2(const char* location, const char* message) { + printf("FatalErrorCallback2\n"); +} +*/ + +void ExitOnPromiseRejectCallback( + v8::PromiseRejectMessage promise_reject_message) { + auto* isolate = v8::Isolate::GetCurrent(); + Deno* d = static_cast<Deno*>(isolate->GetData(0)); + DCHECK_EQ(d->isolate, isolate); + v8::HandleScope handle_scope(d->isolate); + auto exception = promise_reject_message.GetValue(); + auto context = d->context.Get(d->isolate); + HandleException(context, exception); +} + +void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(args.Length(), 1); + auto* isolate = args.GetIsolate(); + v8::HandleScope handle_scope(isolate); + v8::String::Utf8Value str(isolate, args[0]); + const char* cstr = ToCString(str); + printf("%s\n", cstr); + fflush(stdout); +} + +static v8::Local<v8::Uint8Array> ImportBuf(v8::Isolate* isolate, deno_buf buf) { + if (buf.alloc_ptr == nullptr) { + // If alloc_ptr isn't set, we memcpy. + // This is currently used for flatbuffers created in Rust. + auto ab = v8::ArrayBuffer::New(isolate, buf.data_len); + memcpy(ab->GetContents().Data(), buf.data_ptr, buf.data_len); + auto view = v8::Uint8Array::New(ab, 0, buf.data_len); + return view; + } else { + auto ab = v8::ArrayBuffer::New( + isolate, reinterpret_cast<void*>(buf.alloc_ptr), buf.alloc_len, + v8::ArrayBufferCreationMode::kInternalized); + auto view = + v8::Uint8Array::New(ab, buf.data_ptr - buf.alloc_ptr, buf.data_len); + return view; + } +} + +static deno_buf ExportBuf(v8::Isolate* isolate, + v8::Local<v8::ArrayBufferView> view) { + auto ab = view->Buffer(); + auto contents = ab->Externalize(); + + deno_buf buf; + buf.alloc_ptr = reinterpret_cast<uint8_t*>(contents.Data()); + buf.alloc_len = contents.ByteLength(); + buf.data_ptr = buf.alloc_ptr + view->ByteOffset(); + buf.data_len = view->ByteLength(); + + // Prevent JS from modifying buffer contents after exporting. + ab->Neuter(); + + return buf; +} + +static void FreeBuf(deno_buf buf) { free(buf.alloc_ptr); } + +// Sets the recv callback. +void Recv(const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Isolate* isolate = args.GetIsolate(); + Deno* d = reinterpret_cast<Deno*>(isolate->GetData(0)); + DCHECK_EQ(d->isolate, isolate); + + v8::HandleScope handle_scope(isolate); + + if (!d->recv.IsEmpty()) { + isolate->ThrowException(v8_str("deno.recv 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->recv.Reset(isolate, func); +} + +void Send(const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Isolate* isolate = args.GetIsolate(); + Deno* d = static_cast<Deno*>(isolate->GetData(0)); + DCHECK_EQ(d->isolate, isolate); + + v8::Locker locker(d->isolate); + v8::EscapableHandleScope handle_scope(isolate); + + CHECK_EQ(args.Length(), 1); + v8::Local<v8::Value> ab_v = args[0]; + CHECK(ab_v->IsArrayBufferView()); + auto buf = ExportBuf(isolate, v8::Local<v8::ArrayBufferView>::Cast(ab_v)); + + DCHECK_EQ(d->currentArgs, nullptr); + d->currentArgs = &args; + + d->cb(d, buf); + + // Buffer is only valid until the end of the callback. + // TODO(piscisaureus): + // It's possible that data in the buffer is needed after the callback + // returns, e.g. when the handler offloads work to a thread pool, therefore + // make the callback responsible for releasing the buffer. + FreeBuf(buf); + + d->currentArgs = nullptr; +} + +bool ExecuteV8StringSource(v8::Local<v8::Context> context, + const char* js_filename, + v8::Local<v8::String> source) { + auto* isolate = context->GetIsolate(); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + + v8::Context::Scope context_scope(context); + + v8::TryCatch try_catch(isolate); + + auto name = v8_str(js_filename); + + v8::ScriptOrigin origin(name); + + auto script = v8::Script::Compile(context, source, &origin); + + if (script.IsEmpty()) { + DCHECK(try_catch.HasCaught()); + HandleException(context, try_catch.Exception()); + return false; + } + + auto result = script.ToLocalChecked()->Run(context); + + if (result.IsEmpty()) { + DCHECK(try_catch.HasCaught()); + HandleException(context, try_catch.Exception()); + return false; + } + + return true; +} + +bool Execute(v8::Local<v8::Context> context, const char* js_filename, + const char* js_source) { + auto* isolate = context->GetIsolate(); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + auto source = v8_str(js_source); + return ExecuteV8StringSource(context, js_filename, source); +} + +void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context, + const char* js_filename, const std::string& js_source, + const std::string* source_map) { + v8::HandleScope handle_scope(isolate); + v8::Context::Scope context_scope(context); + + auto global = context->Global(); + + auto deno_val = v8::Object::New(isolate); + CHECK(global->Set(context, deno::v8_str("libdeno"), deno_val).FromJust()); + + auto print_tmpl = v8::FunctionTemplate::New(isolate, Print); + auto print_val = print_tmpl->GetFunction(context).ToLocalChecked(); + CHECK(deno_val->Set(context, deno::v8_str("print"), print_val).FromJust()); + + auto recv_tmpl = v8::FunctionTemplate::New(isolate, Recv); + auto recv_val = recv_tmpl->GetFunction(context).ToLocalChecked(); + CHECK(deno_val->Set(context, deno::v8_str("recv"), recv_val).FromJust()); + + auto send_tmpl = v8::FunctionTemplate::New(isolate, Send); + auto send_val = send_tmpl->GetFunction(context).ToLocalChecked(); + CHECK(deno_val->Set(context, deno::v8_str("send"), send_val).FromJust()); + + skip_onerror = true; + { + auto source = deno::v8_str(js_source.c_str()); + CHECK( + deno_val->Set(context, deno::v8_str("mainSource"), source).FromJust()); + + bool r = deno::ExecuteV8StringSource(context, js_filename, source); + CHECK(r); + + if (source_map != nullptr) { + CHECK_GT(source_map->length(), 1u); + v8::TryCatch try_catch(isolate); + v8::ScriptOrigin origin(v8_str("set_source_map.js")); + std::string source_map_parens = "(" + *source_map + ")"; + 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()); + } + } + skip_onerror = false; +} + +void AddIsolate(Deno* d, v8::Isolate* isolate) { + d->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); + d->isolate->SetPromiseRejectCallback(deno::ExitOnPromiseRejectCallback); + d->isolate->SetData(0, d); +} + +} // namespace deno + +extern "C" { + +void deno_init() { + // v8::V8::InitializeICUDefaultLocation(argv[0]); + // v8::V8::InitializeExternalStartupData(argv[0]); + auto* p = v8::platform::CreateDefaultPlatform(); + v8::V8::InitializePlatform(p); + v8::V8::Initialize(); +} + +void* deno_get_data(Deno* d) { return d->data; } + +const char* deno_v8_version() { return v8::V8::GetVersion(); } + +void deno_set_flags(int* argc, char** argv) { + v8::V8::SetFlagsFromCommandLine(argc, argv, true); +} + +const char* deno_last_exception(Deno* d) { return d->last_exception.c_str(); } + +int deno_execute(Deno* d, const char* js_filename, const char* js_source) { + 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); + return deno::Execute(context, js_filename, js_source) ? 1 : 0; +} + +int deno_send(Deno* d, deno_buf buf) { + v8::Locker locker(d->isolate); + v8::Isolate::Scope isolate_scope(d->isolate); + v8::HandleScope handle_scope(d->isolate); + + auto context = d->context.Get(d->isolate); + v8::Context::Scope context_scope(context); + + v8::TryCatch try_catch(d->isolate); + + auto recv = d->recv.Get(d->isolate); + if (recv.IsEmpty()) { + d->last_exception = "deno.recv has not been called."; + return 0; + } + + v8::Local<v8::Value> args[1]; + args[0] = deno::ImportBuf(d->isolate, buf); + recv->Call(context->Global(), 1, args); + + if (try_catch.HasCaught()) { + deno::HandleException(context, try_catch.Exception()); + return 0; + } + + return 1; +} + +void deno_set_response(Deno* d, deno_buf buf) { + auto ab = deno::ImportBuf(d->isolate, buf); + d->currentArgs->GetReturnValue().Set(ab); +} + +void deno_delete(Deno* d) { + d->isolate->Dispose(); + delete d; +} + +void deno_terminate_execution(Deno* d) { d->isolate->TerminateExecution(); } + +} // extern "C" |