Add libdeno.shared global shared ArrayBuffer.

This commit is contained in:
Ryan Dahl 2018-10-24 02:17:10 -04:00
parent 61cda72881
commit 0501330607
10 changed files with 128 additions and 55 deletions

View file

@ -17,6 +17,8 @@ interface Libdeno {
print(x: string, isErr?: boolean): void;
shared: ArrayBuffer;
setGlobalErrorHandler: (
handler: (
message: string,

View file

@ -13,8 +13,8 @@
extern "C" {
Deno* deno_new(deno_buf snapshot, deno_recv_cb cb) {
deno::DenoIsolate* d = new deno::DenoIsolate(snapshot, cb);
Deno* deno_new(deno_buf snapshot, deno_buf shared, deno_recv_cb cb) {
deno::DenoIsolate* d = new deno::DenoIsolate(snapshot, cb, shared);
v8::Isolate::CreateParams params;
params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
@ -42,11 +42,12 @@ Deno* deno_new(deno_buf snapshot, deno_recv_cb cb) {
return reinterpret_cast<Deno*>(d);
}
Deno* deno_new_snapshotter(deno_recv_cb cb, const char* js_filename,
const char* js_source, const char* source_map) {
Deno* deno_new_snapshotter(deno_buf shared, deno_recv_cb cb,
const char* js_filename, const char* js_source,
const char* source_map) {
auto* creator = new v8::SnapshotCreator(deno::external_references);
auto* isolate = creator->GetIsolate();
auto* d = new deno::DenoIsolate(deno::empty_buf, cb);
auto* d = new deno::DenoIsolate(deno::empty_buf, cb, shared);
d->snapshot_creator_ = creator;
d->AddIsolate(isolate);
v8::Isolate::Scope isolate_scope(isolate);

View file

@ -339,6 +339,26 @@ void Send(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}
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);
}
// Sets the global error handler.
void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
@ -470,6 +490,9 @@ 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());
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 =

View file

@ -29,10 +29,11 @@ void deno_init();
const char* deno_v8_version();
void deno_set_v8_flags(int* argc, char** argv);
Deno* deno_new(deno_buf snapshot, deno_recv_cb cb);
Deno* deno_new(deno_buf snapshot, deno_buf shared, deno_recv_cb cb);
Deno* deno_new_snapshotter(deno_recv_cb cb, const char* js_filename,
const char* js_source, const char* source_map);
Deno* deno_new_snapshotter(deno_buf shared, deno_recv_cb cb,
const char* js_filename, const char* js_source,
const char* source_map);
deno_buf deno_get_snapshot(Deno* d);
void deno_delete(Deno* d);

View file

@ -13,8 +13,9 @@ namespace deno {
// deno_s = Wrapped Isolate.
class DenoIsolate {
public:
DenoIsolate(deno_buf snapshot, deno_recv_cb cb)
DenoIsolate(deno_buf snapshot, deno_recv_cb cb, deno_buf shared)
: isolate_(nullptr),
shared_(shared),
current_args_(nullptr),
snapshot_creator_(nullptr),
global_import_buf_ptr_(nullptr),
@ -31,6 +32,7 @@ class DenoIsolate {
void AddIsolate(v8::Isolate* isolate);
v8::Isolate* isolate_;
deno_buf shared_;
const v8::FunctionCallbackInfo<v8::Value>* current_args_;
v8::SnapshotCreator* snapshot_creator_;
void* global_import_buf_ptr_;
@ -48,6 +50,7 @@ class DenoIsolate {
v8::Persistent<v8::Function> promise_error_examiner_;
v8::StartupData snapshot_;
v8::Persistent<v8::ArrayBuffer> global_import_buf_;
v8::Persistent<v8::ArrayBuffer> shared_ab_;
};
class UserDataScope {
@ -75,6 +78,8 @@ struct InternalFieldData {
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 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);
@ -82,6 +87,7 @@ 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),

View file

@ -2,40 +2,38 @@
#include "test.h"
TEST(LibDenoTest, InitializesCorrectly) {
printf("snapshot.data_ptr %p\n", snapshot.data_ptr);
EXPECT_NE(snapshot.data_ptr, nullptr);
Deno* d = deno_new(empty, nullptr);
Deno* d = deno_new(snapshot, empty, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "1 + 2"));
deno_delete(d);
}
TEST(LibDenoTest, InitializesCorrectlyWithoutSnapshot) {
deno_buf empty = {nullptr, 0, nullptr, 0};
Deno* d = deno_new(empty, nullptr);
Deno* d = deno_new(empty, empty, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "1 + 2"));
deno_delete(d);
}
TEST(LibDenoTest, Snapshotter) {
Deno* d1 = deno_new_snapshotter(nullptr, "a.js", "a = 1 + 2", nullptr);
Deno* d1 = deno_new_snapshotter(empty, nullptr, "a.js", "a = 1 + 2", nullptr);
deno_buf test_snapshot = deno_get_snapshot(d1);
// TODO(ry) deno_delete(d1);
Deno* d2 = deno_new(test_snapshot, nullptr);
Deno* d2 = deno_new(test_snapshot, empty, nullptr);
EXPECT_TRUE(
deno_execute(d2, nullptr, "b.js", "if (a != 3) throw Error('x');"));
deno_delete(d2);
}
TEST(LibDenoTest, CanCallFunction) {
Deno* d = deno_new(snapshot, nullptr);
Deno* d = deno_new(snapshot, empty, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js",
"if (CanCallFunction() != 'foo') throw Error();"));
deno_delete(d);
}
TEST(LibDenoTest, ErrorsCorrectly) {
Deno* d = deno_new(snapshot, nullptr);
Deno* d = deno_new(snapshot, empty, nullptr);
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "throw Error()"));
deno_delete(d);
}
@ -72,14 +70,15 @@ void assert_null(deno_buf b) {
TEST(LibDenoTest, RecvReturnEmpty) {
static int count = 0;
Deno* d = deno_new(snapshot, [](auto _, int req_id, auto buf, auto data_buf) {
assert_null(data_buf);
count++;
EXPECT_EQ(static_cast<size_t>(3), buf.data_len);
EXPECT_EQ(buf.data_ptr[0], 'a');
EXPECT_EQ(buf.data_ptr[1], 'b');
EXPECT_EQ(buf.data_ptr[2], 'c');
});
Deno* d = deno_new(snapshot, empty,
[](auto _, int req_id, auto buf, auto data_buf) {
assert_null(data_buf);
count++;
EXPECT_EQ(static_cast<size_t>(3), buf.data_len);
EXPECT_EQ(buf.data_ptr[0], 'a');
EXPECT_EQ(buf.data_ptr[1], 'b');
EXPECT_EQ(buf.data_ptr[2], 'c');
});
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "RecvReturnEmpty()"));
EXPECT_EQ(count, 2);
deno_delete(d);
@ -87,7 +86,7 @@ TEST(LibDenoTest, RecvReturnEmpty) {
TEST(LibDenoTest, RecvReturnBar) {
static int count = 0;
Deno* d = deno_new(snapshot,
Deno* d = deno_new(snapshot, empty,
[](auto user_data, int req_id, auto buf, auto data_buf) {
auto d = reinterpret_cast<Deno*>(user_data);
assert_null(data_buf);
@ -104,7 +103,7 @@ TEST(LibDenoTest, RecvReturnBar) {
}
TEST(LibDenoTest, DoubleRecvFails) {
Deno* d = deno_new(snapshot, nullptr);
Deno* d = deno_new(snapshot, empty, nullptr);
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "DoubleRecvFails()"));
deno_delete(d);
}
@ -112,7 +111,7 @@ TEST(LibDenoTest, DoubleRecvFails) {
TEST(LibDenoTest, SendRecvSlice) {
static int count = 0;
Deno* d = deno_new(
snapshot, [](auto user_data, int req_id, auto buf, auto data_buf) {
snapshot, empty, [](auto user_data, int req_id, auto buf, auto data_buf) {
auto d = reinterpret_cast<Deno*>(user_data);
assert_null(data_buf);
static const size_t alloc_len = 1024;
@ -146,47 +145,49 @@ TEST(LibDenoTest, SendRecvSlice) {
TEST(LibDenoTest, JSSendArrayBufferViewTypes) {
static int count = 0;
Deno* d = deno_new(snapshot, [](auto _, int req_id, auto buf, auto data_buf) {
assert_null(data_buf);
count++;
size_t data_offset = buf.data_ptr - buf.alloc_ptr;
EXPECT_EQ(data_offset, 2468u);
EXPECT_EQ(buf.data_len, 1000u);
EXPECT_EQ(buf.alloc_len, 4321u);
EXPECT_EQ(buf.data_ptr[0], count);
});
Deno* d = deno_new(snapshot, empty,
[](auto _, int req_id, auto buf, auto data_buf) {
assert_null(data_buf);
count++;
size_t data_offset = buf.data_ptr - buf.alloc_ptr;
EXPECT_EQ(data_offset, 2468u);
EXPECT_EQ(buf.data_len, 1000u);
EXPECT_EQ(buf.alloc_len, 4321u);
EXPECT_EQ(buf.data_ptr[0], count);
});
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "JSSendArrayBufferViewTypes()"));
EXPECT_EQ(count, 3);
deno_delete(d);
}
TEST(LibDenoTest, TypedArraySnapshots) {
Deno* d = deno_new(snapshot, nullptr);
Deno* d = deno_new(snapshot, empty, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "TypedArraySnapshots()"));
deno_delete(d);
}
TEST(LibDenoTest, SnapshotBug) {
Deno* d = deno_new(snapshot, nullptr);
Deno* d = deno_new(snapshot, empty, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "SnapshotBug()"));
deno_delete(d);
}
TEST(LibDenoTest, GlobalErrorHandling) {
static int count = 0;
Deno* d = deno_new(snapshot, [](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, empty,
[](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);
});
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "GlobalErrorHandling()"));
EXPECT_EQ(count, 1);
deno_delete(d);
}
TEST(LibDenoTest, DoubleGlobalErrorHandlingFails) {
Deno* d = deno_new(snapshot, nullptr);
Deno* d = deno_new(snapshot, empty, nullptr);
EXPECT_FALSE(
deno_execute(d, nullptr, "a.js", "DoubleGlobalErrorHandlingFails()"));
deno_delete(d);
@ -195,7 +196,7 @@ TEST(LibDenoTest, DoubleGlobalErrorHandlingFails) {
TEST(LibDenoTest, DataBuf) {
static int count = 0;
static deno_buf data_buf_copy;
Deno* d = deno_new(snapshot,
Deno* d = deno_new(snapshot, empty,
[](auto _, int req_id, deno_buf buf, deno_buf data_buf) {
count++;
data_buf.data_ptr[0] = 4;
@ -217,12 +218,25 @@ TEST(LibDenoTest, DataBuf) {
TEST(LibDenoTest, PromiseRejectCatchHandling) {
static int count = 0;
Deno* d = deno_new(snapshot, [](auto _, int req_id, auto buf, auto data_buf) {
// If no error, nothing should be sent, and count should not increment
count++;
});
Deno* d = deno_new(snapshot, empty,
[](auto _, int req_id, auto buf, auto data_buf) {
// If no error, nothing should be sent, and count should
// not increment
count++;
});
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "PromiseRejectCatchHandling()"));
EXPECT_EQ(count, 0);
deno_delete(d);
}
TEST(LibDenoTest, Shared) {
uint8_t s[] = {0, 1, 2};
deno_buf shared = {nullptr, 0, s, 3};
Deno* d = deno_new(snapshot, shared, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "Shared()"));
EXPECT_EQ(s[0], 42);
EXPECT_EQ(s[1], 43);
EXPECT_EQ(s[2], 44);
deno_delete(d);
}

View file

@ -173,3 +173,16 @@ global.PromiseRejectCatchHandling = () => {
}
})();
}
global.Shared = () => {
const ab = libdeno.shared;
assert(ab instanceof ArrayBuffer);
assert(ab.byteLength === 3);
const ui8 = new Uint8Array(ab);
assert(ui8[0] === 0);
assert(ui8[1] === 1);
assert(ui8[2] === 2);
ui8[0] = 42;
ui8[1] = 43;
ui8[2] = 44;
}

View file

@ -30,7 +30,7 @@ int main(int argc, char** argv) {
deno_init();
Deno* d = deno_new_snapshotter(
nullptr, js_fn, js_source.c_str(),
deno::empty_buf, nullptr, js_fn, js_source.c_str(),
source_map_fn != nullptr ? source_map.c_str() : nullptr);
auto snapshot = deno_get_snapshot(d);

View file

@ -100,6 +100,15 @@ pub struct Metrics {
static DENO_INIT: std::sync::Once = std::sync::ONCE_INIT;
fn empty() -> libdeno::deno_buf {
libdeno::deno_buf {
alloc_ptr: std::ptr::null_mut(),
alloc_len: 0,
data_ptr: std::ptr::null_mut(),
data_len: 0,
}
}
impl Isolate {
pub fn new(
flags: flags::DenoFlags,
@ -109,9 +118,9 @@ impl Isolate {
DENO_INIT.call_once(|| {
unsafe { libdeno::deno_init() };
});
let shared = empty(); // TODO Use shared for message passing.
let libdeno_isolate = unsafe {
libdeno::deno_new(snapshot::deno_snapshot.clone(), pre_dispatch)
libdeno::deno_new(snapshot::deno_snapshot.clone(), shared, pre_dispatch)
};
// This channel handles sending async messages back to the runtime.
let (tx, rx) = mpsc::channel::<(i32, Buf)>();

View file

@ -28,7 +28,11 @@ extern "C" {
pub fn deno_init();
pub fn deno_v8_version() -> *const c_char;
pub fn deno_set_v8_flags(argc: *mut c_int, argv: *mut *mut c_char);
pub fn deno_new(snapshot: deno_buf, cb: DenoRecvCb) -> *const isolate;
pub fn deno_new(
snapshot: deno_buf,
shared: deno_buf,
cb: DenoRecvCb,
) -> *const isolate;
pub fn deno_delete(i: *const isolate);
pub fn deno_last_exception(i: *const isolate) -> *const c_char;
pub fn deno_check_promise_errors(i: *const isolate);