mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat(core): allow to specify entry point for snapshotted ES modules (#17771)
This commit adds "ExtensionBuilder::esm_entry_point()" function that allows to specify which of the extension files should be treated as an entry point. If the entry point is not provided all modules are loaded and evaluated, but if it is provided then only the entry point is explicitly loaded and evaluated. Co-authored-by: Leo Kettmeir <crowlkats@toaxl.com>
This commit is contained in:
parent
f917d2e2c1
commit
bd6ddd9b46
4 changed files with 83 additions and 30 deletions
|
@ -43,6 +43,7 @@ impl OpDecl {
|
||||||
pub struct Extension {
|
pub struct Extension {
|
||||||
js_files: Option<Vec<ExtensionFileSource>>,
|
js_files: Option<Vec<ExtensionFileSource>>,
|
||||||
esm_files: Option<Vec<ExtensionFileSource>>,
|
esm_files: Option<Vec<ExtensionFileSource>>,
|
||||||
|
esm_entry_point: Option<&'static str>,
|
||||||
ops: Option<Vec<OpDecl>>,
|
ops: Option<Vec<OpDecl>>,
|
||||||
opstate_fn: Option<Box<OpStateFn>>,
|
opstate_fn: Option<Box<OpStateFn>>,
|
||||||
middleware_fn: Option<Box<OpMiddlewareFn>>,
|
middleware_fn: Option<Box<OpMiddlewareFn>>,
|
||||||
|
@ -100,6 +101,10 @@ impl Extension {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_esm_entry_point(&self) -> Option<&'static str> {
|
||||||
|
self.esm_entry_point
|
||||||
|
}
|
||||||
|
|
||||||
/// Called at JsRuntime startup to initialize ops in the isolate.
|
/// Called at JsRuntime startup to initialize ops in the isolate.
|
||||||
pub fn init_ops(&mut self) -> Option<Vec<OpDecl>> {
|
pub fn init_ops(&mut self) -> Option<Vec<OpDecl>> {
|
||||||
// TODO(@AaronO): maybe make op registration idempotent
|
// TODO(@AaronO): maybe make op registration idempotent
|
||||||
|
@ -158,6 +163,7 @@ impl Extension {
|
||||||
pub struct ExtensionBuilder {
|
pub struct ExtensionBuilder {
|
||||||
js: Vec<ExtensionFileSource>,
|
js: Vec<ExtensionFileSource>,
|
||||||
esm: Vec<ExtensionFileSource>,
|
esm: Vec<ExtensionFileSource>,
|
||||||
|
esm_entry_point: Option<&'static str>,
|
||||||
ops: Vec<OpDecl>,
|
ops: Vec<OpDecl>,
|
||||||
state: Option<Box<OpStateFn>>,
|
state: Option<Box<OpStateFn>>,
|
||||||
middleware: Option<Box<OpMiddlewareFn>>,
|
middleware: Option<Box<OpMiddlewareFn>>,
|
||||||
|
@ -197,6 +203,11 @@ impl ExtensionBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn esm_entry_point(&mut self, entry_point: &'static str) -> &mut Self {
|
||||||
|
self.esm_entry_point = Some(entry_point);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ops(&mut self, ops: Vec<OpDecl>) -> &mut Self {
|
pub fn ops(&mut self, ops: Vec<OpDecl>) -> &mut Self {
|
||||||
self.ops.extend(ops);
|
self.ops.extend(ops);
|
||||||
self
|
self
|
||||||
|
@ -234,6 +245,7 @@ impl ExtensionBuilder {
|
||||||
Extension {
|
Extension {
|
||||||
js_files,
|
js_files,
|
||||||
esm_files,
|
esm_files,
|
||||||
|
esm_entry_point: self.esm_entry_point.take(),
|
||||||
ops,
|
ops,
|
||||||
opstate_fn: self.state.take(),
|
opstate_fn: self.state.take(),
|
||||||
middleware_fn: self.middleware.take(),
|
middleware_fn: self.middleware.take(),
|
||||||
|
|
|
@ -275,21 +275,27 @@ pub struct NoopModuleLoader;
|
||||||
impl ModuleLoader for NoopModuleLoader {
|
impl ModuleLoader for NoopModuleLoader {
|
||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
_specifier: &str,
|
specifier: &str,
|
||||||
_referrer: &str,
|
referrer: &str,
|
||||||
_kind: ResolutionKind,
|
_kind: ResolutionKind,
|
||||||
) -> Result<ModuleSpecifier, Error> {
|
) -> Result<ModuleSpecifier, Error> {
|
||||||
Err(generic_error("Module loading is not supported"))
|
Err(generic_error(
|
||||||
|
format!("Module loading is not supported; attempted to resolve: \"{specifier}\" from \"{referrer}\"")
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(
|
fn load(
|
||||||
&self,
|
&self,
|
||||||
_module_specifier: &ModuleSpecifier,
|
module_specifier: &ModuleSpecifier,
|
||||||
_maybe_referrer: Option<ModuleSpecifier>,
|
maybe_referrer: Option<ModuleSpecifier>,
|
||||||
_is_dyn_import: bool,
|
_is_dyn_import: bool,
|
||||||
) -> Pin<Box<ModuleSourceFuture>> {
|
) -> Pin<Box<ModuleSourceFuture>> {
|
||||||
async { Err(generic_error("Module loading is not supported")) }
|
let err = generic_error(
|
||||||
.boxed_local()
|
format!(
|
||||||
|
"Module loading is not supported; attempted to load: \"{module_specifier}\" from \"{maybe_referrer:?}\"",
|
||||||
|
)
|
||||||
|
);
|
||||||
|
async move { Err(err) }.boxed_local()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2720,14 +2726,20 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
|
||||||
.resolve("file://foo", "file://bar", ResolutionKind::Import)
|
.resolve("file://foo", "file://bar", ResolutionKind::Import)
|
||||||
.err()
|
.err()
|
||||||
.map(|e| e.to_string()),
|
.map(|e| e.to_string()),
|
||||||
Some("Module loading is not supported".to_string())
|
Some(
|
||||||
|
"Module loading is not supported; attempted to resolve: \"file://foo\" from \"file://bar\""
|
||||||
|
.to_string()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
loader
|
loader
|
||||||
.resolve("file://foo", "internal:bar", ResolutionKind::Import)
|
.resolve("file://foo", "internal:bar", ResolutionKind::Import)
|
||||||
.err()
|
.err()
|
||||||
.map(|e| e.to_string()),
|
.map(|e| e.to_string()),
|
||||||
Some("Module loading is not supported".to_string())
|
Some(
|
||||||
|
"Module loading is not supported; attempted to resolve: \"file://foo\" from \"internal:bar\""
|
||||||
|
.to_string()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_helper(
|
resolve_helper(
|
||||||
|
|
|
@ -829,26 +829,39 @@ impl JsRuntime {
|
||||||
|
|
||||||
/// Initializes JS of provided Extensions in the given realm
|
/// Initializes JS of provided Extensions in the given realm
|
||||||
fn init_extension_js(&mut self, realm: &JsRealm) -> Result<(), Error> {
|
fn init_extension_js(&mut self, realm: &JsRealm) -> Result<(), Error> {
|
||||||
|
fn load_and_evaluate_module(
|
||||||
|
runtime: &mut JsRuntime,
|
||||||
|
file_source: &ExtensionFileSource,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
futures::executor::block_on(async {
|
||||||
|
let id = runtime
|
||||||
|
.load_side_module(
|
||||||
|
&ModuleSpecifier::parse(&file_source.specifier)?,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let receiver = runtime.mod_evaluate(id);
|
||||||
|
runtime.run_event_loop(false).await?;
|
||||||
|
receiver.await?
|
||||||
|
})
|
||||||
|
.with_context(|| format!("Couldn't execute '{}'", file_source.specifier))
|
||||||
|
}
|
||||||
|
|
||||||
// Take extensions to avoid double-borrow
|
// Take extensions to avoid double-borrow
|
||||||
let extensions = std::mem::take(&mut self.extensions_with_js);
|
let extensions = std::mem::take(&mut self.extensions_with_js);
|
||||||
for ext in &extensions {
|
for ext in &extensions {
|
||||||
{
|
{
|
||||||
let esm_files = ext.get_esm_sources();
|
let esm_files = ext.get_esm_sources();
|
||||||
for file_source in esm_files {
|
if let Some(entry_point) = ext.get_esm_entry_point() {
|
||||||
futures::executor::block_on(async {
|
let file_source = esm_files
|
||||||
let id = self
|
.iter()
|
||||||
.load_side_module(
|
.find(|file| file.specifier == entry_point)
|
||||||
&ModuleSpecifier::parse(&file_source.specifier)?,
|
.unwrap();
|
||||||
None,
|
load_and_evaluate_module(self, file_source)?;
|
||||||
)
|
} else {
|
||||||
.await?;
|
for file_source in esm_files {
|
||||||
let receiver = self.mod_evaluate(id);
|
load_and_evaluate_module(self, file_source)?;
|
||||||
self.run_event_loop(false).await?;
|
}
|
||||||
receiver.await?
|
|
||||||
})
|
|
||||||
.with_context(|| {
|
|
||||||
format!("Couldn't execute '{}'", file_source.specifier)
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1770,7 +1783,11 @@ impl JsRuntime {
|
||||||
.map(|handle| v8::Local::new(tc_scope, handle))
|
.map(|handle| v8::Local::new(tc_scope, handle))
|
||||||
.expect("ModuleInfo not found");
|
.expect("ModuleInfo not found");
|
||||||
let mut status = module.get_status();
|
let mut status = module.get_status();
|
||||||
assert_eq!(status, v8::ModuleStatus::Instantiated);
|
assert_eq!(
|
||||||
|
status,
|
||||||
|
v8::ModuleStatus::Instantiated,
|
||||||
|
"Module not instantiated {id}"
|
||||||
|
);
|
||||||
|
|
||||||
let (sender, receiver) = oneshot::channel();
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
|
||||||
|
@ -4974,7 +4991,7 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
||||||
_is_dyn_import: bool,
|
_is_dyn_import: bool,
|
||||||
) -> Pin<Box<ModuleSourceFuture>> {
|
) -> Pin<Box<ModuleSourceFuture>> {
|
||||||
let source = r#"
|
let source = r#"
|
||||||
// This module doesn't really exist, just verifying that we'll get
|
// This module doesn't really exist, just verifying that we'll get
|
||||||
// an error when specifier starts with "internal:".
|
// an error when specifier starts with "internal:".
|
||||||
import { core } from "internal:core.js";
|
import { core } from "internal:core.js";
|
||||||
"#;
|
"#;
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub struct CreateSnapshotOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
||||||
|
let start = std::time::Instant::now();
|
||||||
let js_runtime = JsRuntime::new(RuntimeOptions {
|
let js_runtime = JsRuntime::new(RuntimeOptions {
|
||||||
will_snapshot: true,
|
will_snapshot: true,
|
||||||
startup_snapshot: create_snapshot_options.startup_snapshot,
|
startup_snapshot: create_snapshot_options.startup_snapshot,
|
||||||
|
@ -33,7 +34,12 @@ pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
||||||
|
|
||||||
let snapshot = js_runtime.snapshot();
|
let snapshot = js_runtime.snapshot();
|
||||||
let snapshot_slice: &[u8] = &snapshot;
|
let snapshot_slice: &[u8] = &snapshot;
|
||||||
println!("Snapshot size: {}", snapshot_slice.len());
|
println!(
|
||||||
|
"Snapshot size: {}, took {}s ({})",
|
||||||
|
snapshot_slice.len(),
|
||||||
|
start.elapsed().as_secs_f64(),
|
||||||
|
create_snapshot_options.snapshot_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
let maybe_compressed_snapshot: Box<dyn AsRef<[u8]>> =
|
let maybe_compressed_snapshot: Box<dyn AsRef<[u8]>> =
|
||||||
if let Some(compression_cb) = create_snapshot_options.compression_cb {
|
if let Some(compression_cb) = create_snapshot_options.compression_cb {
|
||||||
|
@ -47,7 +53,12 @@ pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
||||||
|
|
||||||
(compression_cb)(&mut vec, snapshot_slice);
|
(compression_cb)(&mut vec, snapshot_slice);
|
||||||
|
|
||||||
println!("Snapshot compressed size: {}", vec.len());
|
println!(
|
||||||
|
"Snapshot compressed size: {}, took {}s ({})",
|
||||||
|
vec.len(),
|
||||||
|
start.elapsed().as_secs_f64(),
|
||||||
|
create_snapshot_options.snapshot_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
Box::new(vec)
|
Box::new(vec)
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,8 +71,9 @@ pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!(
|
println!(
|
||||||
"Snapshot written to: {} ",
|
"Snapshot written to: {}, took: {}s",
|
||||||
create_snapshot_options.snapshot_path.display()
|
create_snapshot_options.snapshot_path.display(),
|
||||||
|
start.elapsed().as_secs_f64()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue