1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat(core): Ability to create snapshots from existing snapshots (#16597)

Co-authored-by: crowlkats <crowlkats@toaxl.com>
This commit is contained in:
Bartek Iwańczuk 2022-11-21 14:36:26 +01:00 committed by GitHub
parent fedeea6dde
commit d232746928
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1144 additions and 991 deletions

13
Cargo.lock generated
View file

@ -781,25 +781,13 @@ dependencies = [
"data-url", "data-url",
"deno_ast 0.21.0", "deno_ast 0.21.0",
"deno_bench_util", "deno_bench_util",
"deno_broadcast_channel",
"deno_cache",
"deno_console",
"deno_core", "deno_core",
"deno_crypto",
"deno_doc", "deno_doc",
"deno_emit", "deno_emit",
"deno_fetch",
"deno_graph", "deno_graph",
"deno_lint", "deno_lint",
"deno_net",
"deno_node",
"deno_runtime", "deno_runtime",
"deno_task_shell", "deno_task_shell",
"deno_url",
"deno_web",
"deno_webgpu",
"deno_websocket",
"deno_webstorage",
"dissimilar", "dissimilar",
"dotenv", "dotenv",
"dprint-plugin-json", "dprint-plugin-json",
@ -822,6 +810,7 @@ dependencies = [
"libc", "libc",
"log", "log",
"lsp-types", "lsp-types",
"lzzzz",
"mitata", "mitata",
"monch", "monch",
"napi_sym", "napi_sym",

View file

@ -26,25 +26,16 @@ harness = false
path = "./bench/lsp_bench_standalone.rs" path = "./bench/lsp_bench_standalone.rs"
[build-dependencies] [build-dependencies]
deno_broadcast_channel = { version = "0.72.0", path = "../ext/broadcast_channel" } deno_runtime = { version = "0.86.0", path = "../runtime" }
deno_cache = { version = "0.10.0", path = "../ext/cache" }
deno_console = { version = "0.78.0", path = "../ext/console" }
deno_core = { version = "0.160.0", path = "../core" } deno_core = { version = "0.160.0", path = "../core" }
deno_crypto = { version = "0.92.0", path = "../ext/crypto" }
deno_fetch = { version = "0.101.0", path = "../ext/fetch" }
deno_net = { version = "0.70.0", path = "../ext/net" }
deno_node = { version = "0.15.0", path = "../ext/node" }
deno_url = { version = "0.78.0", path = "../ext/url" }
deno_web = { version = "0.109.0", path = "../ext/web" }
deno_webgpu = { version = "0.79.0", path = "../ext/webgpu" }
deno_websocket = { version = "0.83.0", path = "../ext/websocket" }
deno_webstorage = { version = "0.73.0", path = "../ext/webstorage" }
regex = "=1.6.0" regex = "=1.6.0"
serde = { version = "=1.0.144", features = ["derive"] } serde = { version = "=1.0.144", features = ["derive"] }
serde_json = "1.0.64" serde_json = "1.0.64"
zstd = '=0.11.2' zstd = '=0.11.2'
glibc_version = "0.1.2" glibc_version = "0.1.2"
lzzzz = '1.0'
[target.'cfg(windows)'.build-dependencies] [target.'cfg(windows)'.build-dependencies]
winapi = "=0.3.9" winapi = "=0.3.9"
winres = "=0.1.12" winres = "=0.1.12"
@ -86,6 +77,7 @@ jsonc-parser = { version = "=0.21.0", features = ["serde"] }
libc = "=0.2.126" libc = "=0.2.126"
log = { version = "=0.4.17", features = ["serde"] } log = { version = "=0.4.17", features = ["serde"] }
lsp-types = "=0.93.2" # used by tower-lsp and "proposed" feature is unstable in patch releases lsp-types = "=0.93.2" # used by tower-lsp and "proposed" feature is unstable in patch releases
lzzzz = '1.0'
mitata = "=0.0.7" mitata = "=0.0.7"
monch = "=0.4.0" monch = "=0.4.0"
notify = "=5.0.0" notify = "=5.0.0"

View file

@ -1,306 +1,319 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use deno_core::error::custom_error;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::serde::Deserialize;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::Extension; use deno_core::Extension;
use deno_core::JsRuntime;
use deno_core::OpState;
use deno_core::RuntimeOptions;
use regex::Regex;
use std::collections::HashMap;
use std::env; use std::env;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
// TODO(bartlomieju): this module contains a lot of duplicated use deno_core::snapshot_util::*;
// logic with `runtime/build.rs`, factor out to `deno_core`. use deno_runtime::deno_cache::SqliteBackedCache;
fn create_snapshot( use deno_runtime::permissions::Permissions;
mut js_runtime: JsRuntime, use deno_runtime::*;
snapshot_path: &Path,
files: Vec<PathBuf>, mod ts {
) { use super::*;
// TODO(nayeemrmn): https://github.com/rust-lang/cargo/issues/3946 to get the use crate::deno_webgpu_get_declaration;
// workspace root. use deno_core::error::custom_error;
let display_root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap(); use deno_core::error::AnyError;
for file in files { use deno_core::op;
println!("cargo:rerun-if-changed={}", file.display()); use deno_core::OpState;
let display_path = file.strip_prefix(display_root).unwrap(); use regex::Regex;
let display_path_str = display_path.display().to_string(); use serde::Deserialize;
js_runtime use serde_json::json;
.execute_script( use serde_json::Value;
&("deno:".to_string() + &display_path_str.replace('\\', "/")), use std::collections::HashMap;
&std::fs::read_to_string(&file).unwrap(), use std::path::Path;
) use std::path::PathBuf;
.unwrap();
#[derive(Debug, Deserialize)]
struct LoadArgs {
/// The fully qualified specifier that should be loaded.
specifier: String,
} }
let snapshot = js_runtime.snapshot(); pub fn create_compiler_snapshot(
let snapshot_slice: &[u8] = &snapshot; snapshot_path: PathBuf,
println!("Snapshot size: {}", snapshot_slice.len()); files: Vec<PathBuf>,
cwd: &Path,
let compressed_snapshot_with_size = { ) {
let mut vec = vec![]; // libs that are being provided by op crates.
let mut op_crate_libs = HashMap::new();
vec.extend_from_slice( op_crate_libs.insert("deno.cache", deno_cache::get_declaration());
&u32::try_from(snapshot.len()) op_crate_libs.insert("deno.console", deno_console::get_declaration());
.expect("snapshot larger than 4gb") op_crate_libs.insert("deno.url", deno_url::get_declaration());
.to_le_bytes(), op_crate_libs.insert("deno.web", deno_web::get_declaration());
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
op_crate_libs.insert("deno.webgpu", deno_webgpu_get_declaration());
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration());
op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration());
op_crate_libs.insert(
"deno.broadcast_channel",
deno_broadcast_channel::get_declaration(),
); );
op_crate_libs.insert("deno.net", deno_net::get_declaration());
vec.extend_from_slice( // ensure we invalidate the build properly.
&zstd::bulk::compress(snapshot_slice, 22) for (_, path) in op_crate_libs.iter() {
.expect("snapshot compression failed"), println!("cargo:rerun-if-changed={}", path.display());
); }
vec // libs that should be loaded into the isolate before snapshotting.
}; let libs = vec![
// Deno custom type libraries
"deno.window",
"deno.worker",
"deno.shared_globals",
"deno.ns",
"deno.unstable",
// Deno built-in type libraries
"es5",
"es2015.collection",
"es2015.core",
"es2015",
"es2015.generator",
"es2015.iterable",
"es2015.promise",
"es2015.proxy",
"es2015.reflect",
"es2015.symbol",
"es2015.symbol.wellknown",
"es2016.array.include",
"es2016",
"es2017",
"es2017.intl",
"es2017.object",
"es2017.sharedmemory",
"es2017.string",
"es2017.typedarrays",
"es2018.asyncgenerator",
"es2018.asynciterable",
"es2018",
"es2018.intl",
"es2018.promise",
"es2018.regexp",
"es2019.array",
"es2019",
"es2019.object",
"es2019.string",
"es2019.symbol",
"es2020.bigint",
"es2020",
"es2020.date",
"es2020.intl",
"es2020.number",
"es2020.promise",
"es2020.sharedmemory",
"es2020.string",
"es2020.symbol.wellknown",
"es2021",
"es2021.intl",
"es2021.promise",
"es2021.string",
"es2021.weakref",
"es2022",
"es2022.array",
"es2022.error",
"es2022.intl",
"es2022.object",
"es2022.string",
"esnext",
"esnext.array",
"esnext.intl",
];
println!( let path_dts = cwd.join("dts");
"Snapshot compressed size: {}", // ensure we invalidate the build properly.
compressed_snapshot_with_size.len() for name in libs.iter() {
); println!(
"cargo:rerun-if-changed={}",
path_dts.join(format!("lib.{}.d.ts", name)).display()
);
}
std::fs::write(snapshot_path, compressed_snapshot_with_size).unwrap(); // create a copy of the vector that includes any op crate libs to be passed
println!("Snapshot written to: {} ", snapshot_path.display()); // to the JavaScript compiler to build into the snapshot
} let mut build_libs = libs.clone();
for (op_lib, _) in op_crate_libs.iter() {
build_libs.push(op_lib.to_owned());
}
#[derive(Debug, Deserialize)] #[op]
struct LoadArgs { fn op_build_info(state: &mut OpState) -> Value {
/// The fully qualified specifier that should be loaded. let build_specifier = "asset:///bootstrap.ts";
specifier: String, let build_libs = state.borrow::<Vec<&str>>();
} json!({
"buildSpecifier": build_specifier,
"libs": build_libs,
})
}
fn create_compiler_snapshot( #[op]
snapshot_path: &Path, fn op_cwd() -> String {
files: Vec<PathBuf>, "cache:///".into()
cwd: &Path, }
) {
// libs that are being provided by op crates.
let mut op_crate_libs = HashMap::new();
op_crate_libs.insert("deno.cache", deno_cache::get_declaration());
op_crate_libs.insert("deno.console", deno_console::get_declaration());
op_crate_libs.insert("deno.url", deno_url::get_declaration());
op_crate_libs.insert("deno.web", deno_web::get_declaration());
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
op_crate_libs.insert("deno.webgpu", deno_webgpu_get_declaration());
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration());
op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration());
op_crate_libs.insert(
"deno.broadcast_channel",
deno_broadcast_channel::get_declaration(),
);
op_crate_libs.insert("deno.net", deno_net::get_declaration());
// ensure we invalidate the build properly. #[op]
for (_, path) in op_crate_libs.iter() { fn op_exists() -> bool {
println!("cargo:rerun-if-changed={}", path.display()); false
} }
// libs that should be loaded into the isolate before snapshotting. #[op]
let libs = vec![ fn op_is_node_file() -> bool {
// Deno custom type libraries false
"deno.window", }
"deno.worker",
"deno.shared_globals",
"deno.ns",
"deno.unstable",
// Deno built-in type libraries
"es5",
"es2015.collection",
"es2015.core",
"es2015",
"es2015.generator",
"es2015.iterable",
"es2015.promise",
"es2015.proxy",
"es2015.reflect",
"es2015.symbol",
"es2015.symbol.wellknown",
"es2016.array.include",
"es2016",
"es2017",
"es2017.intl",
"es2017.object",
"es2017.sharedmemory",
"es2017.string",
"es2017.typedarrays",
"es2018.asyncgenerator",
"es2018.asynciterable",
"es2018",
"es2018.intl",
"es2018.promise",
"es2018.regexp",
"es2019.array",
"es2019",
"es2019.object",
"es2019.string",
"es2019.symbol",
"es2020.bigint",
"es2020",
"es2020.date",
"es2020.intl",
"es2020.number",
"es2020.promise",
"es2020.sharedmemory",
"es2020.string",
"es2020.symbol.wellknown",
"es2021",
"es2021.intl",
"es2021.promise",
"es2021.string",
"es2021.weakref",
"es2022",
"es2022.array",
"es2022.error",
"es2022.intl",
"es2022.object",
"es2022.string",
"esnext",
"esnext.array",
"esnext.intl",
];
let path_dts = cwd.join("dts"); #[op]
// ensure we invalidate the build properly. fn op_script_version(
for name in libs.iter() { _state: &mut OpState,
println!( _args: Value,
"cargo:rerun-if-changed={}", ) -> Result<Option<String>, AnyError> {
path_dts.join(format!("lib.{}.d.ts", name)).display() Ok(Some("1".to_string()))
); }
}
// create a copy of the vector that includes any op crate libs to be passed #[op]
// to the JavaScript compiler to build into the snapshot // using the same op that is used in `tsc.rs` for loading modules and reading
let mut build_libs = libs.clone(); // files, but a slightly different implementation at build time.
for (op_lib, _) in op_crate_libs.iter() { fn op_load(state: &mut OpState, args: LoadArgs) -> Result<Value, AnyError> {
build_libs.push(op_lib.to_owned()); let op_crate_libs = state.borrow::<HashMap<&str, PathBuf>>();
} let path_dts = state.borrow::<PathBuf>();
let re_asset =
Regex::new(r"asset:/{3}lib\.(\S+)\.d\.ts").expect("bad regex");
let build_specifier = "asset:///bootstrap.ts";
#[op] // we need a basic file to send to tsc to warm it up.
fn op_build_info(state: &mut OpState) -> Value { if args.specifier == build_specifier {
let build_specifier = "asset:///bootstrap.ts";
let build_libs = state.borrow::<Vec<&str>>();
json!({
"buildSpecifier": build_specifier,
"libs": build_libs,
})
}
#[op]
fn op_cwd() -> String {
"cache:///".into()
}
#[op]
fn op_exists() -> bool {
false
}
#[op]
fn op_is_node_file() -> bool {
false
}
#[op]
fn op_script_version(
_state: &mut OpState,
_args: Value,
) -> Result<Option<String>, AnyError> {
Ok(Some("1".to_string()))
}
#[op]
// using the same op that is used in `tsc.rs` for loading modules and reading
// files, but a slightly different implementation at build time.
fn op_load(state: &mut OpState, args: LoadArgs) -> Result<Value, AnyError> {
let op_crate_libs = state.borrow::<HashMap<&str, PathBuf>>();
let path_dts = state.borrow::<PathBuf>();
let re_asset =
Regex::new(r"asset:/{3}lib\.(\S+)\.d\.ts").expect("bad regex");
let build_specifier = "asset:///bootstrap.ts";
// we need a basic file to send to tsc to warm it up.
if args.specifier == build_specifier {
Ok(json!({
"data": r#"console.log("hello deno!");"#,
"version": "1",
// this corresponds to `ts.ScriptKind.TypeScript`
"scriptKind": 3
}))
// specifiers come across as `asset:///lib.{lib_name}.d.ts` and we need to
// parse out just the name so we can lookup the asset.
} else if let Some(caps) = re_asset.captures(&args.specifier) {
if let Some(lib) = caps.get(1).map(|m| m.as_str()) {
// if it comes from an op crate, we were supplied with the path to the
// file.
let path = if let Some(op_crate_lib) = op_crate_libs.get(lib) {
PathBuf::from(op_crate_lib).canonicalize().unwrap()
// otherwise we are will generate the path ourself
} else {
path_dts.join(format!("lib.{}.d.ts", lib))
};
let data = std::fs::read_to_string(path)?;
Ok(json!({ Ok(json!({
"data": data, "data": r#"console.log("hello deno!");"#,
"version": "1", "version": "1",
// this corresponds to `ts.ScriptKind.TypeScript` // this corresponds to `ts.ScriptKind.TypeScript`
"scriptKind": 3 "scriptKind": 3
})) }))
// specifiers come across as `asset:///lib.{lib_name}.d.ts` and we need to
// parse out just the name so we can lookup the asset.
} else if let Some(caps) = re_asset.captures(&args.specifier) {
if let Some(lib) = caps.get(1).map(|m| m.as_str()) {
// if it comes from an op crate, we were supplied with the path to the
// file.
let path = if let Some(op_crate_lib) = op_crate_libs.get(lib) {
PathBuf::from(op_crate_lib).canonicalize().unwrap()
// otherwise we are will generate the path ourself
} else {
path_dts.join(format!("lib.{}.d.ts", lib))
};
let data = std::fs::read_to_string(path)?;
Ok(json!({
"data": data,
"version": "1",
// this corresponds to `ts.ScriptKind.TypeScript`
"scriptKind": 3
}))
} else {
Err(custom_error(
"InvalidSpecifier",
format!("An invalid specifier was requested: {}", args.specifier),
))
}
} else { } else {
Err(custom_error( Err(custom_error(
"InvalidSpecifier", "InvalidSpecifier",
format!("An invalid specifier was requested: {}", args.specifier), format!("An invalid specifier was requested: {}", args.specifier),
)) ))
} }
} else {
Err(custom_error(
"InvalidSpecifier",
format!("An invalid specifier was requested: {}", args.specifier),
))
} }
create_snapshot(CreateSnapshotOptions {
cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
snapshot_path,
startup_snapshot: None,
extensions: vec![Extension::builder()
.ops(vec![
op_build_info::decl(),
op_cwd::decl(),
op_exists::decl(),
op_is_node_file::decl(),
op_load::decl(),
op_script_version::decl(),
])
.state(move |state| {
state.put(op_crate_libs.clone());
state.put(build_libs.clone());
state.put(path_dts.clone());
Ok(())
})
.build()],
additional_files: files,
compression_cb: Some(Box::new(|vec, snapshot_slice| {
vec.extend_from_slice(
&zstd::bulk::compress(snapshot_slice, 22)
.expect("snapshot compression failed"),
);
})),
});
} }
let js_runtime = JsRuntime::new(RuntimeOptions {
will_snapshot: true,
extensions: vec![Extension::builder()
.ops(vec![
op_build_info::decl(),
op_cwd::decl(),
op_exists::decl(),
op_is_node_file::decl(),
op_load::decl(),
op_script_version::decl(),
])
.state(move |state| {
state.put(op_crate_libs.clone());
state.put(build_libs.clone());
state.put(path_dts.clone());
Ok(()) pub(crate) fn version() -> String {
}) std::fs::read_to_string("tsc/00_typescript.js")
.build()], .unwrap()
..Default::default() .lines()
}); .find(|l| l.contains("ts.version = "))
.expect(
create_snapshot(js_runtime, snapshot_path, files); "Failed to find the pattern `ts.version = ` in typescript source code",
)
.chars()
.skip_while(|c| !char::is_numeric(*c))
.take_while(|c| *c != '"')
.collect::<String>()
}
} }
fn ts_version() -> String { fn create_cli_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
std::fs::read_to_string("tsc/00_typescript.js") let extensions: Vec<Extension> = vec![
.unwrap() deno_webidl::init(),
.lines() deno_console::init(),
.find(|l| l.contains("ts.version = ")) deno_url::init(),
.expect( deno_tls::init(),
"Failed to find the pattern `ts.version = ` in typescript source code", deno_web::init::<Permissions>(
) deno_web::BlobStore::default(),
.chars() Default::default(),
.skip_while(|c| !char::is_numeric(*c)) ),
.take_while(|c| *c != '"') deno_fetch::init::<Permissions>(Default::default()),
.collect::<String>() deno_cache::init::<SqliteBackedCache>(None),
deno_websocket::init::<Permissions>("".to_owned(), None, None),
deno_webstorage::init(None),
deno_crypto::init(None),
deno_webgpu::init(false),
deno_broadcast_channel::init(
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
false, // No --unstable.
),
deno_node::init::<Permissions>(None), // No --unstable.
deno_ffi::init::<Permissions>(false),
deno_net::init::<Permissions>(
None, false, // No --unstable.
None,
),
deno_napi::init::<Permissions>(false),
deno_http::init(),
deno_flash::init::<Permissions>(false), // No --unstable
];
create_snapshot(CreateSnapshotOptions {
cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
snapshot_path,
startup_snapshot: Some(deno_runtime::js::deno_isolate_init()),
extensions,
additional_files: files,
compression_cb: Some(Box::new(|vec, snapshot_slice| {
lzzzz::lz4_hc::compress_to_vec(
snapshot_slice,
vec,
lzzzz::lz4_hc::CLEVEL_MAX,
)
.expect("snapshot compression failed");
})),
})
} }
fn git_commit_hash() -> String { fn git_commit_hash() -> String {
@ -386,7 +399,7 @@ fn main() {
println!("cargo:rustc-env=GIT_COMMIT_HASH={}", git_commit_hash()); println!("cargo:rustc-env=GIT_COMMIT_HASH={}", git_commit_hash());
println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH"); println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH");
println!("cargo:rustc-env=TS_VERSION={}", ts_version()); println!("cargo:rustc-env=TS_VERSION={}", ts::version());
println!("cargo:rerun-if-env-changed=TS_VERSION"); println!("cargo:rerun-if-env-changed=TS_VERSION");
println!( println!(
@ -440,11 +453,14 @@ fn main() {
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
// Main snapshot
let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin"); let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
let js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "tsc");
ts::create_compiler_snapshot(compiler_snapshot_path, js_files, &c);
let js_files = get_js_files("tsc"); let cli_snapshot_path = o.join("CLI_SNAPSHOT.bin");
create_compiler_snapshot(&compiler_snapshot_path, js_files, &c); let mut js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js");
js_files.push(deno_runtime::js::get_99_main());
create_cli_snapshot(cli_snapshot_path, js_files);
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
@ -462,17 +478,3 @@ fn deno_webgpu_get_declaration() -> PathBuf {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
manifest_dir.join("dts").join("lib.deno_webgpu.d.ts") manifest_dir.join("dts").join("lib.deno_webgpu.d.ts")
} }
fn get_js_files(d: &str) -> Vec<PathBuf> {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let mut js_files = std::fs::read_dir(d)
.unwrap()
.map(|dir_entry| {
let file = dir_entry.unwrap();
manifest_dir.join(file.path())
})
.filter(|path| path.extension().unwrap_or_default() == "js")
.collect::<Vec<PathBuf>>();
js_files.sort();
js_files
}

57
cli/js.rs Normal file
View file

@ -0,0 +1,57 @@
use deno_core::Snapshot;
use log::debug;
use once_cell::sync::Lazy;
pub static CLI_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
#[allow(clippy::uninit_vec)]
#[cold]
#[inline(never)]
|| {
static COMPRESSED_CLI_SNAPSHOT: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin"));
let size =
u32::from_le_bytes(COMPRESSED_CLI_SNAPSHOT[0..4].try_into().unwrap())
as usize;
let mut vec = Vec::with_capacity(size);
// SAFETY: vec is allocated with exact snapshot size (+ alignment)
// SAFETY: non zeroed bytes are overwritten with decompressed snapshot
unsafe {
vec.set_len(size);
}
lzzzz::lz4::decompress(&COMPRESSED_CLI_SNAPSHOT[4..], &mut vec).unwrap();
vec.into_boxed_slice()
},
);
pub fn deno_isolate_init() -> Snapshot {
debug!("Deno isolate init with snapshots.");
Snapshot::Static(&CLI_SNAPSHOT)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn runtime_snapshot() {
let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
startup_snapshot: Some(deno_isolate_init()),
..Default::default()
});
js_runtime
.execute_script(
"<anon>",
r#"
if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) {
throw Error("bad");
}
console.log("we have console.log!!!");
"#,
)
.unwrap();
}
}

View file

@ -1397,12 +1397,16 @@
return testFn; return testFn;
} }
window.__bootstrap.testing = { window.__bootstrap.internals = {
bench, ...window.__bootstrap.internals ?? {},
enableBench, testing: {
enableTest, runTests,
runBenchmarks, runBenchmarks,
runTests, enableTest,
test, enableBench,
},
}; };
window.__bootstrap.denoNs.bench = bench;
window.__bootstrap.denoNs.test = test;
})(this); })(this);

View file

@ -7,7 +7,6 @@ use super::lsp_custom;
use crate::args::flags_from_vec; use crate::args::flags_from_vec;
use crate::args::DenoSubcommand; use crate::args::DenoSubcommand;
use crate::checksum; use crate::checksum;
use crate::create_main_worker;
use crate::lsp::client::Client; use crate::lsp::client::Client;
use crate::lsp::client::TestingNotification; use crate::lsp::client::TestingNotification;
use crate::lsp::config; use crate::lsp::config;
@ -16,6 +15,7 @@ use crate::ops;
use crate::proc_state; use crate::proc_state;
use crate::tools::test; use crate::tools::test;
use crate::tools::test::TestEventSender; use crate::tools::test::TestEventSender;
use crate::worker::create_main_worker_for_test_or_bench;
use deno_core::anyhow::anyhow; use deno_core::anyhow::anyhow;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -154,7 +154,7 @@ async fn test_specifier(
filter: test::TestFilter, filter: test::TestFilter,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
if !token.is_cancelled() { if !token.is_cancelled() {
let mut worker = create_main_worker( let mut worker = create_main_worker_for_test_or_bench(
&ps, &ps,
specifier.clone(), specifier.clone(),
permissions, permissions,

View file

@ -18,6 +18,7 @@ mod fs_util;
mod graph_util; mod graph_util;
mod http_cache; mod http_cache;
mod http_util; mod http_util;
mod js;
mod lockfile; mod lockfile;
mod logger; mod logger;
mod lsp; mod lsp;
@ -289,14 +290,8 @@ async fn eval_command(
resolve_url_or_path(&format!("./$deno$eval.{}", eval_flags.ext))?; resolve_url_or_path(&format!("./$deno$eval.{}", eval_flags.ext))?;
let permissions = Permissions::from_options(&flags.permissions_options())?; let permissions = Permissions::from_options(&flags.permissions_options())?;
let ps = ProcState::build(flags).await?; let ps = ProcState::build(flags).await?;
let mut worker = create_main_worker( let mut worker =
&ps, create_main_worker(&ps, main_module.clone(), permissions).await?;
main_module.clone(),
permissions,
vec![],
Default::default(),
)
.await?;
// Create a dummy source file. // Create a dummy source file.
let source_code = if eval_flags.print { let source_code = if eval_flags.print {
format!("console.log({})", eval_flags.code) format!("console.log({})", eval_flags.code)
@ -602,8 +597,6 @@ async fn repl_command(
&ps, &ps,
main_module.clone(), main_module.clone(),
Permissions::from_options(&ps.options.permissions_options())?, Permissions::from_options(&ps.options.permissions_options())?,
vec![],
Default::default(),
) )
.await?; .await?;
worker.setup_repl().await?; worker.setup_repl().await?;
@ -623,8 +616,6 @@ async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> {
&ps.clone(), &ps.clone(),
main_module.clone(), main_module.clone(),
Permissions::from_options(&ps.options.permissions_options())?, Permissions::from_options(&ps.options.permissions_options())?,
vec![],
Default::default(),
) )
.await?; .await?;
@ -664,14 +655,8 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
let ps = let ps =
ProcState::build_for_file_watcher((*flags).clone(), sender.clone()) ProcState::build_for_file_watcher((*flags).clone(), sender.clone())
.await?; .await?;
let worker = create_main_worker( let worker =
&ps, create_main_worker(&ps, main_module.clone(), permissions).await?;
main_module.clone(),
permissions,
vec![],
Default::default(),
)
.await?;
worker.run_for_watcher().await?; worker.run_for_watcher().await?;
Ok(()) Ok(())
@ -722,14 +707,8 @@ async fn run_command(
}; };
let permissions = let permissions =
Permissions::from_options(&ps.options.permissions_options())?; Permissions::from_options(&ps.options.permissions_options())?;
let mut worker = create_main_worker( let mut worker =
&ps, create_main_worker(&ps, main_module.clone(), permissions).await?;
main_module.clone(),
permissions,
vec![],
Default::default(),
)
.await?;
let exit_code = worker.run().await?; let exit_code = worker.run().await?;
Ok(exit_code) Ok(exit_code)

View file

@ -287,7 +287,7 @@ pub async fn run(
inspect: ps.options.is_inspecting(), inspect: ps.options.is_inspecting(),
}, },
extensions: ops::cli_exts(ps.clone()), extensions: ops::cli_exts(ps.clone()),
startup_snapshot: None, startup_snapshot: Some(crate::js::deno_isolate_init()),
unsafely_ignore_certificate_errors: metadata unsafely_ignore_certificate_errors: metadata
.unsafely_ignore_certificate_errors, .unsafely_ignore_certificate_errors,
root_cert_store: Some(root_cert_store), root_cert_store: Some(root_cert_store),

View file

@ -37,13 +37,13 @@ failing step in failing test ... FAILED ([WILDCARD])
nested failure => ./test/steps/failing_steps.ts:[WILDCARD] nested failure => ./test/steps/failing_steps.ts:[WILDCARD]
error: Error: 1 test step failed. error: Error: 1 test step failed.
at runTest (deno:runtime/js/40_testing.js:[WILDCARD]) at runTest (deno:cli/js/40_testing.js:[WILDCARD])
at async runTests (deno:runtime/js/40_testing.js:[WILDCARD]) at async runTests (deno:cli/js/40_testing.js:[WILDCARD])
multiple test step failures => ./test/steps/failing_steps.ts:[WILDCARD] multiple test step failures => ./test/steps/failing_steps.ts:[WILDCARD]
error: Error: 2 test steps failed. error: Error: 2 test steps failed.
at runTest (deno:runtime/js/40_testing.js:[WILDCARD]) at runTest (deno:cli/js/40_testing.js:[WILDCARD])
at async runTests (deno:runtime/js/40_testing.js:[WILDCARD]) at async runTests (deno:cli/js/40_testing.js:[WILDCARD])
failing step in failing test => ./test/steps/failing_steps.ts:[WILDCARD] failing step in failing test => ./test/steps/failing_steps.ts:[WILDCARD]
error: Error: Fail test. error: Error: Fail test.

View file

@ -4,7 +4,6 @@ use crate::args::BenchFlags;
use crate::args::Flags; use crate::args::Flags;
use crate::args::TypeCheckMode; use crate::args::TypeCheckMode;
use crate::colors; use crate::colors;
use crate::create_main_worker;
use crate::file_watcher; use crate::file_watcher;
use crate::file_watcher::ResolutionResult; use crate::file_watcher::ResolutionResult;
use crate::fs_util::collect_specifiers; use crate::fs_util::collect_specifiers;
@ -15,6 +14,7 @@ use crate::ops;
use crate::proc_state::ProcState; use crate::proc_state::ProcState;
use crate::tools::test::format_test_error; use crate::tools::test::format_test_error;
use crate::tools::test::TestFilter; use crate::tools::test::TestFilter;
use crate::worker::create_main_worker_for_test_or_bench;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -352,7 +352,7 @@ async fn bench_specifier(
options: BenchSpecifierOptions, options: BenchSpecifierOptions,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let filter = TestFilter::from_flag(&options.filter); let filter = TestFilter::from_flag(&options.filter);
let mut worker = create_main_worker( let mut worker = create_main_worker_for_test_or_bench(
&ps, &ps,
specifier.clone(), specifier.clone(),
permissions, permissions,

View file

@ -5,7 +5,6 @@ use crate::args::TestFlags;
use crate::args::TypeCheckMode; use crate::args::TypeCheckMode;
use crate::checksum; use crate::checksum;
use crate::colors; use crate::colors;
use crate::create_main_worker;
use crate::display; use crate::display;
use crate::file_fetcher::File; use crate::file_fetcher::File;
use crate::file_watcher; use crate::file_watcher;
@ -18,6 +17,7 @@ use crate::graph_util::contains_specifier;
use crate::graph_util::graph_valid; use crate::graph_util::graph_valid;
use crate::ops; use crate::ops;
use crate::proc_state::ProcState; use crate::proc_state::ProcState;
use crate::worker::create_main_worker_for_test_or_bench;
use deno_ast::swc::common::comments::CommentKind; use deno_ast::swc::common::comments::CommentKind;
use deno_ast::MediaType; use deno_ast::MediaType;
@ -715,7 +715,7 @@ async fn test_specifier(
sender: &TestEventSender, sender: &TestEventSender,
options: TestSpecifierOptions, options: TestSpecifierOptions,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let mut worker = create_main_worker( let mut worker = create_main_worker_for_test_or_bench(
&ps, &ps,
specifier.clone(), specifier.clone(),
permissions, permissions,

View file

@ -7,6 +7,9 @@ use deno_core::error::AnyError;
use deno_core::futures::task::LocalFutureObj; use deno_core::futures::task::LocalFutureObj;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::located_script_name; use deno_core::located_script_name;
use deno_core::serde_json::json;
use deno_core::serde_v8;
use deno_core::v8;
use deno_core::Extension; use deno_core::Extension;
use deno_core::ModuleId; use deno_core::ModuleId;
use deno_runtime::colors; use deno_runtime::colors;
@ -38,6 +41,11 @@ pub struct CliMainWorker {
is_main_cjs: bool, is_main_cjs: bool,
worker: MainWorker, worker: MainWorker,
ps: ProcState, ps: ProcState,
js_run_tests_callback: Option<v8::Global<v8::Function>>,
js_run_benchmarks_callback: Option<v8::Global<v8::Function>>,
js_enable_test_callback: Option<v8::Global<v8::Function>>,
js_enable_bench_callback: Option<v8::Global<v8::Function>>,
} }
impl CliMainWorker { impl CliMainWorker {
@ -168,7 +176,7 @@ impl CliMainWorker {
&mut self, &mut self,
mode: TestMode, mode: TestMode,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
self.worker.enable_test(); self.enable_test();
// Enable op call tracing in core to enable better debugging of op sanitizer // Enable op call tracing in core to enable better debugging of op sanitizer
// failures. // failures.
@ -194,10 +202,7 @@ impl CliMainWorker {
} }
self.worker.dispatch_load_event(&located_script_name!())?; self.worker.dispatch_load_event(&located_script_name!())?;
self self.run_tests(&self.ps.options.shuffle_tests()).await?;
.worker
.run_tests(&self.ps.options.shuffle_tests())
.await?;
loop { loop {
if !self if !self
.worker .worker
@ -223,7 +228,7 @@ impl CliMainWorker {
&mut self, &mut self,
mode: TestMode, mode: TestMode,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
self.worker.enable_test(); self.enable_test();
self self
.worker .worker
@ -239,7 +244,7 @@ impl CliMainWorker {
} }
self.worker.dispatch_load_event(&located_script_name!())?; self.worker.dispatch_load_event(&located_script_name!())?;
self.worker.run_tests(&None).await?; self.run_tests(&None).await?;
loop { loop {
if !self if !self
.worker .worker
@ -254,13 +259,13 @@ impl CliMainWorker {
} }
pub async fn run_bench_specifier(&mut self) -> Result<(), AnyError> { pub async fn run_bench_specifier(&mut self) -> Result<(), AnyError> {
self.worker.enable_bench(); self.enable_bench();
// We execute the module module as a side module so that import.meta.main is not set. // We execute the module module as a side module so that import.meta.main is not set.
self.execute_side_module_possibly_with_npm().await?; self.execute_side_module_possibly_with_npm().await?;
self.worker.dispatch_load_event(&located_script_name!())?; self.worker.dispatch_load_event(&located_script_name!())?;
self.worker.run_benchmarks().await?; self.run_benchmarks().await?;
loop { loop {
if !self if !self
.worker .worker
@ -340,14 +345,104 @@ impl CliMainWorker {
Ok(None) Ok(None)
} }
} }
/// Run tests declared with `Deno.test()`. Test events will be dispatched
/// by calling ops which are currently only implemented in the CLI crate.
pub async fn run_tests(
&mut self,
shuffle: &Option<u64>,
) -> Result<(), AnyError> {
let promise = {
let scope = &mut self.worker.js_runtime.handle_scope();
let cb = self.js_run_tests_callback.as_ref().unwrap().open(scope);
let this = v8::undefined(scope).into();
let options =
serde_v8::to_v8(scope, json!({ "shuffle": shuffle })).unwrap();
let promise = cb.call(scope, this, &[options]).unwrap();
v8::Global::new(scope, promise)
};
self.worker.js_runtime.resolve_value(promise).await?;
Ok(())
}
/// Run benches declared with `Deno.bench()`. Bench events will be dispatched
/// by calling ops which are currently only implemented in the CLI crate.
pub async fn run_benchmarks(&mut self) -> Result<(), AnyError> {
let promise = {
let scope = &mut self.worker.js_runtime.handle_scope();
let cb = self
.js_run_benchmarks_callback
.as_ref()
.unwrap()
.open(scope);
let this = v8::undefined(scope).into();
let promise = cb.call(scope, this, &[]).unwrap();
v8::Global::new(scope, promise)
};
self.worker.js_runtime.resolve_value(promise).await?;
Ok(())
}
/// Enable `Deno.test()`. If this isn't called before executing user code,
/// `Deno.test()` calls will noop.
pub fn enable_test(&mut self) {
let scope = &mut self.worker.js_runtime.handle_scope();
let cb = self.js_enable_test_callback.as_ref().unwrap().open(scope);
let this = v8::undefined(scope).into();
cb.call(scope, this, &[]).unwrap();
}
/// Enable `Deno.bench()`. If this isn't called before executing user code,
/// `Deno.bench()` calls will noop.
pub fn enable_bench(&mut self) {
let scope = &mut self.worker.js_runtime.handle_scope();
let cb = self.js_enable_bench_callback.as_ref().unwrap().open(scope);
let this = v8::undefined(scope).into();
cb.call(scope, this, &[]).unwrap();
}
} }
pub async fn create_main_worker( pub async fn create_main_worker(
ps: &ProcState, ps: &ProcState,
main_module: ModuleSpecifier, main_module: ModuleSpecifier,
permissions: Permissions, permissions: Permissions,
) -> Result<CliMainWorker, AnyError> {
create_main_worker_internal(
ps,
main_module,
permissions,
vec![],
Default::default(),
false,
)
.await
}
pub async fn create_main_worker_for_test_or_bench(
ps: &ProcState,
main_module: ModuleSpecifier,
permissions: Permissions,
custom_extensions: Vec<Extension>,
stdio: deno_runtime::ops::io::Stdio,
) -> Result<CliMainWorker, AnyError> {
create_main_worker_internal(
ps,
main_module,
permissions,
custom_extensions,
stdio,
true,
)
.await
}
async fn create_main_worker_internal(
ps: &ProcState,
main_module: ModuleSpecifier,
permissions: Permissions,
mut custom_extensions: Vec<Extension>, mut custom_extensions: Vec<Extension>,
stdio: deno_runtime::ops::io::Stdio, stdio: deno_runtime::ops::io::Stdio,
bench_or_test: bool,
) -> Result<CliMainWorker, AnyError> { ) -> Result<CliMainWorker, AnyError> {
let (main_module, is_main_cjs) = if let Ok(package_ref) = let (main_module, is_main_cjs) = if let Ok(package_ref) =
NpmPackageReference::from_specifier(&main_module) NpmPackageReference::from_specifier(&main_module)
@ -426,7 +521,7 @@ pub async fn create_main_worker(
inspect: ps.options.is_inspecting(), inspect: ps.options.is_inspecting(),
}, },
extensions, extensions,
startup_snapshot: None, startup_snapshot: Some(crate::js::deno_isolate_init()),
unsafely_ignore_certificate_errors: ps unsafely_ignore_certificate_errors: ps
.options .options
.unsafely_ignore_certificate_errors() .unsafely_ignore_certificate_errors()
@ -452,16 +547,59 @@ pub async fn create_main_worker(
stdio, stdio,
}; };
let worker = MainWorker::bootstrap_from_options( let mut worker = MainWorker::bootstrap_from_options(
main_module.clone(), main_module.clone(),
permissions, permissions,
options, options,
); );
let (
js_run_tests_callback,
js_run_benchmarks_callback,
js_enable_test_callback,
js_enable_bench_callback,
) = if bench_or_test {
let scope = &mut worker.js_runtime.handle_scope();
let js_run_tests_callback = deno_core::JsRuntime::eval::<v8::Function>(
scope,
"Deno[Deno.internal].testing.runTests",
)
.unwrap();
let js_run_benchmarks_callback =
deno_core::JsRuntime::eval::<v8::Function>(
scope,
"Deno[Deno.internal].testing.runBenchmarks",
)
.unwrap();
let js_enable_tests_callback = deno_core::JsRuntime::eval::<v8::Function>(
scope,
"Deno[Deno.internal].testing.enableTest",
)
.unwrap();
let js_enable_bench_callback = deno_core::JsRuntime::eval::<v8::Function>(
scope,
"Deno[Deno.internal].testing.enableBench",
)
.unwrap();
(
Some(v8::Global::new(scope, js_run_tests_callback)),
Some(v8::Global::new(scope, js_run_benchmarks_callback)),
Some(v8::Global::new(scope, js_enable_tests_callback)),
Some(v8::Global::new(scope, js_enable_bench_callback)),
)
} else {
(None, None, None, None)
};
Ok(CliMainWorker { Ok(CliMainWorker {
main_module, main_module,
is_main_cjs, is_main_cjs,
worker, worker,
ps: ps.clone(), ps: ps.clone(),
js_run_tests_callback,
js_run_benchmarks_callback,
js_enable_test_callback,
js_enable_bench_callback,
}) })
} }
@ -544,6 +682,7 @@ fn create_web_worker_callback(
inspect: ps.options.is_inspecting(), inspect: ps.options.is_inspecting(),
}, },
extensions, extensions,
startup_snapshot: Some(crate::js::deno_isolate_init()),
unsafely_ignore_certificate_errors: ps unsafely_ignore_certificate_errors: ps
.options .options
.unsafely_ignore_certificate_errors() .unsafely_ignore_certificate_errors()
@ -577,3 +716,109 @@ fn create_web_worker_callback(
) )
}) })
} }
#[cfg(test)]
mod tests {
use super::*;
use deno_core::{resolve_url_or_path, FsModuleLoader};
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_web::BlobStore;
fn create_test_worker() -> MainWorker {
let main_module = resolve_url_or_path("./hello.js").unwrap();
let permissions = Permissions::default();
let options = WorkerOptions {
bootstrap: BootstrapOptions {
args: vec![],
cpu_count: 1,
debug_flag: false,
enable_testing_features: false,
locale: deno_core::v8::icu::get_language_tag(),
location: None,
no_color: true,
is_tty: false,
runtime_version: "x".to_string(),
ts_version: "x".to_string(),
unstable: false,
user_agent: "x".to_string(),
inspect: false,
},
extensions: vec![],
startup_snapshot: Some(crate::js::deno_isolate_init()),
unsafely_ignore_certificate_errors: None,
root_cert_store: None,
seed: None,
format_js_error_fn: None,
source_map_getter: None,
web_worker_preload_module_cb: Arc::new(|_| unreachable!()),
web_worker_pre_execute_module_cb: Arc::new(|_| unreachable!()),
create_web_worker_cb: Arc::new(|_| unreachable!()),
maybe_inspector_server: None,
should_break_on_first_statement: false,
module_loader: Rc::new(FsModuleLoader),
npm_resolver: None,
get_error_class_fn: None,
cache_storage_dir: None,
origin_storage_dir: None,
blob_store: BlobStore::default(),
broadcast_channel: InMemoryBroadcastChannel::default(),
shared_array_buffer_store: None,
compiled_wasm_module_store: None,
stdio: Default::default(),
};
MainWorker::bootstrap_from_options(main_module, permissions, options)
}
#[tokio::test]
async fn execute_mod_esm_imports_a() {
let p = test_util::testdata_path().join("runtime/esm_imports_a.js");
let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
let mut worker = create_test_worker();
let result = worker.execute_main_module(&module_specifier).await;
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
}
if let Err(e) = worker.run_event_loop(false).await {
panic!("Future got unexpected error: {:?}", e);
}
}
#[tokio::test]
async fn execute_mod_circular() {
let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("tests/circular1.js");
let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
let mut worker = create_test_worker();
let result = worker.execute_main_module(&module_specifier).await;
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
}
if let Err(e) = worker.run_event_loop(false).await {
panic!("Future got unexpected error: {:?}", e);
}
}
#[tokio::test]
async fn execute_mod_resolve_error() {
// "foo" is not a valid module specifier so this should return an error.
let mut worker = create_test_worker();
let module_specifier = resolve_url_or_path("does-not-exist").unwrap();
let result = worker.execute_main_module(&module_specifier).await;
assert!(result.is_err());
}
#[tokio::test]
async fn execute_mod_002_hello() {
// This assumes cwd is project root (an assumption made throughout the
// tests).
let mut worker = create_test_worker();
let p = test_util::testdata_path().join("run/001_hello.js");
let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
let result = worker.execute_main_module(&module_specifier).await;
assert!(result.is_ok());
}
}

View file

@ -18,6 +18,7 @@ mod ops_builtin_v8;
mod ops_metrics; mod ops_metrics;
mod resources; mod resources;
mod runtime; mod runtime;
pub mod snapshot_util;
mod source_map; mod source_map;
// Re-exports // Re-exports

95
core/snapshot_util.rs Normal file
View file

@ -0,0 +1,95 @@
use crate::Extension;
use crate::JsRuntime;
use crate::RuntimeOptions;
use crate::Snapshot;
use std::path::Path;
use std::path::PathBuf;
pub type CompressionCb = dyn Fn(&mut Vec<u8>, &[u8]);
pub struct CreateSnapshotOptions {
pub cargo_manifest_dir: &'static str,
pub snapshot_path: PathBuf,
pub startup_snapshot: Option<Snapshot>,
pub extensions: Vec<Extension>,
pub additional_files: Vec<PathBuf>,
pub compression_cb: Option<Box<CompressionCb>>,
}
pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
let mut js_runtime = JsRuntime::new(RuntimeOptions {
will_snapshot: true,
startup_snapshot: create_snapshot_options.startup_snapshot,
extensions: create_snapshot_options.extensions,
..Default::default()
});
// TODO(nayeemrmn): https://github.com/rust-lang/cargo/issues/3946 to get the
// workspace root.
let display_root = Path::new(create_snapshot_options.cargo_manifest_dir)
.parent()
.unwrap();
for file in create_snapshot_options.additional_files {
let display_path = file.strip_prefix(display_root).unwrap_or(&file);
let display_path_str = display_path.display().to_string();
js_runtime
.execute_script(
&("deno:".to_string() + &display_path_str.replace('\\', "/")),
&std::fs::read_to_string(&file).unwrap(),
)
.unwrap();
}
let snapshot = js_runtime.snapshot();
let snapshot_slice: &[u8] = &snapshot;
println!("Snapshot size: {}", snapshot_slice.len());
let maybe_compressed_snapshot: Box<dyn AsRef<[u8]>> =
if let Some(compression_cb) = create_snapshot_options.compression_cb {
let mut vec = vec![];
vec.extend_from_slice(
&u32::try_from(snapshot.len())
.expect("snapshot larger than 4gb")
.to_le_bytes(),
);
(compression_cb)(&mut vec, snapshot_slice);
println!("Snapshot compressed size: {}", vec.len());
Box::new(vec)
} else {
Box::new(snapshot_slice)
};
std::fs::write(
&create_snapshot_options.snapshot_path,
&*maybe_compressed_snapshot,
)
.unwrap();
println!(
"Snapshot written to: {} ",
create_snapshot_options.snapshot_path.display()
);
}
pub fn get_js_files(
cargo_manifest_dir: &'static str,
directory: &str,
) -> Vec<PathBuf> {
let manifest_dir = Path::new(cargo_manifest_dir);
let mut js_files = std::fs::read_dir(directory)
.unwrap()
.map(|dir_entry| {
let file = dir_entry.unwrap();
manifest_dir.join(file.path())
})
.filter(|path| {
path.extension().unwrap_or_default() == "js"
&& !path.ends_with("99_main.js")
})
.collect::<Vec<PathBuf>>();
js_files.sort();
js_files
}

View file

@ -9,63 +9,8 @@ use std::path::PathBuf;
mod not_docs { mod not_docs {
use super::*; use super::*;
use deno_cache::SqliteBackedCache; use deno_cache::SqliteBackedCache;
use deno_core::snapshot_util::*;
use deno_core::Extension; use deno_core::Extension;
use deno_core::JsRuntime;
use deno_core::RuntimeOptions;
// TODO(bartlomieju): this module contains a lot of duplicated
// logic with `cli/build.rs`, factor out to `deno_core`.
fn create_snapshot(
mut js_runtime: JsRuntime,
snapshot_path: &Path,
files: Vec<PathBuf>,
) {
// TODO(nayeemrmn): https://github.com/rust-lang/cargo/issues/3946 to get the
// workspace root.
let display_root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
for file in files {
println!("cargo:rerun-if-changed={}", file.display());
let display_path = file.strip_prefix(display_root).unwrap();
let display_path_str = display_path.display().to_string();
js_runtime
.execute_script(
&("deno:".to_string() + &display_path_str.replace('\\', "/")),
&std::fs::read_to_string(&file).unwrap(),
)
.unwrap();
}
let snapshot = js_runtime.snapshot();
let snapshot_slice: &[u8] = &snapshot;
println!("Snapshot size: {}", snapshot_slice.len());
let compressed_snapshot_with_size = {
let mut vec = vec![];
vec.extend_from_slice(
&u32::try_from(snapshot.len())
.expect("snapshot larger than 4gb")
.to_le_bytes(),
);
lzzzz::lz4_hc::compress_to_vec(
snapshot_slice,
&mut vec,
lzzzz::lz4_hc::CLEVEL_MAX,
)
.expect("snapshot compression failed");
vec
};
println!(
"Snapshot compressed size: {}",
compressed_snapshot_with_size.len()
);
std::fs::write(snapshot_path, compressed_snapshot_with_size).unwrap();
println!("Snapshot written to: {} ", snapshot_path.display());
}
struct Permissions; struct Permissions;
@ -174,7 +119,7 @@ mod not_docs {
} }
} }
fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) { fn create_runtime_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
let extensions: Vec<Extension> = vec![ let extensions: Vec<Extension> = vec![
deno_webidl::init(), deno_webidl::init(),
deno_console::init(), deno_console::init(),
@ -205,31 +150,26 @@ mod not_docs {
deno_flash::init::<Permissions>(false), // No --unstable deno_flash::init::<Permissions>(false), // No --unstable
]; ];
let js_runtime = JsRuntime::new(RuntimeOptions { create_snapshot(CreateSnapshotOptions {
will_snapshot: true, cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
snapshot_path,
startup_snapshot: None,
extensions, extensions,
..Default::default() additional_files: files,
compression_cb: Some(Box::new(|vec, snapshot_slice| {
lzzzz::lz4_hc::compress_to_vec(
snapshot_slice,
vec,
lzzzz::lz4_hc::CLEVEL_MAX,
)
.expect("snapshot compression failed");
})),
}); });
create_snapshot(js_runtime, snapshot_path, files);
}
fn get_js_files(d: &str) -> Vec<PathBuf> {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let mut js_files = std::fs::read_dir(d)
.unwrap()
.map(|dir_entry| {
let file = dir_entry.unwrap();
manifest_dir.join(file.path())
})
.filter(|path| path.extension().unwrap_or_default() == "js")
.collect::<Vec<PathBuf>>();
js_files.sort();
js_files
} }
pub fn build_snapshot(runtime_snapshot_path: PathBuf) { pub fn build_snapshot(runtime_snapshot_path: PathBuf) {
let js_files = get_js_files("js"); let js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js");
create_runtime_snapshot(&runtime_snapshot_path, js_files); create_runtime_snapshot(runtime_snapshot_path, js_files);
} }
} }
@ -242,7 +182,7 @@ fn main() {
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
// Main snapshot // Main snapshot
let runtime_snapshot_path = o.join("CLI_SNAPSHOT.bin"); let runtime_snapshot_path = o.join("RUNTIME_SNAPSHOT.bin");
// If we're building on docs.rs we just create // If we're building on docs.rs we just create
// and empty snapshot file and return, because `rusty_v8` // and empty snapshot file and return, because `rusty_v8`

View file

@ -2,17 +2,18 @@
use deno_core::Snapshot; use deno_core::Snapshot;
use log::debug; use log::debug;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::path::PathBuf;
pub static CLI_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new( pub static RUNTIME_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
#[allow(clippy::uninit_vec)] #[allow(clippy::uninit_vec)]
#[cold] #[cold]
#[inline(never)] #[inline(never)]
|| { || {
static COMPRESSED_CLI_SNAPSHOT: &[u8] = static COMPRESSED_RUNTIME_SNAPSHOT: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin")); include_bytes!(concat!(env!("OUT_DIR"), "/RUNTIME_SNAPSHOT.bin"));
let size = let size =
u32::from_le_bytes(COMPRESSED_CLI_SNAPSHOT[0..4].try_into().unwrap()) u32::from_le_bytes(COMPRESSED_RUNTIME_SNAPSHOT[0..4].try_into().unwrap())
as usize; as usize;
let mut vec = Vec::with_capacity(size); let mut vec = Vec::with_capacity(size);
@ -22,7 +23,8 @@ pub static CLI_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
vec.set_len(size); vec.set_len(size);
} }
lzzzz::lz4::decompress(&COMPRESSED_CLI_SNAPSHOT[4..], &mut vec).unwrap(); lzzzz::lz4::decompress(&COMPRESSED_RUNTIME_SNAPSHOT[4..], &mut vec)
.unwrap();
vec.into_boxed_slice() vec.into_boxed_slice()
}, },
@ -30,29 +32,11 @@ pub static CLI_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
pub fn deno_isolate_init() -> Snapshot { pub fn deno_isolate_init() -> Snapshot {
debug!("Deno isolate init with snapshots."); debug!("Deno isolate init with snapshots.");
Snapshot::Static(&CLI_SNAPSHOT) Snapshot::Static(&RUNTIME_SNAPSHOT)
} }
#[cfg(test)] pub fn get_99_main() -> PathBuf {
mod tests { let manifest = env!("CARGO_MANIFEST_DIR");
use super::*; let path = PathBuf::from(manifest);
path.join("js").join("99_main.js")
#[test]
fn cli_snapshot() {
let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
startup_snapshot: Some(deno_isolate_init()),
..Default::default()
});
js_runtime
.execute_script(
"<anon>",
r#"
if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) {
throw Error("bad");
}
console.log("we have console.log!!!");
"#,
)
.unwrap();
}
} }

View file

@ -7,8 +7,6 @@
__bootstrap.denoNs = { __bootstrap.denoNs = {
metrics: core.metrics, metrics: core.metrics,
test: __bootstrap.testing.test,
bench: __bootstrap.testing.bench,
Process: __bootstrap.process.Process, Process: __bootstrap.process.Process,
run: __bootstrap.process.run, run: __bootstrap.process.run,
isatty: __bootstrap.tty.isatty, isatty: __bootstrap.tty.isatty,

View file

@ -0,0 +1,352 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
"use strict";
((window) => {
const core = Deno.core;
const {
ObjectDefineProperties,
SymbolFor,
} = window.__bootstrap.primordials;
const util = window.__bootstrap.util;
const location = window.__bootstrap.location;
const event = window.__bootstrap.event;
const eventTarget = window.__bootstrap.eventTarget;
const timers = window.__bootstrap.timers;
const base64 = window.__bootstrap.base64;
const encoding = window.__bootstrap.encoding;
const Console = window.__bootstrap.console.Console;
const caches = window.__bootstrap.caches;
const compression = window.__bootstrap.compression;
const worker = window.__bootstrap.worker;
const performance = window.__bootstrap.performance;
const crypto = window.__bootstrap.crypto;
const url = window.__bootstrap.url;
const urlPattern = window.__bootstrap.urlPattern;
const headers = window.__bootstrap.headers;
const streams = window.__bootstrap.streams;
const fileReader = window.__bootstrap.fileReader;
const webgpu = window.__bootstrap.webgpu;
const webSocket = window.__bootstrap.webSocket;
const broadcastChannel = window.__bootstrap.broadcastChannel;
const file = window.__bootstrap.file;
const formData = window.__bootstrap.formData;
const fetch = window.__bootstrap.fetch;
const messagePort = window.__bootstrap.messagePort;
const webidl = window.__bootstrap.webidl;
const domException = window.__bootstrap.domException;
const abortSignal = window.__bootstrap.abortSignal;
const globalInterfaces = window.__bootstrap.globalInterfaces;
const webStorage = window.__bootstrap.webStorage;
const prompt = window.__bootstrap.prompt;
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
const windowOrWorkerGlobalScope = {
AbortController: util.nonEnumerable(abortSignal.AbortController),
AbortSignal: util.nonEnumerable(abortSignal.AbortSignal),
Blob: util.nonEnumerable(file.Blob),
ByteLengthQueuingStrategy: util.nonEnumerable(
streams.ByteLengthQueuingStrategy,
),
CloseEvent: util.nonEnumerable(event.CloseEvent),
CompressionStream: util.nonEnumerable(compression.CompressionStream),
CountQueuingStrategy: util.nonEnumerable(
streams.CountQueuingStrategy,
),
CryptoKey: util.nonEnumerable(crypto.CryptoKey),
CustomEvent: util.nonEnumerable(event.CustomEvent),
DecompressionStream: util.nonEnumerable(compression.DecompressionStream),
DOMException: util.nonEnumerable(domException.DOMException),
ErrorEvent: util.nonEnumerable(event.ErrorEvent),
Event: util.nonEnumerable(event.Event),
EventTarget: util.nonEnumerable(eventTarget.EventTarget),
File: util.nonEnumerable(file.File),
FileReader: util.nonEnumerable(fileReader.FileReader),
FormData: util.nonEnumerable(formData.FormData),
Headers: util.nonEnumerable(headers.Headers),
MessageEvent: util.nonEnumerable(event.MessageEvent),
Performance: util.nonEnumerable(performance.Performance),
PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry),
PerformanceMark: util.nonEnumerable(performance.PerformanceMark),
PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure),
PromiseRejectionEvent: util.nonEnumerable(event.PromiseRejectionEvent),
ProgressEvent: util.nonEnumerable(event.ProgressEvent),
ReadableStream: util.nonEnumerable(streams.ReadableStream),
ReadableStreamDefaultReader: util.nonEnumerable(
streams.ReadableStreamDefaultReader,
),
Request: util.nonEnumerable(fetch.Request),
Response: util.nonEnumerable(fetch.Response),
TextDecoder: util.nonEnumerable(encoding.TextDecoder),
TextEncoder: util.nonEnumerable(encoding.TextEncoder),
TextDecoderStream: util.nonEnumerable(encoding.TextDecoderStream),
TextEncoderStream: util.nonEnumerable(encoding.TextEncoderStream),
TransformStream: util.nonEnumerable(streams.TransformStream),
URL: util.nonEnumerable(url.URL),
URLPattern: util.nonEnumerable(urlPattern.URLPattern),
URLSearchParams: util.nonEnumerable(url.URLSearchParams),
WebSocket: util.nonEnumerable(webSocket.WebSocket),
MessageChannel: util.nonEnumerable(messagePort.MessageChannel),
MessagePort: util.nonEnumerable(messagePort.MessagePort),
Worker: util.nonEnumerable(worker.Worker),
WritableStream: util.nonEnumerable(streams.WritableStream),
WritableStreamDefaultWriter: util.nonEnumerable(
streams.WritableStreamDefaultWriter,
),
WritableStreamDefaultController: util.nonEnumerable(
streams.WritableStreamDefaultController,
),
ReadableByteStreamController: util.nonEnumerable(
streams.ReadableByteStreamController,
),
ReadableStreamBYOBReader: util.nonEnumerable(
streams.ReadableStreamBYOBReader,
),
ReadableStreamBYOBRequest: util.nonEnumerable(
streams.ReadableStreamBYOBRequest,
),
ReadableStreamDefaultController: util.nonEnumerable(
streams.ReadableStreamDefaultController,
),
TransformStreamDefaultController: util.nonEnumerable(
streams.TransformStreamDefaultController,
),
atob: util.writable(base64.atob),
btoa: util.writable(base64.btoa),
clearInterval: util.writable(timers.clearInterval),
clearTimeout: util.writable(timers.clearTimeout),
caches: {
enumerable: true,
configurable: true,
get: caches.cacheStorage,
},
CacheStorage: util.nonEnumerable(caches.CacheStorage),
Cache: util.nonEnumerable(caches.Cache),
console: util.nonEnumerable(
new Console((msg, level) => core.print(msg, level > 1)),
),
crypto: util.readOnly(crypto.crypto),
Crypto: util.nonEnumerable(crypto.Crypto),
SubtleCrypto: util.nonEnumerable(crypto.SubtleCrypto),
fetch: util.writable(fetch.fetch),
performance: util.writable(performance.performance),
reportError: util.writable(event.reportError),
setInterval: util.writable(timers.setInterval),
setTimeout: util.writable(timers.setTimeout),
structuredClone: util.writable(messagePort.structuredClone),
// Branding as a WebIDL object
[webidl.brand]: util.nonEnumerable(webidl.brand),
};
const unstableWindowOrWorkerGlobalScope = {
BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
WebSocketStream: util.nonEnumerable(webSocket.WebSocketStream),
GPU: util.nonEnumerable(webgpu.GPU),
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
GPUSupportedLimits: util.nonEnumerable(webgpu.GPUSupportedLimits),
GPUSupportedFeatures: util.nonEnumerable(webgpu.GPUSupportedFeatures),
GPUDevice: util.nonEnumerable(webgpu.GPUDevice),
GPUQueue: util.nonEnumerable(webgpu.GPUQueue),
GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer),
GPUBufferUsage: util.nonEnumerable(webgpu.GPUBufferUsage),
GPUMapMode: util.nonEnumerable(webgpu.GPUMapMode),
GPUTexture: util.nonEnumerable(webgpu.GPUTexture),
GPUTextureUsage: util.nonEnumerable(webgpu.GPUTextureUsage),
GPUTextureView: util.nonEnumerable(webgpu.GPUTextureView),
GPUSampler: util.nonEnumerable(webgpu.GPUSampler),
GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),
GPURenderPipeline: util.nonEnumerable(webgpu.GPURenderPipeline),
GPUColorWrite: util.nonEnumerable(webgpu.GPUColorWrite),
GPUCommandEncoder: util.nonEnumerable(webgpu.GPUCommandEncoder),
GPURenderPassEncoder: util.nonEnumerable(webgpu.GPURenderPassEncoder),
GPUComputePassEncoder: util.nonEnumerable(webgpu.GPUComputePassEncoder),
GPUCommandBuffer: util.nonEnumerable(webgpu.GPUCommandBuffer),
GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder),
GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle),
GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet),
GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError),
GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError),
};
class Navigator {
constructor() {
webidl.illegalConstructor();
}
[SymbolFor("Deno.privateCustomInspect")](inspect) {
return `${this.constructor.name} ${inspect({})}`;
}
}
const navigator = webidl.createBranded(Navigator);
let numCpus, userAgent, language;
function setNumCpus(val) {
numCpus = val;
}
function setUserAgent(val) {
userAgent = val;
}
function setLanguage(val) {
language = val;
}
ObjectDefineProperties(Navigator.prototype, {
gpu: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return webgpu.gpu;
},
},
hardwareConcurrency: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return numCpus;
},
},
userAgent: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return userAgent;
},
},
language: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return language;
},
},
languages: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return [language];
},
},
});
const NavigatorPrototype = Navigator.prototype;
class WorkerNavigator {
constructor() {
webidl.illegalConstructor();
}
[SymbolFor("Deno.privateCustomInspect")](inspect) {
return `${this.constructor.name} ${inspect({})}`;
}
}
const workerNavigator = webidl.createBranded(WorkerNavigator);
ObjectDefineProperties(WorkerNavigator.prototype, {
gpu: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return webgpu.gpu;
},
},
hardwareConcurrency: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return numCpus;
},
language: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return language;
},
},
languages: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return [language];
},
},
},
});
const WorkerNavigatorPrototype = WorkerNavigator.prototype;
const mainRuntimeGlobalProperties = {
Location: location.locationConstructorDescriptor,
location: location.locationDescriptor,
Window: globalInterfaces.windowConstructorDescriptor,
window: util.readOnly(globalThis),
self: util.writable(globalThis),
Navigator: util.nonEnumerable(Navigator),
navigator: {
configurable: true,
enumerable: true,
get: () => navigator,
},
alert: util.writable(prompt.alert),
confirm: util.writable(prompt.confirm),
prompt: util.writable(prompt.prompt),
localStorage: {
configurable: true,
enumerable: true,
get: webStorage.localStorage,
// Makes this reassignable to make astro work
set: () => {},
},
sessionStorage: {
configurable: true,
enumerable: true,
get: webStorage.sessionStorage,
// Makes this reassignable to make astro work
set: () => {},
},
Storage: util.nonEnumerable(webStorage.Storage),
};
const workerRuntimeGlobalProperties = {
WorkerLocation: location.workerLocationConstructorDescriptor,
location: location.workerLocationDescriptor,
WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
DedicatedWorkerGlobalScope:
globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,
WorkerNavigator: util.nonEnumerable(WorkerNavigator),
navigator: {
configurable: true,
enumerable: true,
get: () => workerNavigator,
},
self: util.readOnly(globalThis),
};
window.__bootstrap.globalScope = {
windowOrWorkerGlobalScope,
unstableWindowOrWorkerGlobalScope,
mainRuntimeGlobalProperties,
workerRuntimeGlobalProperties,
setNumCpus,
setUserAgent,
setLanguage,
};
})(this);

