Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions core/extension_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ pub fn setup_op_state(
/// Collects ops from extensions & applies middleware
pub fn init_ops(
deno_core_ops: &'static [OpDecl],
extra_idx: Option<usize>,
extensions: &mut [Extension],
) -> (Vec<OpDecl>, Vec<OpMethodDecl>) {
) -> (Vec<OpDecl>, Vec<OpMethodDecl>, Option<usize>) {
// In debug build verify there that inter-Extension dependencies
// are setup correctly.
#[cfg(debug_assertions)]
Expand Down Expand Up @@ -81,7 +82,11 @@ pub fn init_ops(
});
}

for ext in extensions.iter_mut() {
let extra_idx = extra_idx.unwrap_or(extensions.len());
let mut extra_ops_idx = None;
for (idx, ext) in extensions.iter_mut().enumerate() {
let is_extra = idx >= extra_idx;
let start_len = ops.len();
let ext_ops = ext.init_ops();
for ext_op in ext_ops {
ops.push(OpDecl {
Expand All @@ -95,13 +100,16 @@ pub fn init_ops(
for ext_op in ext_method_ops {
op_methods.push(*ext_op);
}
if is_extra && extra_ops_idx.is_none() {
extra_ops_idx = Some(start_len);
}
}

// In debug build verify there are no duplicate ops.
#[cfg(debug_assertions)]
check_no_duplicate_op_names(&ops);

(ops, op_methods)
(ops, op_methods, extra_ops_idx)
}

/// This functions panics if any of the extensions is missing its dependencies.
Expand Down
1 change: 1 addition & 0 deletions core/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ pub use crate::runtime::ImportAssertionsSupportCustomCallbackArgs;
pub use crate::runtime::JsRuntime;
pub use crate::runtime::JsRuntimeForSnapshot;
pub use crate::runtime::MODULE_MAP_SLOT_INDEX;
pub use crate::runtime::OpRegistration;
pub use crate::runtime::PollEventLoopOptions;
pub use crate::runtime::RuntimeOptions;
pub use crate::runtime::SharedArrayBufferStore;
Expand Down
72 changes: 62 additions & 10 deletions core/runtime/jsruntime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,26 +228,45 @@ pub(crate) enum InitMode {
/// We are using a snapshot, thus certain initialization steps are skipped.
FromSnapshot {
// Can we skip the work of op registration?
skip_op_registration: bool,
op_registration: OpRegistration,
},
}

#[derive(Default, Debug, Eq, PartialEq, Copy, Clone)]
pub enum OpRegistration {
SkipAll,
#[default]
RegisterAll,
RegisterExtra,
}

impl InitMode {
fn from_options(options: &RuntimeOptions) -> Self {
match options.startup_snapshot {
None => Self::New,
Some(_) => Self::FromSnapshot {
skip_op_registration: options.skip_op_registration,
op_registration: options.op_registration,
},
}
}

#[inline]
pub fn needs_ops_bindings(&self) -> bool {
pub fn needs_all_ops_bindings(&self) -> bool {
!matches!(
self,
InitMode::FromSnapshot {
skip_op_registration: true
op_registration: OpRegistration::SkipAll
| OpRegistration::RegisterExtra,
}
)
}

#[inline]
pub fn needs_extra_ops_bindings(&self) -> bool {
matches!(
self,
InitMode::FromSnapshot {
op_registration: OpRegistration::RegisterExtra
}
)
}
Expand Down Expand Up @@ -461,14 +480,17 @@ pub struct RuntimeOptions {
/// JavaScript sources in the extensions.
pub extensions: Vec<Extension>,

/// Extra extensions to register.
pub extra_extensions: Vec<Extension>,

/// V8 snapshot that should be loaded on startup.
///
/// For testing, use `runtime.snapshot()` and then [`Box::leak`] to acquire
// a static slice.
pub startup_snapshot: Option<&'static [u8]>,

/// Should op registration be skipped?
pub skip_op_registration: bool,
pub op_registration: OpRegistration,

/// Isolate creation parameters.
pub create_params: Option<v8::CreateParams>,
Expand Down Expand Up @@ -797,6 +819,17 @@ impl JsRuntime {
let mut op_state = OpState::new(options.maybe_op_stack_trace_callback);
let unrefed_ops = op_state.unrefed_ops.clone();

// If there are "extra" extensions, we may need to register them later.
// Save the start index of the extra extensions.
let has_extra_extensions = !options.extra_extensions.is_empty();
let len = extensions.len();
extensions.extend(options.extra_extensions);
let extra_idx = if has_extra_extensions {
Some(len)
} else {
None
};

let lazy_extensions =
extension_set::setup_op_state(&mut op_state, &mut extensions);

Expand Down Expand Up @@ -873,8 +906,11 @@ impl JsRuntime {

// ...now we're moving on to ops; set them up, create `OpCtx` for each op
// and get ready to actually create V8 isolate...
let (op_decls, mut op_method_decls) =
extension_set::init_ops(crate::ops_builtin::BUILTIN_OPS, &mut extensions);
let (op_decls, mut op_method_decls, extra_idx) = extension_set::init_ops(
crate::ops_builtin::BUILTIN_OPS,
extra_idx,
&mut extensions,
);

let op_driver = Rc::new(OpDriverImpl::default());
let op_metrics_factory_fn = options.op_metrics_factory_fn.take();
Expand All @@ -888,6 +924,10 @@ impl JsRuntime {
state_rc.clone(),
enable_stack_trace_in_ops,
);
// the extra_idx is the start index of the op_decls from "extra" extensions.
// methods get put at the fornt of the opctxs, so the start index of opctxs from "extra" extensions is
// extra_idx + methods_ctx_offset.
let extra_idx = extra_idx.map(|extra_idx| extra_idx + methods_ctx_offset);

// ...ops are now almost fully set up; let's create a V8 isolate...
let (
Expand All @@ -897,7 +937,8 @@ impl JsRuntime {
) = extension_set::get_middlewares_and_external_refs(&mut extensions);

// Capture the extension, op and source counts
let extensions = extensions.iter().map(|e| e.name).collect();
let extension_names: Vec<&str> =
extensions.iter().map(|e| e.name).collect();
let op_count = op_ctxs.len();
let source_count = sources.len();
let addl_refs_count = additional_references.len();
Expand Down Expand Up @@ -1023,7 +1064,7 @@ impl JsRuntime {
}
// If we're creating a new runtime or there are new ops to register
// set up JavaScript bindings for them.
if init_mode.needs_ops_bindings() {
if init_mode.needs_all_ops_bindings() {
bindings::initialize_deno_core_ops_bindings(
scope,
context,
Expand All @@ -1032,6 +1073,17 @@ impl JsRuntime {
methods_ctx_offset,
&mut state_rc.function_templates.borrow_mut(),
);
} else if let Some(extra_idx) = extra_idx {
if init_mode.needs_extra_ops_bindings() {
bindings::initialize_deno_core_ops_bindings(
scope,
context,
&context_state.op_ctxs[extra_idx..],
&[],
0,
&mut state_rc.function_templates.borrow_mut(),
);
}
}

// SAFETY: Initialize the context state slot.
Expand Down Expand Up @@ -1140,7 +1192,7 @@ impl JsRuntime {
inner: InnerIsolateState {
will_snapshot,
op_count,
extensions,
extensions: extension_names,
source_count,
addl_refs_count,
main_realm: ManuallyDrop::new(main_realm),
Expand Down
1 change: 1 addition & 0 deletions core/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub use jsruntime::JsRuntimeForSnapshot;
pub use jsruntime::JsRuntimeState;
#[cfg(test)]
pub(crate) use jsruntime::NO_OF_BUILTIN_MODULES;
pub use jsruntime::OpRegistration;
pub use jsruntime::PollEventLoopOptions;
pub use jsruntime::RuntimeOptions;
pub use jsruntime::SharedArrayBufferStore;
Expand Down
5 changes: 3 additions & 2 deletions core/runtime/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::RuntimeOptions;
use crate::cppgc::FunctionTemplateSnapshotData;
use crate::error::CoreError;
use crate::modules::ModuleMapSnapshotData;
use crate::runtime::jsruntime::OpRegistration;

use super::ExtensionTranspiler;

Expand Down Expand Up @@ -202,7 +203,7 @@ pub fn create_snapshot(
startup_snapshot: create_snapshot_options.startup_snapshot,
extensions: create_snapshot_options.extensions,
extension_transpiler: create_snapshot_options.extension_transpiler,
skip_op_registration: create_snapshot_options.skip_op_registration,
op_registration: OpRegistration::SkipAll,
..Default::default()
});

Expand Down Expand Up @@ -235,7 +236,7 @@ pub fn create_snapshot(
let mut js_runtime = JsRuntimeForSnapshot::new(RuntimeOptions {
startup_snapshot: Some(leaked_snapshot),
extensions: warmup_exts,
skip_op_registration: true,
op_registration: OpRegistration::SkipAll,
..Default::default()
});

Expand Down
Loading