Skip to content

Commit

Permalink
feat(core): support initializing extensions with and without JS (deno…
Browse files Browse the repository at this point in the history
…land#16789)

This commit allows to execute more JS code from extensions when
creating a snapshot from an existing snapshot.

"deno_core::RuntimeOptions::extensions_with_js" field was added
that is used to pass a list of extensions whose both "ops" and
associated JS source should be executed upon start.

Co-authored-by: crowlkats <[email protected]>
  • Loading branch information
bartlomieju and crowlKats committed Nov 26, 2022
1 parent d4f659d commit 28b5a7e
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 30 deletions.
2 changes: 1 addition & 1 deletion bench_util/js_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::profiling::is_profiling;

pub fn create_js_runtime(setup: impl FnOnce() -> Vec<Extension>) -> JsRuntime {
JsRuntime::new(RuntimeOptions {
extensions: setup(),
extensions_with_js: setup(),
..Default::default()
})
}
Expand Down
2 changes: 2 additions & 0 deletions cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ mod ts {
Ok(())
})
.build()],
extensions_with_js: vec![],
additional_files: files,
compression_cb: Some(Box::new(|vec, snapshot_slice| {
vec.extend_from_slice(
Expand Down Expand Up @@ -304,6 +305,7 @@ fn create_cli_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
snapshot_path,
startup_snapshot: Some(deno_runtime::js::deno_isolate_init()),
extensions,
extensions_with_js: vec![],
additional_files: files,
compression_cb: Some(Box::new(|vec, snapshot_slice| {
lzzzz::lz4_hc::compress_to_vec(
Expand Down
101 changes: 74 additions & 27 deletions core/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub struct JsRuntime {
built_from_snapshot: bool,
allocations: IsolateAllocations,
extensions: Vec<Extension>,
extensions_with_js: Vec<Extension>,
event_loop_middlewares: Vec<Box<OpEventLoopFn>>,
// Marks if this is considered the top-level runtime. Used only be inspector.
is_main: bool,
Expand Down Expand Up @@ -240,10 +241,23 @@ pub struct RuntimeOptions {
/// executed tries to load modules.
pub module_loader: Option<Rc<dyn ModuleLoader>>,

/// JsRuntime extensions, not to be confused with ES modules
/// these are sets of ops and other JS code to be initialized.
/// JsRuntime extensions, not to be confused with ES modules.
/// Only ops registered by extensions will be initialized. If you need
/// to execute JS code from extensions, use `extensions_with_js` options
/// instead.
pub extensions: Vec<Extension>,

/// JsRuntime extensions, not to be confused with ES modules.
/// Ops registered by extensions will be initialized and JS code will be
/// executed. If you don't need to execute JS code from extensions, use
/// `extensions` option instead.
///
/// This is useful when creating snapshots, in such case you would pass
/// extensions using `extensions_with_js`, later when creating a runtime
/// from the snapshot, you would pass these extensions using `extensions`
/// option.
pub extensions_with_js: Vec<Extension>,

/// V8 snapshot that should be loaded on startup.
///
/// Currently can't be used with `will_snapshot`.
Expand Down Expand Up @@ -339,11 +353,20 @@ impl JsRuntime {
let has_startup_snapshot = options.startup_snapshot.is_some();

// Add builtins extension
options
.extensions
.insert(0, crate::ops_builtin::init_builtins());
if !has_startup_snapshot {
options
.extensions_with_js
.insert(0, crate::ops_builtin::init_builtins());
} else {
options
.extensions
.insert(0, crate::ops_builtin::init_builtins());
}

let ops = Self::collect_ops(&mut options.extensions);
let ops = Self::collect_ops(
&mut options.extensions,
&mut options.extensions_with_js,
);
let mut op_state = OpState::new(ops.len());

if let Some(get_error_class_fn) = options.get_error_class_fn {
Expand Down Expand Up @@ -539,6 +562,7 @@ impl JsRuntime {
allocations: IsolateAllocations::default(),
event_loop_middlewares: Vec::with_capacity(options.extensions.len()),
extensions: options.extensions,
extensions_with_js: options.extensions_with_js,
state: state_rc,
module_map: Some(module_map_rc),
is_main: options.is_main,
Expand All @@ -547,12 +571,8 @@ impl JsRuntime {
// Init resources and ops before extensions to make sure they are
// available during the initialization process.
js_runtime.init_extension_ops().unwrap();
// TODO(@AaronO): diff extensions inited in snapshot and those provided
// for now we assume that snapshot and extensions always match
if !has_startup_snapshot {
let realm = js_runtime.global_realm();
js_runtime.init_extension_js(&realm).unwrap();
}
let realm = js_runtime.global_realm();
js_runtime.init_extension_js(&realm).unwrap();
// Init callbacks (opresolve)
js_runtime.init_cbs();

Expand Down Expand Up @@ -682,7 +702,8 @@ impl JsRuntime {
/// Initializes JS of provided Extensions in the given realm
fn init_extension_js(&mut self, realm: &JsRealm) -> Result<(), Error> {
// Take extensions to avoid double-borrow
let mut extensions: Vec<Extension> = std::mem::take(&mut self.extensions);
let mut extensions: Vec<Extension> =
std::mem::take(&mut self.extensions_with_js);
for m in extensions.iter_mut() {
let js_files = m.init_js();
for (filename, source) in js_files {
Expand All @@ -691,15 +712,22 @@ impl JsRuntime {
}
}
// Restore extensions
self.extensions = extensions;
self.extensions_with_js = extensions;

Ok(())
}

/// Collects ops from extensions & applies middleware
fn collect_ops(extensions: &mut [Extension]) -> Vec<OpDecl> {
fn collect_ops(
extensions: &mut [Extension],
extensions_with_js: &mut [Extension],
) -> Vec<OpDecl> {
let mut exts = vec![];
exts.extend(extensions);
exts.extend(extensions_with_js);

// Middleware
let middleware: Vec<Box<OpMiddlewareFn>> = extensions
let middleware: Vec<Box<OpMiddlewareFn>> = exts
.iter_mut()
.filter_map(|e| e.init_middleware())
.collect();
Expand All @@ -708,7 +736,7 @@ impl JsRuntime {
let macroware = move |d| middleware.iter().fold(d, |d, m| m(d));

// Flatten ops, apply middlware & override disabled ops
extensions
exts
.iter_mut()
.filter_map(|e| e.init_ops())
.flatten()
Expand All @@ -733,22 +761,41 @@ impl JsRuntime {
fn init_extension_ops(&mut self) -> Result<(), Error> {
let op_state = self.op_state();
// Take extensions to avoid double-borrow
let mut extensions: Vec<Extension> = std::mem::take(&mut self.extensions);
{
let mut extensions: Vec<Extension> = std::mem::take(&mut self.extensions);

// Setup state
for e in extensions.iter_mut() {
// ops are already registered during in bindings::initialize_context();
e.init_state(&mut op_state.borrow_mut())?;
// Setup state
for e in extensions.iter_mut() {
// ops are already registered during in bindings::initialize_context();
e.init_state(&mut op_state.borrow_mut())?;

// Setup event-loop middleware
if let Some(middleware) = e.init_event_loop_middleware() {
self.event_loop_middlewares.push(middleware);
// Setup event-loop middleware
if let Some(middleware) = e.init_event_loop_middleware() {
self.event_loop_middlewares.push(middleware);
}
}

// Restore extensions
self.extensions = extensions;
}
{
let mut extensions: Vec<Extension> =
std::mem::take(&mut self.extensions_with_js);

// Restore extensions
self.extensions = extensions;
// Setup state
for e in extensions.iter_mut() {
// ops are already registered during in bindings::initialize_context();
e.init_state(&mut op_state.borrow_mut())?;

// Setup event-loop middleware
if let Some(middleware) = e.init_event_loop_middleware() {
self.event_loop_middlewares.push(middleware);
}
}

// Restore extensions
self.extensions_with_js = extensions;
}
Ok(())
}

Expand Down
2 changes: 2 additions & 0 deletions core/snapshot_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct CreateSnapshotOptions {
pub snapshot_path: PathBuf,
pub startup_snapshot: Option<Snapshot>,
pub extensions: Vec<Extension>,
pub extensions_with_js: Vec<Extension>,
pub additional_files: Vec<PathBuf>,
pub compression_cb: Option<Box<CompressionCb>>,
}
Expand All @@ -21,6 +22,7 @@ pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
will_snapshot: true,
startup_snapshot: create_snapshot_options.startup_snapshot,
extensions: create_snapshot_options.extensions,
extensions_with_js: create_snapshot_options.extensions_with_js,
..Default::default()
});

Expand Down
5 changes: 3 additions & 2 deletions runtime/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ mod not_docs {
}

fn create_runtime_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
let extensions: Vec<Extension> = vec![
let extensions_with_js: Vec<Extension> = vec![
deno_webidl::init(),
deno_console::init(),
deno_url::init(),
Expand Down Expand Up @@ -154,7 +154,8 @@ mod not_docs {
cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
snapshot_path,
startup_snapshot: None,
extensions,
extensions: vec![],
extensions_with_js,
additional_files: files,
compression_cb: Some(Box::new(|vec, snapshot_slice| {
lzzzz::lz4_hc::compress_to_vec(
Expand Down

0 comments on commit 28b5a7e

Please sign in to comment.