View file

@ -41,48 +41,37 @@ delete Intl.v8BreakIterator;
const util = window.__bootstrap.util; const util = window.__bootstrap.util;
const event = window.__bootstrap.event; const event = window.__bootstrap.event;
const eventTarget = window.__bootstrap.eventTarget; const eventTarget = window.__bootstrap.eventTarget;
const globalInterfaces = window.__bootstrap.globalInterfaces;
const location = window.__bootstrap.location; const location = window.__bootstrap.location;
const build = window.__bootstrap.build; const build = window.__bootstrap.build;
const version = window.__bootstrap.version; const version = window.__bootstrap.version;
const os = window.__bootstrap.os; const os = window.__bootstrap.os;
const timers = window.__bootstrap.timers; const timers = window.__bootstrap.timers;
const base64 = window.__bootstrap.base64;
const encoding = window.__bootstrap.encoding;
const colors = window.__bootstrap.colors; const colors = window.__bootstrap.colors;
const Console = window.__bootstrap.console.Console;
const caches = window.__bootstrap.caches;
const inspectArgs = window.__bootstrap.console.inspectArgs; const inspectArgs = window.__bootstrap.console.inspectArgs;
const quoteString = window.__bootstrap.console.quoteString; const quoteString = window.__bootstrap.console.quoteString;
const compression = window.__bootstrap.compression;
const worker = window.__bootstrap.worker;
const internals = window.__bootstrap.internals; const internals = window.__bootstrap.internals;
const performance = window.__bootstrap.performance; const performance = window.__bootstrap.performance;
const net = window.__bootstrap.net; const net = window.__bootstrap.net;
const crypto = window.__bootstrap.crypto;
const url = window.__bootstrap.url; const url = window.__bootstrap.url;
const urlPattern = window.__bootstrap.urlPattern;
const headers = window.__bootstrap.headers;
const streams = window.__bootstrap.streams;
const fileReader = window.__bootstrap.fileReader;
const webgpu = window.__bootstrap.webgpu;
const webSocket = window.__bootstrap.webSocket;
const webStorage = window.__bootstrap.webStorage;
const broadcastChannel = window.__bootstrap.broadcastChannel;
const file = window.__bootstrap.file;
const formData = window.__bootstrap.formData;
const fetch = window.__bootstrap.fetch; const fetch = window.__bootstrap.fetch;
const prompt = window.__bootstrap.prompt;
const messagePort = window.__bootstrap.messagePort; const messagePort = window.__bootstrap.messagePort;
const denoNs = window.__bootstrap.denoNs; const denoNs = window.__bootstrap.denoNs;
const denoNsUnstable = window.__bootstrap.denoNsUnstable; const denoNsUnstable = window.__bootstrap.denoNsUnstable;
const errors = window.__bootstrap.errors.errors; const errors = window.__bootstrap.errors.errors;
const webidl = window.__bootstrap.webidl; const webidl = window.__bootstrap.webidl;
const domException = window.__bootstrap.domException; const domException = window.__bootstrap.domException;
const abortSignal = window.__bootstrap.abortSignal;
const { defineEventHandler, reportException } = window.__bootstrap.event; const { defineEventHandler, reportException } = window.__bootstrap.event;
const { deserializeJsMessageData, serializeJsMessageData } = const { deserializeJsMessageData, serializeJsMessageData } =
window.__bootstrap.messagePort; window.__bootstrap.messagePort;
const {
windowOrWorkerGlobalScope,
unstableWindowOrWorkerGlobalScope,
workerRuntimeGlobalProperties,
mainRuntimeGlobalProperties,
setNumCpus,
setUserAgent,
setLanguage,
} = window.__bootstrap.globalScope;
let windowIsClosing = false; let windowIsClosing = false;
@ -326,298 +315,6 @@ delete Intl.v8BreakIterator;
); );
} }
class Navigator {
constructor() {
webidl.illegalConstructor();
}
[SymbolFor("Deno.privateCustomInspect")](inspect) {
return `${this.constructor.name} ${inspect({})}`;
}
}
const navigator = webidl.createBranded(Navigator);
let numCpus, userAgent, language;
ObjectDefineProperties(Navigator.prototype, {
gpu: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return webgpu.gpu;
},
},
hardwareConcurrency: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return numCpus;
},
},
userAgent: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return userAgent;
},
},
language: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return language;
},
},
languages: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return [language];
},
},
});
const NavigatorPrototype = Navigator.prototype;
class WorkerNavigator {
constructor() {
webidl.illegalConstructor();
}
[SymbolFor("Deno.privateCustomInspect")](inspect) {
return `${this.constructor.name} ${inspect({})}`;
}
}
const workerNavigator = webidl.createBranded(WorkerNavigator);
ObjectDefineProperties(WorkerNavigator.prototype, {
gpu: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return webgpu.gpu;
},
},
hardwareConcurrency: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return numCpus;
},
language: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return language;
},
},
languages: {
configurable: true,
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return [language];
},
},
},
});
const WorkerNavigatorPrototype = WorkerNavigator.prototype;
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
const windowOrWorkerGlobalScope = {
AbortController: util.nonEnumerable(abortSignal.AbortController),
AbortSignal: util.nonEnumerable(abortSignal.AbortSignal),
Blob: util.nonEnumerable(file.Blob),
ByteLengthQueuingStrategy: util.nonEnumerable(
streams.ByteLengthQueuingStrategy,
),
CloseEvent: util.nonEnumerable(event.CloseEvent),
CompressionStream: util.nonEnumerable(compression.CompressionStream),
CountQueuingStrategy: util.nonEnumerable(
streams.CountQueuingStrategy,
),
CryptoKey: util.nonEnumerable(crypto.CryptoKey),
CustomEvent: util.nonEnumerable(event.CustomEvent),
DecompressionStream: util.nonEnumerable(compression.DecompressionStream),
DOMException: util.nonEnumerable(domException.DOMException),
ErrorEvent: util.nonEnumerable(event.ErrorEvent),
Event: util.nonEnumerable(event.Event),
EventTarget: util.nonEnumerable(eventTarget.EventTarget),
File: util.nonEnumerable(file.File),
FileReader: util.nonEnumerable(fileReader.FileReader),
FormData: util.nonEnumerable(formData.FormData),
Headers: util.nonEnumerable(headers.Headers),
MessageEvent: util.nonEnumerable(event.MessageEvent),
Performance: util.nonEnumerable(performance.Performance),
PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry),
PerformanceMark: util.nonEnumerable(performance.PerformanceMark),
PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure),
PromiseRejectionEvent: util.nonEnumerable(event.PromiseRejectionEvent),
ProgressEvent: util.nonEnumerable(event.ProgressEvent),
ReadableStream: util.nonEnumerable(streams.ReadableStream),
ReadableStreamDefaultReader: util.nonEnumerable(
streams.ReadableStreamDefaultReader,
),
Request: util.nonEnumerable(fetch.Request),
Response: util.nonEnumerable(fetch.Response),
TextDecoder: util.nonEnumerable(encoding.TextDecoder),
TextEncoder: util.nonEnumerable(encoding.TextEncoder),
TextDecoderStream: util.nonEnumerable(encoding.TextDecoderStream),
TextEncoderStream: util.nonEnumerable(encoding.TextEncoderStream),
TransformStream: util.nonEnumerable(streams.TransformStream),
URL: util.nonEnumerable(url.URL),
URLPattern: util.nonEnumerable(urlPattern.URLPattern),
URLSearchParams: util.nonEnumerable(url.URLSearchParams),
WebSocket: util.nonEnumerable(webSocket.WebSocket),
MessageChannel: util.nonEnumerable(messagePort.MessageChannel),
MessagePort: util.nonEnumerable(messagePort.MessagePort),
Worker: util.nonEnumerable(worker.Worker),
WritableStream: util.nonEnumerable(streams.WritableStream),
WritableStreamDefaultWriter: util.nonEnumerable(
streams.WritableStreamDefaultWriter,
),
WritableStreamDefaultController: util.nonEnumerable(
streams.WritableStreamDefaultController,
),
ReadableByteStreamController: util.nonEnumerable(
streams.ReadableByteStreamController,
),
ReadableStreamBYOBReader: util.nonEnumerable(
streams.ReadableStreamBYOBReader,
),
ReadableStreamBYOBRequest: util.nonEnumerable(
streams.ReadableStreamBYOBRequest,
),
ReadableStreamDefaultController: util.nonEnumerable(
streams.ReadableStreamDefaultController,
),
TransformStreamDefaultController: util.nonEnumerable(
streams.TransformStreamDefaultController,
),
atob: util.writable(base64.atob),
btoa: util.writable(base64.btoa),
clearInterval: util.writable(timers.clearInterval),
clearTimeout: util.writable(timers.clearTimeout),
caches: {
enumerable: true,
configurable: true,
get: caches.cacheStorage,
},
CacheStorage: util.nonEnumerable(caches.CacheStorage),
Cache: util.nonEnumerable(caches.Cache),
console: util.nonEnumerable(
new Console((msg, level) => core.print(msg, level > 1)),
),
crypto: util.readOnly(crypto.crypto),
Crypto: util.nonEnumerable(crypto.Crypto),
SubtleCrypto: util.nonEnumerable(crypto.SubtleCrypto),
fetch: util.writable(fetch.fetch),
performance: util.writable(performance.performance),
reportError: util.writable(event.reportError),
setInterval: util.writable(timers.setInterval),
setTimeout: util.writable(timers.setTimeout),
structuredClone: util.writable(messagePort.structuredClone),
// Branding as a WebIDL object
[webidl.brand]: util.nonEnumerable(webidl.brand),
};
const unstableWindowOrWorkerGlobalScope = {
BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
WebSocketStream: util.nonEnumerable(webSocket.WebSocketStream),
GPU: util.nonEnumerable(webgpu.GPU),
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
GPUSupportedLimits: util.nonEnumerable(webgpu.GPUSupportedLimits),
GPUSupportedFeatures: util.nonEnumerable(webgpu.GPUSupportedFeatures),
GPUDevice: util.nonEnumerable(webgpu.GPUDevice),
GPUQueue: util.nonEnumerable(webgpu.GPUQueue),
GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer),
GPUBufferUsage: util.nonEnumerable(webgpu.GPUBufferUsage),
GPUMapMode: util.nonEnumerable(webgpu.GPUMapMode),
GPUTexture: util.nonEnumerable(webgpu.GPUTexture),
GPUTextureUsage: util.nonEnumerable(webgpu.GPUTextureUsage),
GPUTextureView: util.nonEnumerable(webgpu.GPUTextureView),
GPUSampler: util.nonEnumerable(webgpu.GPUSampler),
GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),
GPURenderPipeline: util.nonEnumerable(webgpu.GPURenderPipeline),
GPUColorWrite: util.nonEnumerable(webgpu.GPUColorWrite),
GPUCommandEncoder: util.nonEnumerable(webgpu.GPUCommandEncoder),
GPURenderPassEncoder: util.nonEnumerable(webgpu.GPURenderPassEncoder),
GPUComputePassEncoder: util.nonEnumerable(webgpu.GPUComputePassEncoder),
GPUCommandBuffer: util.nonEnumerable(webgpu.GPUCommandBuffer),
GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder),
GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle),
GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet),
GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError),
GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError),
};
const mainRuntimeGlobalProperties = {
Location: location.locationConstructorDescriptor,
location: location.locationDescriptor,
Window: globalInterfaces.windowConstructorDescriptor,
window: util.readOnly(globalThis),
self: util.writable(globalThis),
Navigator: util.nonEnumerable(Navigator),
navigator: {
configurable: true,
enumerable: true,
get: () => navigator,
},
close: util.writable(windowClose),
closed: util.getterOnly(() => windowIsClosing),
alert: util.writable(prompt.alert),
confirm: util.writable(prompt.confirm),
prompt: util.writable(prompt.prompt),
localStorage: {
configurable: true,
enumerable: true,
get: webStorage.localStorage,
// Makes this reassignable to make astro work
set: () => {},
},
sessionStorage: {
configurable: true,
enumerable: true,
get: webStorage.sessionStorage,
// Makes this reassignable to make astro work
set: () => {},
},
Storage: util.nonEnumerable(webStorage.Storage),
};
const workerRuntimeGlobalProperties = {
WorkerLocation: location.workerLocationConstructorDescriptor,
location: location.workerLocationDescriptor,
WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
DedicatedWorkerGlobalScope:
globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,
WorkerNavigator: util.nonEnumerable(WorkerNavigator),
navigator: {
configurable: true,
enumerable: true,
get: () => workerNavigator,
},
self: util.readOnly(globalThis),
// TODO(bartlomieju): should be readonly?
close: util.nonEnumerable(workerClose),
postMessage: util.writable(postMessage),
};
const pendingRejections = []; const pendingRejections = [];
const pendingRejectionsReasons = new SafeWeakMap(); const pendingRejectionsReasons = new SafeWeakMap();
@ -726,6 +423,10 @@ delete Intl.v8BreakIterator;
ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope); ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope);
} }
ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties); ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties);
ObjectDefineProperties(globalThis, {
close: util.writable(windowClose),
closed: util.getterOnly(() => windowIsClosing),
});
ObjectSetPrototypeOf(globalThis, Window.prototype); ObjectSetPrototypeOf(globalThis, Window.prototype);
if (runtimeOptions.inspectFlag) { if (runtimeOptions.inspectFlag) {
@ -754,9 +455,9 @@ delete Intl.v8BreakIterator;
runtimeStart(runtimeOptions); runtimeStart(runtimeOptions);
numCpus = runtimeOptions.cpuCount; setNumCpus(runtimeOptions.cpuCount);
userAgent = runtimeOptions.userAgent; setUserAgent(runtimeOptions.userAgent);
language = runtimeOptions.locale; setLanguage(runtimeOptions.locale);
const internalSymbol = Symbol("Deno.internal"); const internalSymbol = Symbol("Deno.internal");
@ -849,7 +550,12 @@ delete Intl.v8BreakIterator;
ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope); ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope);
} }
ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties); ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties);
ObjectDefineProperties(globalThis, { name: util.writable(name) }); ObjectDefineProperties(globalThis, {
name: util.writable(name),
// TODO(bartlomieju): should be readonly?
close: util.nonEnumerable(workerClose),
postMessage: util.writable(postMessage),
});
if (runtimeOptions.enableTestingFeaturesFlag) { if (runtimeOptions.enableTestingFeaturesFlag) {
ObjectDefineProperty( ObjectDefineProperty(
globalThis, globalThis,
@ -882,8 +588,9 @@ delete Intl.v8BreakIterator;
); );
location.setLocationHref(runtimeOptions.location); location.setLocationHref(runtimeOptions.location);
numCpus = runtimeOptions.cpuCount;
language = runtimeOptions.locale; setNumCpus(runtimeOptions.cpuCount);
setLanguage(runtimeOptions.locale);
globalThis.pollForMessages = pollForMessages; globalThis.pollForMessages = pollForMessages;

View file

@ -7,6 +7,7 @@ pub use deno_core;
pub use deno_crypto; pub use deno_crypto;
pub use deno_fetch; pub use deno_fetch;
pub use deno_ffi; pub use deno_ffi;
pub use deno_flash;
pub use deno_http; pub use deno_http;
pub use deno_napi; pub use deno_napi;
pub use deno_net; pub use deno_net;

View file

@ -17,7 +17,6 @@ use deno_core::futures::channel::mpsc;
use deno_core::futures::future::poll_fn; use deno_core::futures::future::poll_fn;
use deno_core::futures::stream::StreamExt; use deno_core::futures::stream::StreamExt;
use deno_core::futures::task::AtomicWaker; use deno_core::futures::task::AtomicWaker;
use deno_core::located_script_name;
use deno_core::serde::Deserialize; use deno_core::serde::Deserialize;
use deno_core::serde::Serialize; use deno_core::serde::Serialize;
use deno_core::serde_json::json; use deno_core::serde_json::json;
@ -33,6 +32,7 @@ use deno_core::ModuleSpecifier;
use deno_core::RuntimeOptions; use deno_core::RuntimeOptions;
use deno_core::SharedArrayBufferStore; use deno_core::SharedArrayBufferStore;
use deno_core::SourceMapGetter; use deno_core::SourceMapGetter;
use deno_core::{located_script_name, Snapshot};
use deno_node::RequireNpmResolver; use deno_node::RequireNpmResolver;
use deno_tls::rustls::RootCertStore; use deno_tls::rustls::RootCertStore;
use deno_web::create_entangled_message_port; use deno_web::create_entangled_message_port;
@ -322,6 +322,7 @@ pub struct WebWorker {
pub struct WebWorkerOptions { pub struct WebWorkerOptions {
pub bootstrap: BootstrapOptions, pub bootstrap: BootstrapOptions,
pub extensions: Vec<Extension>, pub extensions: Vec<Extension>,
pub startup_snapshot: Option<Snapshot>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>, pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub root_cert_store: Option<RootCertStore>, pub root_cert_store: Option<RootCertStore>,
pub seed: Option<u64>, pub seed: Option<u64>,
@ -451,7 +452,11 @@ impl WebWorker {
let mut js_runtime = JsRuntime::new(RuntimeOptions { let mut js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(options.module_loader.clone()), module_loader: Some(options.module_loader.clone()),
startup_snapshot: Some(js::deno_isolate_init()), startup_snapshot: Some(
options
.startup_snapshot
.unwrap_or_else(js::deno_isolate_init),
),
source_map_getter: options.source_map_getter, source_map_getter: options.source_map_getter,
get_error_class_fn: options.get_error_class_fn, get_error_class_fn: options.get_error_class_fn,
shared_array_buffer_store: options.shared_array_buffer_store.clone(), shared_array_buffer_store: options.shared_array_buffer_store.clone(),

View file

@ -13,9 +13,6 @@ use deno_core::error::AnyError;
use deno_core::error::JsError; use deno_core::error::JsError;
use deno_core::futures::Future; use deno_core::futures::Future;
use deno_core::located_script_name; use deno_core::located_script_name;
use deno_core::serde_json::json;
use deno_core::serde_v8;
use deno_core::v8;
use deno_core::CompiledWasmModuleStore; use deno_core::CompiledWasmModuleStore;
use deno_core::Extension; use deno_core::Extension;
use deno_core::FsModuleLoader; use deno_core::FsModuleLoader;
@ -66,10 +63,6 @@ pub struct MainWorker {
pub js_runtime: JsRuntime, pub js_runtime: JsRuntime,
should_break_on_first_statement: bool, should_break_on_first_statement: bool,
exit_code: ExitCode, exit_code: ExitCode,
js_run_tests_callback: v8::Global<v8::Function>,
js_run_benchmarks_callback: v8::Global<v8::Function>,
js_enable_test_callback: v8::Global<v8::Function>,
js_enable_bench_callback: v8::Global<v8::Function>,
} }
pub struct WorkerOptions { pub struct WorkerOptions {
@ -99,15 +92,6 @@ pub struct WorkerOptions {
pub stdio: Stdio, pub stdio: Stdio,
} }
fn grab_cb(
scope: &mut v8::HandleScope,
path: &str,
) -> v8::Global<v8::Function> {
let cb = JsRuntime::eval::<v8::Function>(scope, path)
.unwrap_or_else(|| panic!("{} must be defined", path));
v8::Global::new(scope, cb)
}
impl Default for WorkerOptions { impl Default for WorkerOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -267,29 +251,10 @@ impl MainWorker {
); );
} }
let (
js_run_tests_callback,
js_run_benchmarks_callback,
js_enable_test_callback,
js_enable_bench_callback,
) = {
let scope = &mut js_runtime.handle_scope();
(
grab_cb(scope, "__bootstrap.testing.runTests"),
grab_cb(scope, "__bootstrap.testing.runBenchmarks"),
grab_cb(scope, "__bootstrap.testing.enableTest"),
grab_cb(scope, "__bootstrap.testing.enableBench"),
)
};
Self { Self {
js_runtime, js_runtime,
should_break_on_first_statement: options.should_break_on_first_statement, should_break_on_first_statement: options.should_break_on_first_statement,
exit_code, exit_code,
js_run_tests_callback,
js_run_benchmarks_callback,
js_enable_test_callback,
js_enable_bench_callback,
} }
} }
@ -377,65 +342,6 @@ impl MainWorker {
self.evaluate_module(id).await self.evaluate_module(id).await
} }
/// Run tests declared with `Deno.test()`. Test events will be dispatched
/// by calling ops which are currently only implemented in the CLI crate.
// TODO(nayeemrmn): Move testing ops to deno_runtime and redesign/unhide.
#[doc(hidden)]
pub async fn run_tests(
&mut self,
shuffle: &Option<u64>,
) -> Result<(), AnyError> {
let promise = {
let scope = &mut self.js_runtime.handle_scope();
let cb = self.js_run_tests_callback.open(scope);
let this = v8::undefined(scope).into();
let options =
serde_v8::to_v8(scope, json!({ "shuffle": shuffle })).unwrap();
let promise = cb.call(scope, this, &[options]).unwrap();
v8::Global::new(scope, promise)
};
self.js_runtime.resolve_value(promise).await?;
Ok(())
}
/// Run benches declared with `Deno.bench()`. Bench events will be dispatched
/// by calling ops which are currently only implemented in the CLI crate.
// TODO(nayeemrmn): Move benchmark ops to deno_runtime and redesign/unhide.
#[doc(hidden)]
pub async fn run_benchmarks(&mut self) -> Result<(), AnyError> {
let promise = {
let scope = &mut self.js_runtime.handle_scope();
let cb = self.js_run_benchmarks_callback.open(scope);
let this = v8::undefined(scope).into();
let promise = cb.call(scope, this, &[]).unwrap();
v8::Global::new(scope, promise)
};
self.js_runtime.resolve_value(promise).await?;
Ok(())
}
/// Enable `Deno.test()`. If this isn't called before executing user code,
/// `Deno.test()` calls will noop.
// TODO(nayeemrmn): Move testing ops to deno_runtime and redesign/unhide.
#[doc(hidden)]
pub fn enable_test(&mut self) {
let scope = &mut self.js_runtime.handle_scope();
let cb = self.js_enable_test_callback.open(scope);
let this = v8::undefined(scope).into();
cb.call(scope, this, &[]).unwrap();
}
/// Enable `Deno.bench()`. If this isn't called before executing user code,
/// `Deno.bench()` calls will noop.
// TODO(nayeemrmn): Move benchmark ops to deno_runtime and redesign/unhide.
#[doc(hidden)]
pub fn enable_bench(&mut self) {
let scope = &mut self.js_runtime.handle_scope();
let cb = self.js_enable_bench_callback.open(scope);
let this = v8::undefined(scope).into();
cb.call(scope, this, &[]).unwrap();
}
fn wait_for_inspector_session(&mut self) { fn wait_for_inspector_session(&mut self) {
if self.should_break_on_first_statement { if self.should_break_on_first_statement {
self self
@ -542,107 +448,3 @@ impl MainWorker {
Ok(local_value.is_false()) Ok(local_value.is_false())
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use deno_core::resolve_url_or_path;
fn create_test_worker() -> MainWorker {
let main_module = resolve_url_or_path("./hello.js").unwrap();
let permissions = Permissions::default();
let options = WorkerOptions {
bootstrap: BootstrapOptions {
args: vec![],
cpu_count: 1,
debug_flag: false,
enable_testing_features: false,
locale: deno_core::v8::icu::get_language_tag(),
location: None,
no_color: true,
is_tty: false,
runtime_version: "x".to_string(),
ts_version: "x".to_string(),
unstable: false,
user_agent: "x".to_string(),
inspect: false,
},
extensions: vec![],
startup_snapshot: None,
unsafely_ignore_certificate_errors: None,
root_cert_store: None,
seed: None,
format_js_error_fn: None,
source_map_getter: None,
web_worker_preload_module_cb: Arc::new(|_| unreachable!()),
web_worker_pre_execute_module_cb: Arc::new(|_| unreachable!()),
create_web_worker_cb: Arc::new(|_| unreachable!()),
maybe_inspector_server: None,
should_break_on_first_statement: false,
module_loader: Rc::new(FsModuleLoader),
npm_resolver: None,
get_error_class_fn: None,
cache_storage_dir: None,
origin_storage_dir: None,
blob_store: BlobStore::default(),
broadcast_channel: InMemoryBroadcastChannel::default(),
shared_array_buffer_store: None,
compiled_wasm_module_store: None,
stdio: Default::default(),
};
MainWorker::bootstrap_from_options(main_module, permissions, options)
}
#[tokio::test]
async fn execute_mod_esm_imports_a() {
let p = test_util::testdata_path().join("runtime/esm_imports_a.js");
let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
let mut worker = create_test_worker();
let result = worker.execute_main_module(&module_specifier).await;
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
}
if let Err(e) = worker.run_event_loop(false).await {
panic!("Future got unexpected error: {:?}", e);
}
}
#[tokio::test]
async fn execute_mod_circular() {
let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("tests/circular1.js");
let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
let mut worker = create_test_worker();
let result = worker.execute_main_module(&module_specifier).await;
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
}
if let Err(e) = worker.run_event_loop(false).await {
panic!("Future got unexpected error: {:?}", e);
}
}
#[tokio::test]
async fn execute_mod_resolve_error() {
// "foo" is not a valid module specifier so this should return an error.
let mut worker = create_test_worker();
let module_specifier = resolve_url_or_path("does-not-exist").unwrap();
let result = worker.execute_main_module(&module_specifier).await;
assert!(result.is_err());
}
#[tokio::test]
async fn execute_mod_002_hello() {
// This assumes cwd is project root (an assumption made throughout the
// tests).
let mut worker = create_test_worker();
let p = test_util::testdata_path().join("run/001_hello.js");
let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
let result = worker.execute_main_module(&module_specifier).await;
assert!(result.is_ok());
}
}