2018-07-23 14:46:30 -04:00
|
|
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
2018-06-09 18:32:04 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2018-09-28 15:55:06 -04:00
|
|
|
#include <iostream>
|
2018-06-09 18:32:04 -04:00
|
|
|
#include <string>
|
|
|
|
|
2018-07-03 04:15:32 -04:00
|
|
|
#include "third_party/v8/include/v8.h"
|
|
|
|
#include "third_party/v8/src/base/logging.h"
|
2018-06-09 18:32:04 -04:00
|
|
|
|
2018-07-06 03:19:19 -04:00
|
|
|
#include "deno.h"
|
2018-07-06 15:00:45 -04:00
|
|
|
#include "internal.h"
|
2018-06-09 18:32:04 -04:00
|
|
|
|
2018-10-17 14:02:00 -04:00
|
|
|
#define GLOBAL_IMPORT_BUF_SIZE 1024
|
|
|
|
|
2018-06-09 20:24:34 -04:00
|
|
|
namespace deno {
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
std::vector<InternalFieldData*> deserialized_data;
|
|
|
|
|
|
|
|
void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
|
|
|
|
v8::StartupData payload, void* data) {
|
|
|
|
DCHECK_EQ(data, nullptr);
|
|
|
|
if (payload.raw_size == 0) {
|
|
|
|
holder->SetAlignedPointerInInternalField(index, nullptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
InternalFieldData* embedder_field = new InternalFieldData{0};
|
|
|
|
memcpy(embedder_field, payload.data, payload.raw_size);
|
|
|
|
holder->SetAlignedPointerInInternalField(index, embedder_field);
|
|
|
|
deserialized_data.push_back(embedder_field);
|
2018-07-30 15:12:33 -04:00
|
|
|
}
|
|
|
|
|
2018-10-24 01:12:13 -04:00
|
|
|
v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder, int index,
|
|
|
|
void* data) {
|
|
|
|
DCHECK_EQ(data, nullptr);
|
|
|
|
InternalFieldData* embedder_field = static_cast<InternalFieldData*>(
|
|
|
|
holder->GetAlignedPointerFromInternalField(index));
|
|
|
|
if (embedder_field == nullptr) return {nullptr, 0};
|
|
|
|
int size = sizeof(*embedder_field);
|
|
|
|
char* payload = new char[size];
|
|
|
|
// We simply use memcpy to serialize the content.
|
|
|
|
memcpy(payload, embedder_field, size);
|
|
|
|
return {payload, size};
|
|
|
|
}
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* FromIsolate(v8::Isolate* isolate) {
|
|
|
|
return static_cast<DenoIsolate*>(isolate->GetData(0));
|
2018-09-27 17:33:10 -04:00
|
|
|
}
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
void AddDataRef(DenoIsolate* d, int32_t req_id, v8::Local<v8::Value> data_v) {
|
|
|
|
d->async_data_map_.emplace(std::piecewise_construct, std::make_tuple(req_id),
|
|
|
|
std::make_tuple(d->isolate_, data_v));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeleteDataRef(DenoIsolate* d, int32_t req_id) {
|
2018-09-27 17:33:10 -04:00
|
|
|
// Delete persistent reference to data ArrayBuffer.
|
2018-10-23 23:58:20 -04:00
|
|
|
auto it = d->async_data_map_.find(req_id);
|
|
|
|
if (it != d->async_data_map_.end()) {
|
2018-10-18 18:20:23 -04:00
|
|
|
it->second.Reset();
|
2018-10-23 23:58:20 -04:00
|
|
|
d->async_data_map_.erase(it);
|
2018-10-18 11:44:45 -04:00
|
|
|
}
|
2018-09-27 17:33:10 -04:00
|
|
|
}
|
|
|
|
|
2018-06-09 18:32:04 -04:00
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
2018-07-30 15:12:33 -04:00
|
|
|
void HandleExceptionStr(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Value> exception,
|
|
|
|
std::string* exception_str) {
|
2018-06-10 08:24:39 -04:00
|
|
|
auto* isolate = context->GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
2018-08-26 03:57:16 -04:00
|
|
|
|
2018-06-10 00:13:48 -04:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
2018-06-09 18:32:04 -04:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2018-06-10 00:13:48 -04:00
|
|
|
auto message = v8::Exception::CreateMessage(isolate, exception);
|
2018-07-05 17:33:20 -04:00
|
|
|
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());
|
2018-06-09 18:32:04 -04:00
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
auto global_error_handler_ = d->global_error_handler_.Get(isolate);
|
2018-08-26 03:57:16 -04:00
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
if (!global_error_handler_.IsEmpty()) {
|
|
|
|
// global_error_handler_ is set so we try to handle the exception in
|
2018-08-26 13:22:07 -04:00
|
|
|
// javascript.
|
2018-08-26 03:57:16 -04:00
|
|
|
v8::Local<v8::Value> args[5];
|
2018-09-17 18:57:25 -04:00
|
|
|
args[0] = exception->ToString(context).ToLocalChecked();
|
2018-08-26 03:57:16 -04:00
|
|
|
args[1] = message->GetScriptResourceName();
|
|
|
|
args[2] = line;
|
|
|
|
args[3] = column;
|
|
|
|
args[4] = exception;
|
2018-10-23 23:58:20 -04:00
|
|
|
global_error_handler_->Call(context->Global(), 5, args);
|
2018-08-26 03:57:16 -04:00
|
|
|
/* message, source, lineno, colno, error */
|
|
|
|
|
|
|
|
return;
|
2018-07-30 15:12:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
char buf[12 * 1024];
|
|
|
|
if (!stack_trace.IsEmpty()) {
|
2018-08-26 03:57:16 -04:00
|
|
|
// No javascript error handler, but we do have a stack trace. Format it
|
2018-10-23 23:58:20 -04:00
|
|
|
// into a string and add to last_exception_.
|
2018-07-05 17:33:20 -04:00
|
|
|
std::string msg;
|
|
|
|
v8::String::Utf8Value exceptionStr(isolate, exception);
|
|
|
|
msg += ToCString(exceptionStr);
|
|
|
|
msg += "\n";
|
|
|
|
|
|
|
|
for (int i = 0; i < stack_trace->GetFrameCount(); ++i) {
|
2018-09-17 18:57:25 -04:00
|
|
|
auto frame = stack_trace->GetFrame(isolate, i);
|
2018-07-05 17:33:20 -04:00
|
|
|
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;
|
|
|
|
}
|
2018-07-30 15:12:33 -04:00
|
|
|
*exception_str += msg;
|
2018-06-09 18:32:04 -04:00
|
|
|
} else {
|
2018-08-26 03:57:16 -04:00
|
|
|
// No javascript error handler, no stack trace. Format the little info we
|
2018-10-23 23:58:20 -04:00
|
|
|
// have into a string and add to last_exception_.
|
2018-06-10 00:13:48 -04:00
|
|
|
v8::String::Utf8Value exceptionStr(isolate, exception);
|
2018-07-05 17:33:20 -04:00
|
|
|
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));
|
2018-07-30 15:12:33 -04:00
|
|
|
*exception_str += buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HandleException(v8::Local<v8::Context> context,
|
|
|
|
v8::Local<v8::Value> exception) {
|
|
|
|
v8::Isolate* isolate = context->GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
2018-07-30 15:12:33 -04:00
|
|
|
std::string exception_str;
|
|
|
|
HandleExceptionStr(context, exception, &exception_str);
|
|
|
|
if (d != nullptr) {
|
2018-10-23 23:58:20 -04:00
|
|
|
d->last_exception_ = exception_str;
|
2018-07-30 15:12:33 -04:00
|
|
|
} else {
|
2018-09-28 15:55:06 -04:00
|
|
|
std::cerr << "Pre-Deno Exception " << exception_str << std::endl;
|
2018-08-06 18:37:32 -04:00
|
|
|
exit(1);
|
2018-06-09 18:32:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-12 14:22:52 -04:00
|
|
|
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";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PromiseRejectCallback(v8::PromiseRejectMessage promise_reject_message) {
|
2018-06-09 18:32:04 -04:00
|
|
|
auto* isolate = v8::Isolate::GetCurrent();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = static_cast<DenoIsolate*>(isolate->GetData(0));
|
|
|
|
DCHECK_EQ(d->isolate_, isolate);
|
|
|
|
v8::HandleScope handle_scope(d->isolate_);
|
2018-06-09 18:32:04 -04:00
|
|
|
auto exception = promise_reject_message.GetValue();
|
2018-10-23 23:58:20 -04:00
|
|
|
auto context = d->context_.Get(d->isolate_);
|
2018-10-12 14:22:52 -04:00
|
|
|
auto promise = promise_reject_message.GetPromise();
|
|
|
|
auto event = promise_reject_message.GetEvent();
|
|
|
|
|
|
|
|
v8::Context::Scope context_scope(context);
|
2018-10-23 23:58:20 -04:00
|
|
|
auto promise_reject_handler = d->promise_reject_handler_.Get(isolate);
|
2018-10-12 14:22:52 -04:00
|
|
|
|
|
|
|
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) {
|
2018-10-23 23:58:20 -04:00
|
|
|
d->pending_promise_events_++;
|
2018-10-12 14:22:52 -04:00
|
|
|
// exception only valid for kPromiseRejectWithNoHandler
|
|
|
|
args[0] = exception;
|
|
|
|
} else if (event ==
|
|
|
|
v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject) {
|
2018-10-23 23:58:20 -04:00
|
|
|
d->pending_promise_events_--; // unhandled event cancelled
|
|
|
|
if (d->pending_promise_events_ < 0) {
|
|
|
|
d->pending_promise_events_ = 0;
|
2018-10-12 14:22:52 -04:00
|
|
|
}
|
|
|
|
// Placeholder, not actually used
|
|
|
|
args[0] = v8_str("Promise handler added");
|
|
|
|
} else if (event == v8::PromiseRejectEvent::kPromiseResolveAfterResolved) {
|
2018-10-23 23:58:20 -04:00
|
|
|
d->pending_promise_events_++;
|
2018-10-12 14:22:52 -04:00
|
|
|
args[0] = v8_str("Promise resolved after resolved");
|
|
|
|
} else if (event == v8::PromiseRejectEvent::kPromiseRejectAfterResolved) {
|
2018-10-23 23:58:20 -04:00
|
|
|
d->pending_promise_events_++;
|
2018-10-12 14:22:52 -04:00
|
|
|
args[0] = v8_str("Promise rejected after resolved");
|
|
|
|
}
|
|
|
|
promise_reject_handler->Call(context->Global(), 3, args);
|
|
|
|
return;
|
|
|
|
}
|
2018-06-09 18:32:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
2018-09-25 01:27:02 -04:00
|
|
|
CHECK_GE(args.Length(), 1);
|
|
|
|
CHECK_LE(args.Length(), 2);
|
2018-06-09 18:32:04 -04:00
|
|
|
auto* isolate = args.GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
|
|
|
auto context = d->context_.Get(d->isolate_);
|
2018-06-09 20:02:40 -04:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::String::Utf8Value str(isolate, args[0]);
|
2018-09-25 01:27:02 -04:00
|
|
|
bool is_err =
|
|
|
|
args.Length() >= 2 ? args[1]->BooleanValue(context).ToChecked() : false;
|
2018-06-09 20:02:40 -04:00
|
|
|
const char* cstr = ToCString(str);
|
2018-09-28 15:55:06 -04:00
|
|
|
auto& stream = is_err ? std::cerr : std::cout;
|
|
|
|
stream << cstr << std::endl;
|
2018-06-09 18:32:04 -04:00
|
|
|
}
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
v8::Local<v8::Uint8Array> ImportBuf(DenoIsolate* d, deno_buf buf) {
|
2018-07-23 14:11:41 -04:00
|
|
|
if (buf.alloc_ptr == nullptr) {
|
|
|
|
// If alloc_ptr isn't set, we memcpy.
|
|
|
|
// This is currently used for flatbuffers created in Rust.
|
2018-10-17 14:02:00 -04:00
|
|
|
|
|
|
|
// To avoid excessively allocating new ArrayBuffers, we try to reuse a
|
|
|
|
// single global ArrayBuffer. The caveat is that users must extract data
|
|
|
|
// from it before the next tick. We only do this for ArrayBuffers less than
|
|
|
|
// 1024 bytes.
|
|
|
|
v8::Local<v8::ArrayBuffer> ab;
|
|
|
|
void* data;
|
|
|
|
if (buf.data_len > GLOBAL_IMPORT_BUF_SIZE) {
|
|
|
|
// Simple case. We allocate a new ArrayBuffer for this.
|
2018-10-23 23:58:20 -04:00
|
|
|
ab = v8::ArrayBuffer::New(d->isolate_, buf.data_len);
|
2018-10-17 14:02:00 -04:00
|
|
|
data = ab->GetContents().Data();
|
|
|
|
} else {
|
|
|
|
// Fast case. We reuse the global ArrayBuffer.
|
2018-10-23 23:58:20 -04:00
|
|
|
if (d->global_import_buf_.IsEmpty()) {
|
2018-10-17 14:02:00 -04:00
|
|
|
// Lazily initialize it.
|
2018-10-23 23:58:20 -04:00
|
|
|
DCHECK_EQ(d->global_import_buf_ptr_, nullptr);
|
|
|
|
ab = v8::ArrayBuffer::New(d->isolate_, GLOBAL_IMPORT_BUF_SIZE);
|
|
|
|
d->global_import_buf_.Reset(d->isolate_, ab);
|
|
|
|
d->global_import_buf_ptr_ = ab->GetContents().Data();
|
2018-10-17 14:02:00 -04:00
|
|
|
} else {
|
2018-10-23 23:58:20 -04:00
|
|
|
DCHECK(d->global_import_buf_ptr_);
|
|
|
|
ab = d->global_import_buf_.Get(d->isolate_);
|
2018-10-17 14:02:00 -04:00
|
|
|
}
|
2018-10-23 23:58:20 -04:00
|
|
|
data = d->global_import_buf_ptr_;
|
2018-10-17 14:02:00 -04:00
|
|
|
}
|
|
|
|
memcpy(data, buf.data_ptr, buf.data_len);
|
2018-07-23 14:11:41 -04:00
|
|
|
auto view = v8::Uint8Array::New(ab, 0, buf.data_len);
|
|
|
|
return view;
|
|
|
|
} else {
|
|
|
|
auto ab = v8::ArrayBuffer::New(
|
2018-10-23 23:58:20 -04:00
|
|
|
d->isolate_, reinterpret_cast<void*>(buf.alloc_ptr), buf.alloc_len,
|
2018-07-23 14:11:41 -04:00
|
|
|
v8::ArrayBufferCreationMode::kInternalized);
|
|
|
|
auto view =
|
|
|
|
v8::Uint8Array::New(ab, buf.data_ptr - buf.alloc_ptr, buf.data_len);
|
|
|
|
return view;
|
|
|
|
}
|
2018-07-08 21:35:34 -04:00
|
|
|
}
|
|
|
|
|
2018-09-19 11:24:34 -04:00
|
|
|
static deno_buf GetContents(v8::Isolate* isolate,
|
|
|
|
v8::Local<v8::ArrayBufferView> view) {
|
2018-07-08 21:35:34 -04:00
|
|
|
auto ab = view->Buffer();
|
2018-09-19 11:24:34 -04:00
|
|
|
auto contents = ab->GetContents();
|
2018-07-08 21:35:34 -04:00
|
|
|
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();
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
// Sets the recv_ callback.
|
2018-07-01 12:07:12 -04:00
|
|
|
void Recv(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
2018-06-09 18:32:04 -04:00
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
|
|
|
DCHECK_EQ(d->isolate_, isolate);
|
2018-06-09 18:32:04 -04:00
|
|
|
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
if (!d->recv_.IsEmpty()) {
|
|
|
|
isolate->ThrowException(v8_str("libdeno.recv_ already called."));
|
2018-06-11 16:51:11 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-09 18:32:04 -04:00
|
|
|
v8::Local<v8::Value> v = args[0];
|
2018-06-13 18:55:40 -04:00
|
|
|
CHECK(v->IsFunction());
|
2018-06-09 18:32:04 -04:00
|
|
|
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
d->recv_.Reset(isolate, func);
|
2018-06-09 18:32:04 -04:00
|
|
|
}
|
|
|
|
|
2018-07-01 12:07:12 -04:00
|
|
|
void Send(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
2018-06-09 18:32:04 -04:00
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
|
|
|
DCHECK_EQ(d->isolate_, isolate);
|
2018-06-09 18:32:04 -04:00
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
v8::Locker locker(d->isolate_);
|
2018-06-09 18:32:04 -04:00
|
|
|
v8::EscapableHandleScope handle_scope(isolate);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
CHECK_EQ(d->current_args_, nullptr); // libdeno.send re-entry forbidden.
|
|
|
|
int32_t req_id = d->next_req_id_++;
|
2018-09-27 17:33:10 -04:00
|
|
|
|
|
|
|
v8::Local<v8::Value> control_v = args[0];
|
|
|
|
CHECK(control_v->IsArrayBufferView());
|
|
|
|
deno_buf control =
|
|
|
|
GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(control_v));
|
|
|
|
deno_buf data = {nullptr, 0u, nullptr, 0u};
|
|
|
|
v8::Local<v8::Value> data_v;
|
|
|
|
if (args.Length() == 2) {
|
|
|
|
if (args[1]->IsArrayBufferView()) {
|
|
|
|
data_v = args[1];
|
|
|
|
data = GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(data_v));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
|
|
}
|
2018-06-09 18:32:04 -04:00
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
DCHECK_EQ(d->current_args_, nullptr);
|
|
|
|
d->current_args_ = &args;
|
2018-06-13 13:38:22 -04:00
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
d->cb_(d->user_data_, req_id, control, data);
|
2018-06-13 13:38:22 -04:00
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
if (d->current_args_ == nullptr) {
|
2018-09-27 17:33:10 -04:00
|
|
|
// This indicates that deno_repond() was called already.
|
|
|
|
} else {
|
|
|
|
// Asynchronous.
|
2018-10-23 23:58:20 -04:00
|
|
|
d->current_args_ = nullptr;
|
2018-09-27 17:33:10 -04:00
|
|
|
// If the data ArrayBuffer was given, we must maintain a strong reference
|
|
|
|
// to it until deno_respond is called.
|
|
|
|
if (!data_v.IsEmpty()) {
|
|
|
|
AddDataRef(d, req_id, data_v);
|
|
|
|
}
|
|
|
|
}
|
2018-06-09 18:32:04 -04:00
|
|
|
}
|
|
|
|
|
2018-10-24 02:17:10 -04:00
|
|
|
void Shared(v8::Local<v8::Name> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
|
|
|
DCHECK_EQ(d->isolate_, isolate);
|
|
|
|
v8::Locker locker(d->isolate_);
|
|
|
|
v8::EscapableHandleScope handle_scope(isolate);
|
|
|
|
if (d->shared_.data_ptr == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
v8::Local<v8::ArrayBuffer> ab;
|
|
|
|
if (d->shared_ab_.IsEmpty()) {
|
|
|
|
// Lazily initialize the persistent external ArrayBuffer.
|
|
|
|
ab = v8::ArrayBuffer::New(isolate, d->shared_.data_ptr, d->shared_.data_len,
|
|
|
|
v8::ArrayBufferCreationMode::kExternalized);
|
|
|
|
d->shared_ab_.Reset(isolate, ab);
|
|
|
|
}
|
|
|
|
info.GetReturnValue().Set(ab);
|
|
|
|
}
|
|
|
|
|
2018-08-26 03:57:16 -04:00
|
|
|
// Sets the global error handler.
|
|
|
|
void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
|
|
|
DCHECK_EQ(d->isolate_, isolate);
|
2018-08-26 03:57:16 -04:00
|
|
|
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
if (!d->global_error_handler_.IsEmpty()) {
|
2018-08-26 13:22:07 -04:00
|
|
|
isolate->ThrowException(
|
|
|
|
v8_str("libdeno.setGlobalErrorHandler already called."));
|
2018-08-26 03:57:16 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Local<v8::Value> v = args[0];
|
|
|
|
CHECK(v->IsFunction());
|
|
|
|
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
d->global_error_handler_.Reset(isolate, func);
|
2018-08-26 03:57:16 -04:00
|
|
|
}
|
|
|
|
|
2018-10-12 14:22:52 -04:00
|
|
|
// Sets the promise uncaught reject handler
|
|
|
|
void SetPromiseRejectHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
|
|
|
DCHECK_EQ(d->isolate_, isolate);
|
2018-10-12 14:22:52 -04:00
|
|
|
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
if (!d->promise_reject_handler_.IsEmpty()) {
|
2018-10-12 14:22:52 -04:00
|
|
|
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);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
d->promise_reject_handler_.Reset(isolate, func);
|
2018-10-12 14:22:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sets the promise uncaught reject handler
|
|
|
|
void SetPromiseErrorExaminer(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
2018-10-23 23:58:20 -04:00
|
|
|
DenoIsolate* d = FromIsolate(isolate);
|
|
|
|
DCHECK_EQ(d->isolate_, isolate);
|
2018-10-12 14:22:52 -04:00
|
|
|
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
if (!d->promise_error_examiner_.IsEmpty()) {
|
2018-10-12 14:22:52 -04:00
|
|
|
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);
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
d->promise_error_examiner_.Reset(isolate, func);
|
2018-10-12 14:22:52 -04:00
|
|
|
}
|
|
|
|
|
2018-07-30 15:12:33 -04:00
|
|
|
bool ExecuteV8StringSource(v8::Local<v8::Context> context,
|
|
|
|
const char* js_filename,
|
|
|
|
v8::Local<v8::String> source) {
|
2018-06-10 08:24:39 -04:00
|
|
|
auto* isolate = context->GetIsolate();
|
2018-06-10 00:13:48 -04:00
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2018-06-09 18:32:04 -04:00
|
|
|
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2018-06-10 00:13:48 -04:00
|
|
|
v8::TryCatch try_catch(isolate);
|
2018-06-09 18:32:04 -04:00
|
|
|
|
2018-06-11 14:49:57 -04:00
|
|
|
auto name = v8_str(js_filename);
|
2018-06-09 18:32:04 -04:00
|
|
|
|
|
|
|
v8::ScriptOrigin origin(name);
|
|
|
|
|
|
|
|
auto script = v8::Script::Compile(context, source, &origin);
|
|
|
|
|
|
|
|
if (script.IsEmpty()) {
|
2018-06-13 18:55:40 -04:00
|
|
|
DCHECK(try_catch.HasCaught());
|
2018-06-10 00:13:48 -04:00
|
|
|
HandleException(context, try_catch.Exception());
|
|
|
|
return false;
|
2018-06-09 18:32:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
auto result = script.ToLocalChecked()->Run(context);
|
|
|
|
|
|
|
|
if (result.IsEmpty()) {
|
2018-06-13 18:55:40 -04:00
|
|
|
DCHECK(try_catch.HasCaught());
|
2018-06-10 00:13:48 -04:00
|
|
|
HandleException(context, try_catch.Exception());
|
|
|
|
return false;
|
2018-06-09 18:32:04 -04:00
|
|
|
}
|
|
|
|
|
2018-06-10 00:13:48 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-30 15:12:33 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-06-13 14:55:08 -04:00
|
|
|
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
2018-10-24 01:12:13 -04:00
|
|
|
const char* js_filename, const char* js_source,
|
|
|
|
const char* source_map) {
|
|
|
|
CHECK_NE(js_source, nullptr);
|
|
|
|
CHECK_NE(js_filename, nullptr);
|
2018-06-13 14:55:08 -04:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
auto global = context->Global();
|
2018-06-14 08:31:31 -04:00
|
|
|
|
|
|
|
auto deno_val = v8::Object::New(isolate);
|
2018-08-06 18:37:32 -04:00
|
|
|
CHECK(global->Set(context, deno::v8_str("libdeno"), deno_val).FromJust());
|
2018-06-14 08:31:31 -04:00
|
|
|
|
2018-06-13 14:55:08 -04:00
|
|
|
auto print_tmpl = v8::FunctionTemplate::New(isolate, Print);
|
|
|
|
auto print_val = print_tmpl->GetFunction(context).ToLocalChecked();
|
2018-06-14 08:31:31 -04:00
|
|
|
CHECK(deno_val->Set(context, deno::v8_str("print"), print_val).FromJust());
|
2018-06-13 14:55:08 -04:00
|
|
|
|
2018-07-01 12:07:12 -04:00
|
|
|
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());
|
2018-06-13 14:55:08 -04:00
|
|
|
|
2018-07-01 12:07:12 -04:00
|
|
|
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());
|
2018-06-13 14:55:08 -04:00
|
|
|
|
2018-10-24 02:17:10 -04:00
|
|
|
CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared)
|
|
|
|
.FromJust());
|
|
|
|
|
2018-08-26 13:22:07 -04:00
|
|
|
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());
|
2018-08-26 03:57:16 -04:00
|
|
|
|
2018-10-12 14:22:52 -04:00
|
|
|
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());
|
|
|
|
|
2018-07-30 15:12:33 -04:00
|
|
|
{
|
2018-08-02 13:13:32 -04:00
|
|
|
if (source_map != nullptr) {
|
2018-08-06 18:37:32 -04:00
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
v8::ScriptOrigin origin(v8_str("set_source_map.js"));
|
2018-10-24 01:12:13 -04:00
|
|
|
std::string source_map_parens =
|
|
|
|
std::string("(") + std::string(source_map) + std::string(")");
|
2018-08-06 18:37:32 -04:00
|
|
|
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());
|
2018-08-02 13:13:32 -04:00
|
|
|
}
|
2018-10-25 11:00:14 -04:00
|
|
|
|
|
|
|
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);
|
2018-07-30 15:12:33 -04:00
|
|
|
}
|
2018-06-13 14:55:08 -04:00
|
|
|
}
|
|
|
|
|
2018-10-23 23:58:20 -04:00
|
|
|
void DenoIsolate::AddIsolate(v8::Isolate* isolate) {
|
|
|
|
isolate_ = isolate;
|
2018-06-10 08:18:15 -04:00
|
|
|
// 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);
|
2018-10-23 23:58:20 -04:00
|
|
|
isolate_->SetPromiseRejectCallback(deno::PromiseRejectCallback);
|
|
|
|
isolate_->SetData(0, this);
|
2018-06-10 08:18:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace deno
|