mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
core: Add test for snapshotting from Rust (#2197)
This commit is contained in:
parent
f694823507
commit
7fc9d7d62a
5 changed files with 78 additions and 35 deletions
|
@ -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')"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue