mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
feat(core): support creating snapshots from existing snapshots (#14744)
This commit is contained in:
parent
7c80f15020
commit
e202f890f0
2 changed files with 138 additions and 25 deletions
|
@ -7,6 +7,7 @@ use crate::modules::validate_import_assertions;
|
||||||
use crate::modules::ImportAssertionsKind;
|
use crate::modules::ImportAssertionsKind;
|
||||||
use crate::modules::ModuleMap;
|
use crate::modules::ModuleMap;
|
||||||
use crate::ops::OpCtx;
|
use crate::ops::OpCtx;
|
||||||
|
use crate::runtime::SnapshotOptions;
|
||||||
use crate::JsRuntime;
|
use crate::JsRuntime;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use std::option::Option;
|
use std::option::Option;
|
||||||
|
@ -96,8 +97,7 @@ pub fn module_origin<'a>(
|
||||||
pub fn initialize_context<'s>(
|
pub fn initialize_context<'s>(
|
||||||
scope: &mut v8::HandleScope<'s, ()>,
|
scope: &mut v8::HandleScope<'s, ()>,
|
||||||
op_ctxs: &[OpCtx],
|
op_ctxs: &[OpCtx],
|
||||||
snapshot_loaded: bool,
|
snapshot_options: SnapshotOptions,
|
||||||
will_snapshot: bool,
|
|
||||||
) -> v8::Local<'s, v8::Context> {
|
) -> v8::Local<'s, v8::Context> {
|
||||||
let scope = &mut v8::EscapableHandleScope::new(scope);
|
let scope = &mut v8::EscapableHandleScope::new(scope);
|
||||||
|
|
||||||
|
@ -108,12 +108,12 @@ pub fn initialize_context<'s>(
|
||||||
|
|
||||||
// Snapshot already registered `Deno.core.ops` but
|
// Snapshot already registered `Deno.core.ops` but
|
||||||
// extensions may provide ops that aren't part of the snapshot.
|
// extensions may provide ops that aren't part of the snapshot.
|
||||||
if snapshot_loaded {
|
if snapshot_options.loaded() {
|
||||||
// Grab the Deno.core.ops object & init it
|
// Grab the Deno.core.ops object & init it
|
||||||
let ops_obj = JsRuntime::grab_global::<v8::Object>(scope, "Deno.core.ops")
|
let ops_obj = JsRuntime::grab_global::<v8::Object>(scope, "Deno.core.ops")
|
||||||
.expect("Deno.core.ops to exist");
|
.expect("Deno.core.ops to exist");
|
||||||
initialize_ops(scope, ops_obj, op_ctxs, snapshot_loaded);
|
initialize_ops(scope, ops_obj, op_ctxs, snapshot_options);
|
||||||
if !will_snapshot {
|
if snapshot_options != SnapshotOptions::CreateFromExisting {
|
||||||
initialize_async_ops_info(scope, ops_obj, op_ctxs);
|
initialize_async_ops_info(scope, ops_obj, op_ctxs);
|
||||||
}
|
}
|
||||||
return scope.escape(context);
|
return scope.escape(context);
|
||||||
|
@ -127,10 +127,10 @@ pub fn initialize_context<'s>(
|
||||||
|
|
||||||
// Bind functions to Deno.core.ops.*
|
// Bind functions to Deno.core.ops.*
|
||||||
let ops_obj = JsRuntime::ensure_objs(scope, global, "Deno.core.ops").unwrap();
|
let ops_obj = JsRuntime::ensure_objs(scope, global, "Deno.core.ops").unwrap();
|
||||||
if !will_snapshot {
|
if !snapshot_options.will_snapshot() {
|
||||||
initialize_async_ops_info(scope, ops_obj, op_ctxs);
|
initialize_async_ops_info(scope, ops_obj, op_ctxs);
|
||||||
}
|
}
|
||||||
initialize_ops(scope, ops_obj, op_ctxs, !will_snapshot);
|
initialize_ops(scope, ops_obj, op_ctxs, snapshot_options);
|
||||||
scope.escape(context)
|
scope.escape(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,14 +138,14 @@ fn initialize_ops(
|
||||||
scope: &mut v8::HandleScope,
|
scope: &mut v8::HandleScope,
|
||||||
ops_obj: v8::Local<v8::Object>,
|
ops_obj: v8::Local<v8::Object>,
|
||||||
op_ctxs: &[OpCtx],
|
op_ctxs: &[OpCtx],
|
||||||
snapshot_loaded: bool,
|
snapshot_options: SnapshotOptions,
|
||||||
) {
|
) {
|
||||||
for ctx in op_ctxs {
|
for ctx in op_ctxs {
|
||||||
let ctx_ptr = ctx as *const OpCtx as *const c_void;
|
let ctx_ptr = ctx as *const OpCtx as *const c_void;
|
||||||
|
|
||||||
// If this is a fast op, we don't want it to be in the snapshot.
|
// If this is a fast op, we don't want it to be in the snapshot.
|
||||||
// Only initialize once snapshot is loaded.
|
// Only initialize once snapshot is loaded.
|
||||||
if ctx.decl.fast_fn.is_some() && snapshot_loaded {
|
if ctx.decl.fast_fn.is_some() && snapshot_options.loaded() {
|
||||||
set_func_raw(
|
set_func_raw(
|
||||||
scope,
|
scope,
|
||||||
ops_obj,
|
ops_obj,
|
||||||
|
@ -153,7 +153,7 @@ fn initialize_ops(
|
||||||
ctx.decl.v8_fn_ptr,
|
ctx.decl.v8_fn_ptr,
|
||||||
ctx_ptr,
|
ctx_ptr,
|
||||||
&ctx.decl.fast_fn,
|
&ctx.decl.fast_fn,
|
||||||
snapshot_loaded,
|
snapshot_options,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
set_func_raw(
|
set_func_raw(
|
||||||
|
@ -163,7 +163,7 @@ fn initialize_ops(
|
||||||
ctx.decl.v8_fn_ptr,
|
ctx.decl.v8_fn_ptr,
|
||||||
ctx_ptr,
|
ctx_ptr,
|
||||||
&None,
|
&None,
|
||||||
snapshot_loaded,
|
snapshot_options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ pub fn set_func_raw(
|
||||||
callback: v8::FunctionCallback,
|
callback: v8::FunctionCallback,
|
||||||
external_data: *const c_void,
|
external_data: *const c_void,
|
||||||
fast_function: &Option<Box<dyn FastFunction>>,
|
fast_function: &Option<Box<dyn FastFunction>>,
|
||||||
snapshot_loaded: bool,
|
snapshot_options: SnapshotOptions,
|
||||||
) {
|
) {
|
||||||
let key = v8::String::new(scope, name).unwrap();
|
let key = v8::String::new(scope, name).unwrap();
|
||||||
let external = v8::External::new(scope, external_data as *mut c_void);
|
let external = v8::External::new(scope, external_data as *mut c_void);
|
||||||
|
@ -198,11 +198,14 @@ pub fn set_func_raw(
|
||||||
v8::FunctionTemplate::builder_raw(callback).data(external.into());
|
v8::FunctionTemplate::builder_raw(callback).data(external.into());
|
||||||
let templ = if let Some(fast_function) = fast_function {
|
let templ = if let Some(fast_function) = fast_function {
|
||||||
// Don't initialize fast ops when snapshotting, the external references count mismatch.
|
// Don't initialize fast ops when snapshotting, the external references count mismatch.
|
||||||
if !snapshot_loaded {
|
if matches!(
|
||||||
builder.build(scope)
|
snapshot_options,
|
||||||
} else {
|
SnapshotOptions::Load | SnapshotOptions::None
|
||||||
|
) {
|
||||||
// TODO(@littledivy): Support fast api overloads in ops.
|
// TODO(@littledivy): Support fast api overloads in ops.
|
||||||
builder.build_fast(scope, &**fast_function, None)
|
builder.build_fast(scope, &**fast_function, None)
|
||||||
|
} else {
|
||||||
|
builder.build(scope)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builder.build(scope)
|
builder.build(scope)
|
||||||
|
|
130
core/runtime.rs
130
core/runtime.rs
|
@ -82,6 +82,7 @@ pub struct JsRuntime {
|
||||||
// This is an Option<OwnedIsolate> instead of just OwnedIsolate to workaround
|
// This is an Option<OwnedIsolate> instead of just OwnedIsolate to workaround
|
||||||
// a safety issue with SnapshotCreator. See JsRuntime::drop.
|
// a safety issue with SnapshotCreator. See JsRuntime::drop.
|
||||||
v8_isolate: Option<v8::OwnedIsolate>,
|
v8_isolate: Option<v8::OwnedIsolate>,
|
||||||
|
snapshot_options: SnapshotOptions,
|
||||||
built_from_snapshot: bool,
|
built_from_snapshot: bool,
|
||||||
allocations: IsolateAllocations,
|
allocations: IsolateAllocations,
|
||||||
extensions: Vec<Extension>,
|
extensions: Vec<Extension>,
|
||||||
|
@ -279,6 +280,38 @@ pub struct RuntimeOptions {
|
||||||
pub inspector: bool,
|
pub inspector: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum SnapshotOptions {
|
||||||
|
Load,
|
||||||
|
CreateFromExisting,
|
||||||
|
Create,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SnapshotOptions {
|
||||||
|
pub fn loaded(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
SnapshotOptions::Load | SnapshotOptions::CreateFromExisting
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn will_snapshot(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
SnapshotOptions::Create | SnapshotOptions::CreateFromExisting
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bools(snapshot_loaded: bool, will_snapshot: bool) -> Self {
|
||||||
|
match (snapshot_loaded, will_snapshot) {
|
||||||
|
(true, true) => SnapshotOptions::CreateFromExisting,
|
||||||
|
(false, true) => SnapshotOptions::Create,
|
||||||
|
(true, false) => SnapshotOptions::Load,
|
||||||
|
(false, false) => SnapshotOptions::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for JsRuntime {
|
impl Drop for JsRuntime {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(v8_isolate) = self.v8_isolate.as_mut() {
|
if let Some(v8_isolate) = self.v8_isolate.as_mut() {
|
||||||
|
@ -371,10 +404,39 @@ impl JsRuntime {
|
||||||
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
|
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
|
||||||
let global_context;
|
let global_context;
|
||||||
|
|
||||||
let mut isolate = if options.will_snapshot {
|
let (mut isolate, snapshot_options) = if options.will_snapshot {
|
||||||
// TODO(ry) Support loading snapshots before snapshotting.
|
let (snapshot_creator, snapshot_loaded) =
|
||||||
assert!(options.startup_snapshot.is_none());
|
if let Some(snapshot) = options.startup_snapshot {
|
||||||
let snapshot_creator = v8::Isolate::snapshot_creator(Some(refs));
|
(
|
||||||
|
match snapshot {
|
||||||
|
Snapshot::Static(data) => {
|
||||||
|
v8::Isolate::snapshot_creator_from_existing_snapshot(
|
||||||
|
data,
|
||||||
|
Some(refs),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Snapshot::JustCreated(data) => {
|
||||||
|
v8::Isolate::snapshot_creator_from_existing_snapshot(
|
||||||
|
data,
|
||||||
|
Some(refs),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Snapshot::Boxed(data) => {
|
||||||
|
v8::Isolate::snapshot_creator_from_existing_snapshot(
|
||||||
|
data,
|
||||||
|
Some(refs),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(v8::Isolate::snapshot_creator(Some(refs)), false)
|
||||||
|
};
|
||||||
|
|
||||||
|
let snapshot_options =
|
||||||
|
SnapshotOptions::from_bools(snapshot_loaded, options.will_snapshot);
|
||||||
|
|
||||||
let mut isolate = JsRuntime::setup_isolate(snapshot_creator);
|
let mut isolate = JsRuntime::setup_isolate(snapshot_creator);
|
||||||
{
|
{
|
||||||
// SAFETY: this is first use of `isolate_ptr` so we are sure we're
|
// SAFETY: this is first use of `isolate_ptr` so we are sure we're
|
||||||
|
@ -385,11 +447,11 @@ impl JsRuntime {
|
||||||
};
|
};
|
||||||
let scope = &mut v8::HandleScope::new(&mut isolate);
|
let scope = &mut v8::HandleScope::new(&mut isolate);
|
||||||
let context =
|
let context =
|
||||||
bindings::initialize_context(scope, &op_ctxs, false, true);
|
bindings::initialize_context(scope, &op_ctxs, snapshot_options);
|
||||||
global_context = v8::Global::new(scope, context);
|
global_context = v8::Global::new(scope, context);
|
||||||
scope.set_default_context(context);
|
scope.set_default_context(context);
|
||||||
}
|
}
|
||||||
isolate
|
(isolate, snapshot_options)
|
||||||
} else {
|
} else {
|
||||||
let mut params = options
|
let mut params = options
|
||||||
.create_params
|
.create_params
|
||||||
|
@ -412,6 +474,9 @@ impl JsRuntime {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let snapshot_options =
|
||||||
|
SnapshotOptions::from_bools(snapshot_loaded, options.will_snapshot);
|
||||||
|
|
||||||
let isolate = v8::Isolate::new(params);
|
let isolate = v8::Isolate::new(params);
|
||||||
let mut isolate = JsRuntime::setup_isolate(isolate);
|
let mut isolate = JsRuntime::setup_isolate(isolate);
|
||||||
{
|
{
|
||||||
|
@ -423,12 +488,12 @@ impl JsRuntime {
|
||||||
};
|
};
|
||||||
let scope = &mut v8::HandleScope::new(&mut isolate);
|
let scope = &mut v8::HandleScope::new(&mut isolate);
|
||||||
let context =
|
let context =
|
||||||
bindings::initialize_context(scope, &op_ctxs, snapshot_loaded, false);
|
bindings::initialize_context(scope, &op_ctxs, snapshot_options);
|
||||||
|
|
||||||
global_context = v8::Global::new(scope, context);
|
global_context = v8::Global::new(scope, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
isolate
|
(isolate, snapshot_options)
|
||||||
};
|
};
|
||||||
|
|
||||||
op_state.borrow_mut().put(isolate_ptr);
|
op_state.borrow_mut().put(isolate_ptr);
|
||||||
|
@ -464,6 +529,7 @@ impl JsRuntime {
|
||||||
let mut js_runtime = Self {
|
let mut js_runtime = Self {
|
||||||
v8_isolate: Some(isolate),
|
v8_isolate: Some(isolate),
|
||||||
built_from_snapshot: has_startup_snapshot,
|
built_from_snapshot: has_startup_snapshot,
|
||||||
|
snapshot_options,
|
||||||
allocations: IsolateAllocations::default(),
|
allocations: IsolateAllocations::default(),
|
||||||
event_loop_middlewares: Vec::with_capacity(options.extensions.len()),
|
event_loop_middlewares: Vec::with_capacity(options.extensions.len()),
|
||||||
extensions: options.extensions,
|
extensions: options.extensions,
|
||||||
|
@ -550,8 +616,10 @@ impl JsRuntime {
|
||||||
let context = bindings::initialize_context(
|
let context = bindings::initialize_context(
|
||||||
scope,
|
scope,
|
||||||
&self.state.borrow().op_ctxs,
|
&self.state.borrow().op_ctxs,
|
||||||
self.built_from_snapshot,
|
SnapshotOptions::from_bools(
|
||||||
false,
|
self.built_from_snapshot,
|
||||||
|
self.snapshot_options.will_snapshot(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
JsRealm::new(v8::Global::new(scope, context))
|
JsRealm::new(v8::Global::new(scope, context))
|
||||||
};
|
};
|
||||||
|
@ -2749,6 +2817,48 @@ pub mod tests {
|
||||||
.execute_script("check.js", "if (a != 3) throw Error('x')")
|
.execute_script("check.js", "if (a != 3) throw Error('x')")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn will_snapshot2() {
|
||||||
|
let startup_data = {
|
||||||
|
let mut runtime = JsRuntime::new(RuntimeOptions {
|
||||||
|
will_snapshot: true,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
runtime.execute_script("a.js", "let a = 1 + 2").unwrap();
|
||||||
|
runtime.snapshot()
|
||||||
|
};
|
||||||
|
|
||||||
|
let snapshot = Snapshot::JustCreated(startup_data);
|
||||||
|
let mut runtime = JsRuntime::new(RuntimeOptions {
|
||||||
|
will_snapshot: true,
|
||||||
|
startup_snapshot: Some(snapshot),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let startup_data = {
|
||||||
|
runtime
|
||||||
|
.execute_script("check_a.js", "if (a != 3) throw Error('x')")
|
||||||
|
.unwrap();
|
||||||
|
runtime.execute_script("b.js", "b = 2 + 3").unwrap();
|
||||||
|
runtime.snapshot()
|
||||||
|
};
|
||||||
|
|
||||||
|
let snapshot = Snapshot::JustCreated(startup_data);
|
||||||
|
{
|
||||||
|
let mut runtime = JsRuntime::new(RuntimeOptions {
|
||||||
|
startup_snapshot: Some(snapshot),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
runtime
|
||||||
|
.execute_script("check_b.js", "if (b != 5) throw Error('x')")
|
||||||
|
.unwrap();
|
||||||
|
runtime
|
||||||
|
.execute_script("check2.js", "if (!Deno.core) throw Error('x')")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_snapshot_callbacks() {
|
fn test_snapshot_callbacks() {
|
||||||
let snapshot = {
|
let snapshot = {
|
||||||
|
|
Loading…
Reference in a new issue