core: Add test for snapshotting from Rust (#2197)

This commit is contained in:
Ryan Dahl 2019-04-24 21:43:06 -04:00 committed by GitHub
parent f694823507
commit 7fc9d7d62a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 35 deletions

View file

@ -65,12 +65,14 @@ pub struct Script<'a> {
pub enum StartupData<'a> { pub enum StartupData<'a> {
Script(Script<'a>), Script(Script<'a>),
Snapshot(&'a [u8]), Snapshot(&'a [u8]),
LibdenoSnapshot(Snapshot1<'a>),
None, None,
} }
#[derive(Default)] #[derive(Default)]
pub struct Config { pub struct Config {
dispatch: Option<Arc<Fn(&[u8], deno_buf) -> (bool, Box<Op>) + Send + Sync>>, dispatch: Option<Arc<Fn(&[u8], deno_buf) -> (bool, Box<Op>) + Send + Sync>>,
pub will_snapshot: bool,
} }
impl Config { impl Config {
@ -129,21 +131,29 @@ impl Isolate {
let shared = SharedQueue::new(RECOMMENDED_SIZE); let shared = SharedQueue::new(RECOMMENDED_SIZE);
let needs_init = true; let needs_init = true;
// Seperate into Option values for eatch startup type
let (startup_snapshot, startup_script) = match startup_data { let mut startup_script: Option<Script> = None;
StartupData::Snapshot(d) => (Some(d), None), let mut libdeno_config = libdeno::deno_config {
StartupData::Script(d) => (None, Some(d)), will_snapshot: if config.will_snapshot { 1 } else { 0 },
StartupData::None => (None, None), load_snapshot: Snapshot2::empty(),
};
let libdeno_config = libdeno::deno_config {
will_snapshot: 0,
load_snapshot: match startup_snapshot {
Some(s) => Snapshot2::from(s),
None => Snapshot2::empty(),
},
shared: shared.as_deno_buf(), shared: shared.as_deno_buf(),
recv_cb: Self::pre_dispatch, recv_cb: Self::pre_dispatch,
}; };
// Seperate into Option values for each startup type
match startup_data {
StartupData::Script(d) => {
startup_script = Some(d);
}
StartupData::Snapshot(d) => {
libdeno_config.load_snapshot = d.into();
}
StartupData::LibdenoSnapshot(d) => {
libdeno_config.load_snapshot = d;
}
StartupData::None => {}
};
let libdeno_isolate = unsafe { libdeno::deno_new(libdeno_config) }; let libdeno_isolate = unsafe { libdeno::deno_new(libdeno_config) };
let mut core_isolate = Self { let mut core_isolate = Self {
@ -342,7 +352,7 @@ impl Isolate {
out out
} }
pub fn snapshot_new(&self) -> Result<Snapshot1, JSError> { pub fn snapshot(&self) -> Result<Snapshot1<'static>, JSError> {
let snapshot = unsafe { libdeno::deno_snapshot_new(self.libdeno_isolate) }; let snapshot = unsafe { libdeno::deno_snapshot_new(self.libdeno_isolate) };
if let Some(js_error) = self.last_exception() { if let Some(js_error) = self.last_exception() {
assert_eq!(snapshot.data_ptr, null()); assert_eq!(snapshot.data_ptr, null());
@ -994,4 +1004,21 @@ pub mod tests {
assert_eq!(Ok(Async::Ready(())), isolate.poll()); assert_eq!(Ok(Async::Ready(())), isolate.poll());
}); });
} }
#[test]
fn will_snapshot() {
let snapshot = {
let mut config = Config::default();
config.will_snapshot = true;
let mut isolate = Isolate::new(StartupData::None, config);
js_check(isolate.execute("a.js", "a = 1 + 2"));
let s = isolate.snapshot().unwrap();
drop(isolate);
s
};
let startup_data = StartupData::LibdenoSnapshot(snapshot);
let mut isolate2 = Isolate::new(startup_data, Config::default());
js_check(isolate2.execute("check.js", "if (a != 3) throw Error('x')"));
}
} }

View file

@ -126,13 +126,6 @@ unsafe impl Send for deno_snapshot<'_> {}
/// The type returned from deno_snapshot_new. Needs to be dropped. /// The type returned from deno_snapshot_new. Needs to be dropped.
pub type Snapshot1<'a> = deno_snapshot<'a>; pub type Snapshot1<'a> = deno_snapshot<'a>;
// TODO Does this make sense?
impl Drop for Snapshot1<'_> {
fn drop(&mut self) {
unsafe { deno_snapshot_delete(self) }
}
}
/// The type created from slice. Used for loading. /// The type created from slice. Used for loading.
pub type Snapshot2<'a> = deno_snapshot<'a>; pub type Snapshot2<'a> = deno_snapshot<'a>;

View file

@ -101,6 +101,7 @@ deno_snapshot deno_snapshot_new(Deno* d_) {
auto blob = d->snapshot_creator_->CreateBlob( auto blob = d->snapshot_creator_->CreateBlob(
v8::SnapshotCreator::FunctionCodeHandling::kKeep); v8::SnapshotCreator::FunctionCodeHandling::kKeep);
d->has_snapshotted_ = true;
return {reinterpret_cast<uint8_t*>(const_cast<char*>(blob.data)), return {reinterpret_cast<uint8_t*>(const_cast<char*>(blob.data)),
blob.raw_size}; blob.raw_size};
} }

