summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bindings.rs158
-rw-r--r--core/extensions.rs16
-rw-r--r--core/ops.rs37
-rw-r--r--core/runtime.rs66
-rw-r--r--core/snapshot_util.rs2
5 files changed, 176 insertions, 103 deletions
diff --git a/core/bindings.rs b/core/bindings.rs
index 7970f9aab..52db40f0d 100644
--- a/core/bindings.rs
+++ b/core/bindings.rs
@@ -19,24 +19,22 @@ use crate::snapshot_util::SnapshotOptions;
use crate::JsRealm;
use crate::JsRuntime;
-pub(crate) fn external_references(
- ops: &[OpCtx],
- snapshot_options: SnapshotOptions,
-) -> v8::ExternalReferences {
- let mut references = vec![
- v8::ExternalReference {
- function: call_console.map_fn_to(),
- },
- v8::ExternalReference {
- function: import_meta_resolve.map_fn_to(),
- },
- v8::ExternalReference {
- function: catch_dynamic_import_promise_error.map_fn_to(),
- },
- v8::ExternalReference {
- function: empty_fn.map_fn_to(),
- },
- ];
+pub(crate) fn external_references(ops: &[OpCtx]) -> v8::ExternalReferences {
+ // Overallocate a bit, it's better than having to resize the vector.
+ let mut references = Vec::with_capacity(4 + ops.len() * 4);
+
+ references.push(v8::ExternalReference {
+ function: call_console.map_fn_to(),
+ });
+ references.push(v8::ExternalReference {
+ function: import_meta_resolve.map_fn_to(),
+ });
+ references.push(v8::ExternalReference {
+ function: catch_dynamic_import_promise_error.map_fn_to(),
+ });
+ references.push(v8::ExternalReference {
+ function: empty_fn.map_fn_to(),
+ });
for ctx in ops {
let ctx_ptr = ctx as *const OpCtx as _;
@@ -44,12 +42,13 @@ pub(crate) fn external_references(
references.push(v8::ExternalReference {
function: ctx.decl.v8_fn_ptr,
});
- if !snapshot_options.will_snapshot() {
- if let Some(fast_fn) = &ctx.decl.fast_fn {
- references.push(v8::ExternalReference {
- pointer: fast_fn.function() as _,
- });
- }
+ if let Some(fast_fn) = &ctx.decl.fast_fn {
+ references.push(v8::ExternalReference {
+ pointer: fast_fn.function() as _,
+ });
+ references.push(v8::ExternalReference {
+ pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _,
+ });
}
}
@@ -114,9 +113,9 @@ pub(crate) fn initialize_context<'s>(
v8::String::new_external_onebyte_static(scope, b"core").unwrap();
let ops_str = v8::String::new_external_onebyte_static(scope, b"ops").unwrap();
- // Snapshot already registered `Deno.core.ops` but
- // extensions may provide ops that aren't part of the snapshot.
- if snapshot_options.loaded() {
+ let ops_obj = if snapshot_options.loaded() {
+ // Snapshot already registered `Deno.core.ops` but
+ // extensions may provide ops that aren't part of the snapshot.
// Grab the Deno.core.ops object & init it
let deno_obj: v8::Local<v8::Object> = global
.get(scope, deno_str.into())
@@ -133,34 +132,58 @@ pub(crate) fn initialize_context<'s>(
.expect("Deno.core.ops to exist")
.try_into()
.unwrap();
- for ctx in op_ctxs {
- add_op_to_deno_core_ops(scope, ops_obj, ctx, snapshot_options);
- }
- return context;
- }
-
- // global.Deno = { core: { } };
- let deno_obj = v8::Object::new(scope);
- global.set(scope, deno_str.into(), deno_obj.into());
-
- let core_obj = v8::Object::new(scope);
- deno_obj.set(scope, core_str.into(), core_obj.into());
-
- // Bind functions to Deno.core.*
- set_func(scope, core_obj, "callConsole", call_console);
-
- // Bind v8 console object to Deno.core.console
- let extra_binding_obj = context.get_extras_binding_object(scope);
- let console_str =
- v8::String::new_external_onebyte_static(scope, b"console").unwrap();
- let console_obj = extra_binding_obj.get(scope, console_str.into()).unwrap();
- core_obj.set(scope, console_str.into(), console_obj);
+ ops_obj
+ } else {
+ // globalThis.Deno = { core: { } };
+ let deno_obj = v8::Object::new(scope);
+ global.set(scope, deno_str.into(), deno_obj.into());
+
+ let core_obj = v8::Object::new(scope);
+ deno_obj.set(scope, core_str.into(), core_obj.into());
+
+ // Bind functions to Deno.core.*
+ set_func(scope, core_obj, "callConsole", call_console);
+
+ // Bind v8 console object to Deno.core.console
+ let extra_binding_obj = context.get_extras_binding_object(scope);
+ let console_str =
+ v8::String::new_external_onebyte_static(scope, b"console").unwrap();
+ let console_obj = extra_binding_obj.get(scope, console_str.into()).unwrap();
+ core_obj.set(scope, console_str.into(), console_obj);
+
+ // Bind functions to Deno.core.ops.*
+ let ops_obj = v8::Object::new(scope);
+ core_obj.set(scope, ops_str.into(), ops_obj.into());
+ ops_obj
+ };
- // Bind functions to Deno.core.ops.*
- let ops_obj = v8::Object::new(scope);
- core_obj.set(scope, ops_str.into(), ops_obj.into());
- for ctx in op_ctxs {
- add_op_to_deno_core_ops(scope, ops_obj, ctx, snapshot_options);
+ if matches!(snapshot_options, SnapshotOptions::Load) {
+ // Only register ops that have `force_registration` flag set to true,
+ // the remaining ones should already be in the snapshot.
+ for op_ctx in op_ctxs
+ .iter()
+ .filter(|op_ctx| op_ctx.decl.force_registration)
+ {
+ add_op_to_deno_core_ops(scope, ops_obj, op_ctx);
+ }
+ } else if matches!(snapshot_options, SnapshotOptions::CreateFromExisting) {
+ // Register all ops, probing for which ones are already registered.
+ for op_ctx in op_ctxs {
+ let key = v8::String::new_external_onebyte_static(
+ scope,
+ op_ctx.decl.name.as_bytes(),
+ )
+ .unwrap();
+ if ops_obj.get(scope, key.into()).is_some() {
+ continue;
+ }
+ add_op_to_deno_core_ops(scope, ops_obj, op_ctx);
+ }
+ } else {
+ // In other cases register all ops unconditionally.
+ for op_ctx in op_ctxs {
+ add_op_to_deno_core_ops(scope, ops_obj, op_ctx);
+ }
}
context
@@ -183,7 +206,6 @@ fn add_op_to_deno_core_ops(
scope: &mut v8::HandleScope<'_>,
obj: v8::Local<v8::Object>,
op_ctx: &OpCtx,
- snapshot_options: SnapshotOptions,
) {
let op_ctx_ptr = op_ctx as *const OpCtx as *const c_void;
let key =
@@ -193,24 +215,14 @@ fn add_op_to_deno_core_ops(
let builder = v8::FunctionTemplate::builder_raw(op_ctx.decl.v8_fn_ptr)
.data(external.into());
- // TODO(bartlomieju): this should be cleaned up once we update Fast Calls API
- // If this is a fast op, we don't want it to be in the snapshot.
- // Only initialize once snapshot is loaded.
- let maybe_fast_fn =
- if op_ctx.decl.fast_fn.is_some() && snapshot_options.loaded() {
- &op_ctx.decl.fast_fn
- } else {
- &None
- };
-
- let templ = if let Some(fast_function) = maybe_fast_fn {
- // Don't initialize fast ops when snapshotting, the external references count mismatch.
- if !snapshot_options.will_snapshot() {
- // TODO(@littledivy): Support fast api overloads in ops.
- builder.build_fast(scope, &**fast_function, None, None, None)
- } else {
- builder.build(scope)
- }
+ let templ = if let Some(fast_function) = &op_ctx.decl.fast_fn {
+ builder.build_fast(
+ scope,
+ &**fast_function,
+ Some(op_ctx.fast_fn_c_info.unwrap().as_ptr()),
+ None,
+ None,
+ )
} else {
builder.build(scope)
};
diff --git a/core/extensions.rs b/core/extensions.rs
index bba37ca3e..801f5f1a6 100644
--- a/core/extensions.rs
+++ b/core/extensions.rs
@@ -53,6 +53,7 @@ pub struct OpDecl {
pub is_unstable: bool,
pub is_v8: bool,
pub fast_fn: Option<Box<dyn FastFunction>>,
+ pub force_registration: bool,
}
impl OpDecl {
@@ -240,6 +241,7 @@ macro_rules! extension {
#[inline(always)]
#[allow(unused_variables)]
+ #[allow(clippy::redundant_closure_call)]
fn with_customizer(ext: &mut $crate::ExtensionBuilder) {
$( ($customizer_fn)(ext); )?
}
@@ -325,6 +327,7 @@ pub struct Extension {
enabled: bool,
name: &'static str,
deps: Option<&'static [&'static str]>,
+ force_op_registration: bool,
}
// Note: this used to be a trait, but we "downgraded" it to a single concrete type
@@ -394,6 +397,7 @@ impl Extension {
let mut ops = self.ops.take()?;
for op in ops.iter_mut() {
op.enabled = self.enabled && op.enabled;
+ op.force_registration = self.force_op_registration;
}
Some(ops)
}
@@ -447,6 +451,7 @@ pub struct ExtensionBuilder {
event_loop_middleware: Option<Box<OpEventLoopFn>>,
name: &'static str,
deps: &'static [&'static str],
+ force_op_registration: bool,
}
impl ExtensionBuilder {
@@ -511,6 +516,15 @@ impl ExtensionBuilder {
self
}
+ /// Mark that ops from this extension should be added to `Deno.core.ops`
+ /// unconditionally. This is useful is some ops are not available
+ /// during snapshotting, as ops are not registered by default when a
+ /// `JsRuntime` is created with an existing snapshot.
+ pub fn force_op_registration(&mut self) -> &mut Self {
+ self.force_op_registration = true;
+ self
+ }
+
/// Consume the [`ExtensionBuilder`] and return an [`Extension`].
pub fn take(self) -> Extension {
let js_files = Some(self.js);
@@ -528,6 +542,7 @@ impl ExtensionBuilder {
initialized: false,
enabled: true,
name: self.name,
+ force_op_registration: self.force_op_registration,
deps,
}
}
@@ -549,6 +564,7 @@ impl ExtensionBuilder {
enabled: true,
name: self.name,
deps,
+ force_op_registration: self.force_op_registration,
}
}
}
diff --git a/core/ops.rs b/core/ops.rs
index ca465c821..3a276082f 100644
--- a/core/ops.rs
+++ b/core/ops.rs
@@ -19,10 +19,13 @@ use std::cell::RefCell;
use std::ops::Deref;
use std::ops::DerefMut;
use std::pin::Pin;
+use std::ptr::NonNull;
use std::rc::Rc;
use std::rc::Weak;
use std::task::Context;
use std::task::Poll;
+use v8::fast_api::CFunctionInfo;
+use v8::fast_api::CTypeInfo;
/// Wrapper around a Future, which causes that Future to be polled immediately.
///
@@ -155,11 +158,45 @@ pub struct OpCtx {
pub id: OpId,
pub state: Rc<RefCell<OpState>>,
pub decl: Rc<OpDecl>,
+ pub fast_fn_c_info: Option<NonNull<v8::fast_api::CFunctionInfo>>,
pub runtime_state: Weak<RefCell<JsRuntimeState>>,
// Index of the current realm into `JsRuntimeState::known_realms`.
pub realm_idx: RealmIdx,
}
+impl OpCtx {
+ pub fn new(
+ id: OpId,
+ realm_idx: RealmIdx,
+ decl: Rc<OpDecl>,
+ state: Rc<RefCell<OpState>>,
+ runtime_state: Weak<RefCell<JsRuntimeState>>,
+ ) -> Self {
+ let mut fast_fn_c_info = None;
+
+ if let Some(fast_fn) = &decl.fast_fn {
+ let args = CTypeInfo::new_from_slice(fast_fn.args());
+ let ret = CTypeInfo::new(fast_fn.return_type());
+
+ // SAFETY: all arguments are coming from the trait and they have
+ // static lifetime
+ let c_fn = unsafe {
+ CFunctionInfo::new(args.as_ptr(), fast_fn.args().len(), ret.as_ptr())
+ };
+ fast_fn_c_info = Some(c_fn);
+ }
+
+ OpCtx {
+ id,
+ state,
+ runtime_state,
+ decl,
+ realm_idx,
+ fast_fn_c_info,
+ }
+ }
+}
+
/// Maintains the resources and ops inside a JS runtime.
pub struct OpState {
pub resource_table: ResourceTable,
diff --git a/core/runtime.rs b/core/runtime.rs
index 0531e861c..054c55470 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -319,6 +319,7 @@ impl JsRuntime {
DENO_INIT.call_once(move || v8_init(v8_platform, options.will_snapshot));
// Add builtins extension
+ // TODO(bartlomieju): remove this in favor of `SnapshotOptions`.
let has_startup_snapshot = options.startup_snapshot.is_some();
if !has_startup_snapshot {
options
@@ -375,12 +376,8 @@ impl JsRuntime {
let op_ctxs = ops
.into_iter()
.enumerate()
- .map(|(id, decl)| OpCtx {
- id,
- state: op_state.clone(),
- runtime_state: weak.clone(),
- decl: Rc::new(decl),
- realm_idx: 0,
+ .map(|(id, decl)| {
+ OpCtx::new(id, 0, Rc::new(decl), op_state.clone(), weak.clone())
})
.collect::<Vec<_>>()
.into_boxed_slice();
@@ -389,7 +386,7 @@ impl JsRuntime {
options.startup_snapshot.is_some(),
options.will_snapshot,
);
- let refs = bindings::external_references(&op_ctxs, snapshot_options);
+ let refs = bindings::external_references(&op_ctxs);
// V8 takes ownership of external_references.
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
let global_context;
@@ -398,7 +395,7 @@ impl JsRuntime {
let (mut isolate, snapshot_options) = if snapshot_options.will_snapshot() {
let snapshot_creator =
snapshot_util::create_snapshot_creator(refs, options.startup_snapshot);
-
+ eprintln!("create snapshot {:#?}", snapshot_options);
let mut isolate = JsRuntime::setup_isolate(snapshot_creator);
{
let scope = &mut v8::HandleScope::new(&mut isolate);
@@ -460,6 +457,7 @@ impl JsRuntime {
isolate_ptr.write(isolate);
isolate_ptr.read()
};
+
global_context.open(&mut isolate).set_slot(
&mut isolate,
Rc::new(RefCell::new(ContextState {
@@ -620,12 +618,14 @@ impl JsRuntime {
.borrow()
.op_ctxs
.iter()
- .map(|op_ctx| OpCtx {
- id: op_ctx.id,
- state: op_ctx.state.clone(),
- decl: op_ctx.decl.clone(),
- runtime_state: op_ctx.runtime_state.clone(),
- realm_idx,
+ .map(|op_ctx| {
+ OpCtx::new(
+ op_ctx.id,
+ realm_idx,
+ op_ctx.decl.clone(),
+ op_ctx.state.clone(),
+ op_ctx.runtime_state.clone(),
+ )
})
.collect();
@@ -927,18 +927,6 @@ impl JsRuntime {
///
/// `Error` can usually be downcast to `JsError`.
pub fn snapshot(mut self) -> v8::StartupData {
- // Nuke Deno.core.ops.* to avoid ExternalReference snapshotting issues
- // TODO(@AaronO): make ops stable across snapshots
- {
- let scope = &mut self.handle_scope();
- let o = Self::eval::<v8::Object>(scope, "Deno.core.ops").unwrap();
- let names = o.get_own_property_names(scope, Default::default()).unwrap();
- for i in 0..names.length() {
- let key = names.get_index(scope, i).unwrap();
- o.delete(scope, key);
- }
- }
-
self.state.borrow_mut().inspector.take();
// Serialize the module map and store its data in the snapshot.
@@ -3556,10 +3544,18 @@ pub mod tests {
}
}
+ #[op]
+ fn op_test() -> Result<String, Error> {
+ Ok(String::from("test"))
+ }
+
let loader = Rc::new(ModsLoader::default());
let mut runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(loader.clone()),
will_snapshot: true,
+ extensions: vec![Extension::builder("text_ext")
+ .ops(vec![op_test::decl()])
+ .build()],
..Default::default()
});
@@ -3594,6 +3590,9 @@ pub mod tests {
module_loader: Some(loader.clone()),
will_snapshot: true,
startup_snapshot: Some(Snapshot::JustCreated(snapshot)),
+ extensions: vec![Extension::builder("text_ext")
+ .ops(vec![op_test::decl()])
+ .build()],
..Default::default()
});
@@ -3609,6 +3608,9 @@ pub mod tests {
let mut runtime3 = JsRuntime::new(RuntimeOptions {
module_loader: Some(loader),
startup_snapshot: Some(Snapshot::JustCreated(snapshot2)),
+ extensions: vec![Extension::builder("text_ext")
+ .ops(vec![op_test::decl()])
+ .build()],
..Default::default()
});
@@ -3616,7 +3618,7 @@ pub mod tests {
let source_code = r#"(async () => {
const mod = await import("file:///400.js");
- return mod.f400();
+ return mod.f400() + " " + Deno.core.ops.op_test();
})();"#
.to_string();
let val = runtime3.execute_script(".", &source_code).unwrap();
@@ -3625,7 +3627,7 @@ pub mod tests {
let scope = &mut runtime3.handle_scope();
let value = v8::Local::new(scope, val);
let str_ = value.to_string(scope).unwrap().to_rust_string_lossy(scope);
- assert_eq!(str_, "hello world");
+ assert_eq!(str_, "hello world test");
}
}
@@ -4600,7 +4602,13 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
Ok(String::from("Test"))
}
- deno_core::extension!(test_ext, ops = [op_test]);
+ deno_core::extension!(
+ test_ext,
+ ops = [op_test],
+ customizer = |ext: &mut deno_core::ExtensionBuilder| {
+ ext.force_op_registration();
+ },
+ );
let mut runtime = JsRuntime::new(RuntimeOptions {
startup_snapshot: Some(Snapshot::Boxed(snapshot)),
extensions: vec![test_ext::init_ops()],
diff --git a/core/snapshot_util.rs b/core/snapshot_util.rs
index 74bd7d8a3..20019f5cc 100644
--- a/core/snapshot_util.rs
+++ b/core/snapshot_util.rs
@@ -121,7 +121,7 @@ fn data_error_to_panic(err: v8::DataError) -> ! {
}
}
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum SnapshotOptions {
Load,
CreateFromExisting,