0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 17:34:47 -05:00

libdeno: deno_new should take a snapshot parameter.

This commit is contained in:
Ryan Dahl 2018-10-23 19:39:31 -04:00
parent 3438dbe350
commit fd68f85ce8
13 changed files with 201 additions and 168 deletions

View file

@ -390,7 +390,11 @@ after_test:
Select-String $trap -Path $files -SimpleMatch | where {
# V8 took the liberty to produce an absolute path in their ninja
# output. We can't do much about that, so we just ignore it.
$_.Line -notmatch "v8/builtins-generated/bytecodes-builtins-list.h"
$_.Line -notmatch "v8/builtins-generated/bytecodes-builtins-list.h" -and
# The absolute path to snapshot_libdeno_test.bin is passed to test_cc
# via pre-processor variable. It's absolute because we want to be able
# to execute test_cc from both the project root and the build root.
$_.Line -notmatch "snapshot_libdeno_test.bin"
} | tee -Variable line_matches
if ($line_matches) {
$ctx = $line_matches.Line |

122
BUILD.gn
View file

@ -124,9 +124,49 @@ rust_executable("deno") {
extern = main_extern
deps = [
":libdeno",
":snapshot",
]
}
source_set("snapshot") {
sources = [
"src/snapshot.cc",
]
configs += [ ":deno_config" ]
data = [
"$target_gen_dir/snapshot_deno.bin",
]
deps = [
":create_snapshot_deno",
]
# snapshot.cc doesn't need to depend on libdeno, it just needs deno_buf.
include_dirs = [ "libdeno/" ]
# src/snapshot.cc uses an assembly '.incbin' directive to embed the snapshot.
# This causes trouble when using sccache: since the snapshot file is not
# inlined by the c preprocessor, sccache doesn't take its contents into
# consideration, leading to false-positive cache hits.
# Maybe other caching tools have this issue too, but ccache is unaffected.
# Therefore, if a cc_wrapper is used that isn't ccache, include a generated
# header file that contains the the sha256 hash of the snapshot.
if (cc_wrapper != "" && cc_wrapper != "ccache") {
hash_h = "$target_gen_dir/bundle/hash.h"
inputs = [
hash_h,
]
deps += [ ":bundle_hash_h" ]
if (is_win) {
cflags = [ "/FI" + rebase_path(hash_h, target_out_dir) ]
} else {
cflags = [
"-include",
rebase_path(hash_h, target_out_dir),
]
}
}
}
rust_executable("hyper_hello") {
source_root = "tools/hyper_hello.rs"
extern = [
@ -140,100 +180,48 @@ rust_test("test_rs") {
extern = main_extern
deps = [
":libdeno",
":snapshot",
]
}
v8_executable("test_cc") {
testonly = true
sources = [
"libdeno/file_util_test.cc",
"libdeno/libdeno_test.cc",
"libdeno/test.cc",
]
deps = [
":deno_base_test",
":create_snapshot_libdeno_test",
":libdeno",
"//testing/gtest:gtest",
]
data = [
"$target_gen_dir/snapshot_libdeno_test.bin",
]
snapshot_abs_path = rebase_path(data[0])
defines = [ "SNAPSHOT_PATH=\"$snapshot_abs_path\"" ]
configs = [ ":deno_config" ]
}
static_library("libdeno") {
complete_static_lib = true
sources = [
"libdeno/from_snapshot.cc",
]
inputs = [
"$target_gen_dir/snapshot_deno.bin",
]
deps = [
":create_snapshot_deno",
":deno_bindings",
]
configs += [ ":deno_config" ]
# from_snapshot.cc uses an assembly '.incbin' directive to embed the snapshot.
# This causes trouble when using sccache: since the snapshot file is not
# inlined by the c preprocessor, sccache doesn't take its contents into
# consideration, leading to false-positive cache hits.
# Maybe other caching tools have this issue too, but ccache is unaffected.
# Therefore, if a cc_wrapper is used that isn't ccache, include a generated
# header file that contains the the sha256 hash of the snapshot.
if (cc_wrapper != "" && cc_wrapper != "ccache") {
hash_h = "$target_gen_dir/bundle/hash.h"
inputs += [ hash_h ]
deps += [ ":bundle_hash_h" ]
if (is_win) {
cflags = [ "/FI" + rebase_path(hash_h, target_out_dir) ]
} else {
cflags = [
"-include",
rebase_path(hash_h, target_out_dir),
]
}
}
}
# Only functionality needed for libdeno_test and snapshot_creator
# In particular no flatbuffers, no assets, no rust, no msg handlers.
# Because snapshots are slow, it's important that snapshot_creator's
# dependencies are minimal.
v8_source_set("deno_base") {
static_library("libdeno") {
complete_static_lib = true
sources = [
"libdeno/binding.cc",
"libdeno/deno.h",
"libdeno/file_util.cc",
"libdeno/file_util.h",
"libdeno/internal.h",
"libdeno/new.cc",
]
public_deps = [
"third_party/v8:v8_monolith",
]
configs = [ ":deno_config" ]
}
v8_source_set("deno_base_test") {
testonly = true
sources = [
"libdeno/file_util_test.cc",
"libdeno/from_snapshot.cc",
"libdeno/libdeno_test.cc",
"libdeno/test.cc",
]
deps = [
":create_snapshot_libdeno_test",
":deno_base",
"//testing/gtest:gtest",
]
data = [
"$target_gen_dir/snapshot_libdeno_test.bin",
]
defines = [ "LIBDENO_TEST" ]
configs = [ ":deno_config" ]
}
v8_source_set("deno_bindings") {
deps = [
":deno_base",
]
configs = [ ":deno_config" ]
configs += [ ":deno_config" ]
}
executable("snapshot_creator") {
@ -241,7 +229,7 @@ executable("snapshot_creator") {
"libdeno/snapshot_creator.cc",
]
deps = [
":deno_base",
":libdeno",
]
configs += [ ":deno_config" ]
}

View file

@ -30,7 +30,7 @@ void deno_init();
const char* deno_v8_version();
void deno_set_v8_flags(int* argc, char** argv);
Deno* deno_new(deno_recv_cb cb);
Deno* deno_new(deno_buf snapshot, deno_recv_cb cb);
void deno_delete(Deno* d);
// Returns false on error.

View file

@ -17,7 +17,7 @@ struct deno_s {
v8::Persistent<v8::Function> global_error_handler;
v8::Persistent<v8::Function> promise_reject_handler;
v8::Persistent<v8::Function> promise_error_examiner;
v8::StartupData snapshot;
v8::Persistent<v8::ArrayBuffer> global_import_buf;
void* global_import_buf_ptr;

View file

@ -1,23 +1,30 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
#include "testing/gtest/include/gtest/gtest.h"
#include "deno.h"
#include "test.h"
TEST(LibDenoTest, InitializesCorrectly) {
Deno* d = deno_new(nullptr);
printf("snapshot.data_ptr %p\n", snapshot.data_ptr);
EXPECT_NE(snapshot.data_ptr, nullptr);
Deno* d = deno_new(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);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "1 + 2"));
deno_delete(d);
}
TEST(LibDenoTest, CanCallFunction) {
Deno* d = deno_new(nullptr);
Deno* d = deno_new(snapshot, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js",
"if (CanCallFunction() != 'foo') throw Error();"));
deno_delete(d);
}
TEST(LibDenoTest, ErrorsCorrectly) {
Deno* d = deno_new(nullptr);
Deno* d = deno_new(snapshot, nullptr);
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "throw Error()"));
deno_delete(d);
}
@ -54,7 +61,7 @@ void assert_null(deno_buf b) {
TEST(LibDenoTest, RecvReturnEmpty) {
static int count = 0;
Deno* d = deno_new([](auto _, int req_id, auto buf, auto data_buf) {
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);
@ -69,56 +76,58 @@ TEST(LibDenoTest, RecvReturnEmpty) {
TEST(LibDenoTest, RecvReturnBar) {
static int count = 0;
Deno* d = deno_new([](auto user_data, int req_id, auto buf, auto data_buf) {
auto d = reinterpret_cast<Deno*>(user_data);
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_respond(d, user_data, req_id, strbuf("bar"));
});
Deno* d = deno_new(snapshot,
[](auto user_data, int req_id, auto buf, auto data_buf) {
auto d = reinterpret_cast<Deno*>(user_data);
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_respond(d, user_data, req_id, strbuf("bar"));
});
EXPECT_TRUE(deno_execute(d, d, "a.js", "RecvReturnBar()"));
EXPECT_EQ(count, 1);
deno_delete(d);
}
TEST(LibDenoTest, DoubleRecvFails) {
Deno* d = deno_new(nullptr);
Deno* d = deno_new(snapshot, nullptr);
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "DoubleRecvFails()"));
deno_delete(d);
}
TEST(LibDenoTest, SendRecvSlice) {
static int count = 0;
Deno* d = deno_new([](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;
size_t i = count++;
// Check the size and offset of the slice.
size_t data_offset = buf.data_ptr - buf.alloc_ptr;
EXPECT_EQ(data_offset, i * 11);
EXPECT_EQ(buf.data_len, alloc_len - i * 30);
EXPECT_EQ(buf.alloc_len, alloc_len);
// Check values written by the JS side.
EXPECT_EQ(buf.data_ptr[0], 100 + i);
EXPECT_EQ(buf.data_ptr[buf.data_len - 1], 100 - i);
// Make copy of the backing buffer -- this is currently necessary
// because deno_respond() takes ownership over the buffer, but we are
// not given ownership of `buf` by our caller.
uint8_t* alloc_ptr = reinterpret_cast<uint8_t*>(malloc(alloc_len));
memcpy(alloc_ptr, buf.alloc_ptr, alloc_len);
// Make a slice that is a bit shorter than the original.
deno_buf buf2{alloc_ptr, alloc_len, alloc_ptr + data_offset,
buf.data_len - 19};
// Place some values into the buffer for the JS side to verify.
buf2.data_ptr[0] = 200 + i;
buf2.data_ptr[buf2.data_len - 1] = 200 - i;
// Send back.
deno_respond(d, user_data, req_id, buf2);
});
Deno* d = deno_new(
snapshot, [](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;
size_t i = count++;
// Check the size and offset of the slice.
size_t data_offset = buf.data_ptr - buf.alloc_ptr;
EXPECT_EQ(data_offset, i * 11);
EXPECT_EQ(buf.data_len, alloc_len - i * 30);
EXPECT_EQ(buf.alloc_len, alloc_len);
// Check values written by the JS side.
EXPECT_EQ(buf.data_ptr[0], 100 + i);
EXPECT_EQ(buf.data_ptr[buf.data_len - 1], 100 - i);
// Make copy of the backing buffer -- this is currently necessary
// because deno_respond() takes ownership over the buffer, but we are
// not given ownership of `buf` by our caller.
uint8_t* alloc_ptr = reinterpret_cast<uint8_t*>(malloc(alloc_len));
memcpy(alloc_ptr, buf.alloc_ptr, alloc_len);
// Make a slice that is a bit shorter than the original.
deno_buf buf2{alloc_ptr, alloc_len, alloc_ptr + data_offset,
buf.data_len - 19};
// Place some values into the buffer for the JS side to verify.
buf2.data_ptr[0] = 200 + i;
buf2.data_ptr[buf2.data_len - 1] = 200 - i;
// Send back.
deno_respond(d, user_data, req_id, buf2);
});
EXPECT_TRUE(deno_execute(d, d, "a.js", "SendRecvSlice()"));
EXPECT_EQ(count, 5);
deno_delete(d);
@ -126,7 +135,7 @@ TEST(LibDenoTest, SendRecvSlice) {
TEST(LibDenoTest, JSSendArrayBufferViewTypes) {
static int count = 0;
Deno* d = deno_new([](auto _, int req_id, auto buf, auto data_buf) {
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;
@ -141,20 +150,20 @@ TEST(LibDenoTest, JSSendArrayBufferViewTypes) {
}
TEST(LibDenoTest, TypedArraySnapshots) {
Deno* d = deno_new(nullptr);
Deno* d = deno_new(snapshot, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "TypedArraySnapshots()"));
deno_delete(d);
}
TEST(LibDenoTest, SnapshotBug) {
Deno* d = deno_new(nullptr);
Deno* d = deno_new(snapshot, nullptr);
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "SnapshotBug()"));
deno_delete(d);
}
TEST(LibDenoTest, GlobalErrorHandling) {
static int count = 0;
Deno* d = deno_new([](auto _, int req_id, auto buf, auto data_buf) {
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);
@ -166,7 +175,7 @@ TEST(LibDenoTest, GlobalErrorHandling) {
}
TEST(LibDenoTest, DoubleGlobalErrorHandlingFails) {
Deno* d = deno_new(nullptr);
Deno* d = deno_new(snapshot, nullptr);
EXPECT_FALSE(
deno_execute(d, nullptr, "a.js", "DoubleGlobalErrorHandlingFails()"));
deno_delete(d);
@ -175,16 +184,17 @@ TEST(LibDenoTest, DoubleGlobalErrorHandlingFails) {
TEST(LibDenoTest, DataBuf) {
static int count = 0;
static deno_buf data_buf_copy;
Deno* d = deno_new([](auto _, int req_id, deno_buf buf, deno_buf data_buf) {
count++;
data_buf.data_ptr[0] = 4;
data_buf.data_ptr[1] = 2;
data_buf_copy = data_buf;
EXPECT_EQ(2u, buf.data_len);
EXPECT_EQ(2u, data_buf.data_len);
EXPECT_EQ(buf.data_ptr[0], 1);
EXPECT_EQ(buf.data_ptr[1], 2);
});
Deno* d = deno_new(snapshot,
[](auto _, int req_id, deno_buf buf, deno_buf data_buf) {
count++;
data_buf.data_ptr[0] = 4;
data_buf.data_ptr[1] = 2;
data_buf_copy = data_buf;
EXPECT_EQ(2u, buf.data_len);
EXPECT_EQ(2u, data_buf.data_len);
EXPECT_EQ(buf.data_ptr[0], 1);
EXPECT_EQ(buf.data_ptr[1], 2);
});
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "DataBuf()"));
EXPECT_EQ(count, 1);
// data_buf was subsequently changed in JS, let's check that our copy reflects
@ -196,7 +206,7 @@ TEST(LibDenoTest, DataBuf) {
TEST(LibDenoTest, PromiseRejectCatchHandling) {
static int count = 0;
Deno* d = deno_new([](auto _, int req_id, auto buf, auto data_buf) {
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++;
});

View file

@ -10,22 +10,6 @@
#include "deno.h"
#include "internal.h"
extern const char deno_snapshot_start asm("deno_snapshot_start");
extern const char deno_snapshot_end asm("deno_snapshot_end");
#ifdef LIBDENO_TEST
asm(".data\n"
"deno_snapshot_start: .incbin \"gen/snapshot_libdeno_test.bin\"\n"
"deno_snapshot_end:\n"
".globl deno_snapshot_start;\n"
".globl deno_snapshot_end;");
#else
asm(".data\n"
"deno_snapshot_start: .incbin \"gen/snapshot_deno.bin\"\n"
"deno_snapshot_end:\n"
".globl deno_snapshot_start;\n"
".globl deno_snapshot_end;");
#endif // LIBDENO_TEST
namespace deno {
std::vector<InternalFieldData*> deserialized_data;
@ -43,7 +27,11 @@ void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
deserialized_data.push_back(embedder_field);
}
Deno* NewFromSnapshot(deno_recv_cb cb) {
} // namespace deno
extern "C" {
Deno* deno_new(deno_buf snapshot, deno_recv_cb cb) {
Deno* d = new Deno;
d->currentArgs = nullptr;
d->cb = cb;
@ -51,16 +39,16 @@ Deno* NewFromSnapshot(deno_recv_cb cb) {
v8::Isolate::CreateParams params;
params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
params.external_references = external_references;
params.external_references = deno::external_references;
CHECK_NE(&deno_snapshot_start, nullptr);
int snapshot_len =
static_cast<int>(&deno_snapshot_end - &deno_snapshot_start);
static v8::StartupData snapshot = {&deno_snapshot_start, snapshot_len};
params.snapshot_blob = &snapshot;
if (snapshot.data_ptr) {
d->snapshot.data = reinterpret_cast<const char*>(snapshot.data_ptr);
d->snapshot.raw_size = static_cast<int>(snapshot.data_len);
params.snapshot_blob = &d->snapshot;
}
v8::Isolate* isolate = v8::Isolate::New(params);
AddIsolate(d, isolate);
deno::AddIsolate(d, isolate);
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
@ -70,15 +58,10 @@ Deno* NewFromSnapshot(deno_recv_cb cb) {
v8::Context::New(isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
v8::MaybeLocal<v8::Value>(),
v8::DeserializeInternalFieldsCallback(
DeserializeInternalFields, nullptr));
deno::DeserializeInternalFields, nullptr));
d->context.Reset(d->isolate, context);
}
return d;
}
} // namespace deno
extern "C" {
Deno* deno_new(deno_recv_cb cb) { return deno::NewFromSnapshot(cb); }
}

View file

@ -1,8 +1,20 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
#include "deno.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "test.h"
#include "file_util.h"
deno_buf snapshot = {nullptr, 0, nullptr, 0};
int main(int argc, char** argv) {
// Load the snapshot.
std::string contents;
if (!deno::ReadFileToString(SNAPSHOT_PATH, &contents)) {
printf("Failed to read file %s\n", SNAPSHOT_PATH);
return 1;
}
snapshot.data_ptr =
reinterpret_cast<uint8_t*>(const_cast<char*>(contents.c_str()));
snapshot.data_len = contents.size();
testing::InitGoogleTest(&argc, argv);
deno_init();
deno_set_v8_flags(&argc, argv);

11
libdeno/test.h Normal file
View file

@ -0,0 +1,11 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
#ifndef TEST_H
#define TEST_H
#include "deno.h"
#include "testing/gtest/include/gtest/gtest.h"
extern deno_buf snapshot; // Loaded in libdeno/test.cc
const deno_buf empty = {nullptr, 0, nullptr, 0};
#endif // TEST_H

View file

@ -8,6 +8,7 @@ use deno_dir;
use errors::DenoError;
use flags;
use libdeno;
use snapshot;
use futures::Future;
use libc::c_void;
@ -109,7 +110,9 @@ impl Isolate {
unsafe { libdeno::deno_init() };
});
let libdeno_isolate = unsafe { libdeno::deno_new(pre_dispatch) };
let libdeno_isolate = unsafe {
libdeno::deno_new(snapshot::deno_snapshot.clone(), pre_dispatch)
};
// This channel handles sending async messages back to the runtime.
let (tx, rx) = mpsc::channel::<(i32, Buf)>();

View file

@ -1,6 +1,4 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
#![allow(dead_code)]
extern crate libc;
use libc::c_char;
use libc::c_int;
use libc::c_void;
@ -11,7 +9,7 @@ pub struct isolate {
}
#[repr(C)]
#[derive(PartialEq)]
#[derive(Clone, PartialEq)]
pub struct deno_buf {
pub alloc_ptr: *mut u8,
pub alloc_len: usize,
@ -30,7 +28,7 @@ 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(cb: DenoRecvCb) -> *const isolate;
pub fn deno_new(snapshot: 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);

View file

@ -32,6 +32,7 @@ mod isolate;
mod libdeno;
pub mod ops;
mod resources;
mod snapshot;
mod tokio_util;
mod tokio_write;
mod version;

18
src/snapshot.cc Normal file
View file

@ -0,0 +1,18 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
#include "deno.h"
extern "C" {
extern const char snapshot_start asm("snapshot_start");
extern const char snapshot_end asm("snapshot_end");
asm(".data\n"
"snapshot_start: .incbin \"gen/snapshot_deno.bin\"\n"
"snapshot_end:\n"
".globl snapshot_start;\n"
".globl snapshot_end;");
extern const deno_buf deno_snapshot = {
nullptr, 0, reinterpret_cast<uint8_t*>(const_cast<char*>(&snapshot_start)),
static_cast<size_t>(&snapshot_end - &snapshot_start)};
}

5
src/snapshot.rs Normal file
View file

@ -0,0 +1,5 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
use libdeno::deno_buf;
extern "C" {
pub static deno_snapshot: deno_buf;
}