View file

@ -38,7 +38,8 @@ class DenoIsolate {
recv_cb_(config.recv_cb), recv_cb_(config.recv_cb),
next_zero_copy_id_(1), // zero_copy_id must not be zero. next_zero_copy_id_(1), // zero_copy_id must not be zero.
user_data_(nullptr), user_data_(nullptr),
resolve_cb_(nullptr) { resolve_cb_(nullptr),
has_snapshotted_(false) {
array_buffer_allocator_ = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); array_buffer_allocator_ = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
if (config.load_snapshot.data_ptr) { if (config.load_snapshot.data_ptr) {
snapshot_.data = snapshot_.data =
@ -53,7 +54,14 @@ class DenoIsolate {
delete locker_; delete locker_;
} }
if (snapshot_creator_) { if (snapshot_creator_) {
// TODO(ry) V8 has a strange assert which prevents a SnapshotCreator from
// being deallocated if it hasn't created a snapshot yet.
// https://github.com/v8/v8/blob/73212783fbd534fac76cc4b66aac899c13f71fc8/src/api.cc#L603
// If that assert is removed, this if guard could be removed.
// WARNING: There may be false positive LSAN errors here.
if (has_snapshotted_) {
delete snapshot_creator_; delete snapshot_creator_;
}
} else { } else {
isolate_->Dispose(); isolate_->Dispose();
} }
@ -120,6 +128,7 @@ class DenoIsolate {
v8::StartupData snapshot_; v8::StartupData snapshot_;
v8::Persistent<v8::ArrayBuffer> global_import_buf_; v8::Persistent<v8::ArrayBuffer> global_import_buf_;
v8::Persistent<v8::SharedArrayBuffer> shared_ab_; v8::Persistent<v8::SharedArrayBuffer> shared_ab_;
bool has_snapshotted_;
}; };
class UserDataScope { class UserDataScope {

View file

@ -16,6 +16,8 @@ SharedQueue Binary Layout
+---------------------------------------------------------------+ +---------------------------------------------------------------+
*/ */
/* eslint-disable @typescript-eslint/no-use-before-define */
(window => { (window => {
const GLOBAL_NAMESPACE = "Deno"; const GLOBAL_NAMESPACE = "Deno";
const CORE_NAMESPACE = "core"; const CORE_NAMESPACE = "core";
@ -32,6 +34,25 @@ SharedQueue Binary Layout
let sharedBytes; let sharedBytes;
let shared32; let shared32;
let initialized = false;
function maybeInit() {
if (!initialized) {
init();
initialized = true;
}
}
function init() {
let shared = Deno.core.shared;
assert(shared.byteLength > 0);
assert(sharedBytes == null);
assert(shared32 == null);
sharedBytes = new Uint8Array(shared);
shared32 = new Int32Array(shared);
// Callers should not call Deno.core.recv, use setAsyncHandler.
Deno.core.recv(handleAsyncMsgFromRust);
}
function assert(cond) { function assert(cond) {
if (!cond) { if (!cond) {
@ -40,12 +61,14 @@ SharedQueue Binary Layout
} }
function reset() { function reset() {
maybeInit();
shared32[INDEX_NUM_RECORDS] = 0; shared32[INDEX_NUM_RECORDS] = 0;
shared32[INDEX_NUM_SHIFTED_OFF] = 0; shared32[INDEX_NUM_SHIFTED_OFF] = 0;
shared32[INDEX_HEAD] = HEAD_INIT; shared32[INDEX_HEAD] = HEAD_INIT;
} }
function head() { function head() {
maybeInit();
return shared32[INDEX_HEAD]; return shared32[INDEX_HEAD];
} }
@ -121,6 +144,7 @@ SharedQueue Binary Layout
let asyncHandler; let asyncHandler;
function setAsyncHandler(cb) { function setAsyncHandler(cb) {
maybeInit();
assert(asyncHandler == null); assert(asyncHandler == null);
asyncHandler = cb; asyncHandler = cb;
} }
@ -135,17 +159,8 @@ SharedQueue Binary Layout
} }
} }
function init(shared) {
assert(shared.byteLength > 0);
assert(sharedBytes == null);
assert(shared32 == null);
sharedBytes = new Uint8Array(shared);
shared32 = new Int32Array(shared);
// Callers should not call Deno.core.recv, use setAsyncHandler.
window.Deno.core.recv(handleAsyncMsgFromRust);
}
function dispatch(control, zeroCopy = null) { function dispatch(control, zeroCopy = null) {
maybeInit();
// First try to push control to shared. // First try to push control to shared.
const success = push(control); const success = push(control);
// If successful, don't use first argument of core.send. // If successful, don't use first argument of core.send.
@ -170,6 +185,4 @@ SharedQueue Binary Layout
assert(window[GLOBAL_NAMESPACE] != null); assert(window[GLOBAL_NAMESPACE] != null);
assert(window[GLOBAL_NAMESPACE][CORE_NAMESPACE] != null); assert(window[GLOBAL_NAMESPACE][CORE_NAMESPACE] != null);
Object.assign(core, denoCore); Object.assign(core, denoCore);
})(this);
init(Deno.core.shared);
})(globalThis);