diff --git a/Cargo.lock b/Cargo.lock index f0d44b2198..c91e2414d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -781,25 +781,13 @@ dependencies = [ "data-url", "deno_ast 0.21.0", "deno_bench_util", - "deno_broadcast_channel", - "deno_cache", - "deno_console", "deno_core", - "deno_crypto", "deno_doc", "deno_emit", - "deno_fetch", "deno_graph", "deno_lint", - "deno_net", - "deno_node", "deno_runtime", "deno_task_shell", - "deno_url", - "deno_web", - "deno_webgpu", - "deno_websocket", - "deno_webstorage", "dissimilar", "dotenv", "dprint-plugin-json", @@ -822,6 +810,7 @@ dependencies = [ "libc", "log", "lsp-types", + "lzzzz", "mitata", "monch", "napi_sym", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 83ff3d77e2..732d586d26 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,25 +26,16 @@ harness = false path = "./bench/lsp_bench_standalone.rs" [build-dependencies] -deno_broadcast_channel = { version = "0.72.0", path = "../ext/broadcast_channel" } -deno_cache = { version = "0.10.0", path = "../ext/cache" } -deno_console = { version = "0.78.0", path = "../ext/console" } +deno_runtime = { version = "0.86.0", path = "../runtime" } 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" serde = { version = "=1.0.144", features = ["derive"] } serde_json = "1.0.64" zstd = '=0.11.2' glibc_version = "0.1.2" +lzzzz = '1.0' + [target.'cfg(windows)'.build-dependencies] winapi = "=0.3.9" winres = "=0.1.12" @@ -86,6 +77,7 @@ jsonc-parser = { version = "=0.21.0", features = ["serde"] } libc = "=0.2.126" log = { version = "=0.4.17", features = ["serde"] } lsp-types = "=0.93.2" # used by tower-lsp and "proposed" feature is unstable in patch releases +lzzzz = '1.0' mitata = "=0.0.7" monch = "=0.4.0" notify = "=5.0.0" diff --git a/cli/build.rs b/cli/build.rs index 73d0208f67..c7d902941f 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -1,306 +1,319 @@ // 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::JsRuntime; -use deno_core::OpState; -use deno_core::RuntimeOptions; -use regex::Regex; -use std::collections::HashMap; use std::env; use std::path::Path; use std::path::PathBuf; -// TODO(bartlomieju): this module contains a lot of duplicated -// logic with `runtime/build.rs`, factor out to `deno_core`. -fn create_snapshot( - mut js_runtime: JsRuntime, - snapshot_path: &Path, - files: Vec, -) { - // 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(); +use deno_core::snapshot_util::*; +use deno_runtime::deno_cache::SqliteBackedCache; +use deno_runtime::permissions::Permissions; +use deno_runtime::*; + +mod ts { + use super::*; + use crate::deno_webgpu_get_declaration; + use deno_core::error::custom_error; + use deno_core::error::AnyError; + use deno_core::op; + use deno_core::OpState; + use regex::Regex; + use serde::Deserialize; + use serde_json::json; + use serde_json::Value; + use std::collections::HashMap; + use std::path::Path; + use std::path::PathBuf; + + #[derive(Debug, Deserialize)] + struct LoadArgs { + /// The fully qualified specifier that should be loaded. + specifier: String, } - 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(), + pub fn create_compiler_snapshot( + snapshot_path: PathBuf, + files: Vec, + 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()); - vec.extend_from_slice( - &zstd::bulk::compress(snapshot_slice, 22) - .expect("snapshot compression failed"), - ); + // ensure we invalidate the build properly. + for (_, path) in op_crate_libs.iter() { + 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!( - "Snapshot compressed size: {}", - compressed_snapshot_with_size.len() - ); + let path_dts = cwd.join("dts"); + // ensure we invalidate the build properly. + 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(); - println!("Snapshot written to: {} ", snapshot_path.display()); -} + // create a copy of the vector that includes any op crate libs to be passed + // 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)] -struct LoadArgs { - /// The fully qualified specifier that should be loaded. - specifier: String, -} + #[op] + fn op_build_info(state: &mut OpState) -> Value { + let build_specifier = "asset:///bootstrap.ts"; + let build_libs = state.borrow::>(); + json!({ + "buildSpecifier": build_specifier, + "libs": build_libs, + }) + } -fn create_compiler_snapshot( - snapshot_path: &Path, - files: Vec, - 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()); + #[op] + fn op_cwd() -> String { + "cache:///".into() + } - // ensure we invalidate the build properly. - for (_, path) in op_crate_libs.iter() { - println!("cargo:rerun-if-changed={}", path.display()); - } + #[op] + fn op_exists() -> bool { + false + } - // 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", - ]; + #[op] + fn op_is_node_file() -> bool { + false + } - let path_dts = cwd.join("dts"); - // ensure we invalidate the build properly. - for name in libs.iter() { - println!( - "cargo:rerun-if-changed={}", - path_dts.join(format!("lib.{}.d.ts", name)).display() - ); - } + #[op] + fn op_script_version( + _state: &mut OpState, + _args: Value, + ) -> Result, AnyError> { + Ok(Some("1".to_string())) + } - // create a copy of the vector that includes any op crate libs to be passed - // 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()); - } + #[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 { + let op_crate_libs = state.borrow::>(); + let path_dts = state.borrow::(); + let re_asset = + Regex::new(r"asset:/{3}lib\.(\S+)\.d\.ts").expect("bad regex"); + let build_specifier = "asset:///bootstrap.ts"; - #[op] - fn op_build_info(state: &mut OpState) -> Value { - let build_specifier = "asset:///bootstrap.ts"; - let build_libs = state.borrow::>(); - 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, 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 { - let op_crate_libs = state.borrow::>(); - let path_dts = state.borrow::(); - 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)?; + // we need a basic file to send to tsc to warm it up. + if args.specifier == build_specifier { Ok(json!({ - "data": data, + "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!({ + "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 { Err(custom_error( "InvalidSpecifier", 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(()) - }) - .build()], - ..Default::default() - }); - - create_snapshot(js_runtime, snapshot_path, files); + pub(crate) fn version() -> String { + std::fs::read_to_string("tsc/00_typescript.js") + .unwrap() + .lines() + .find(|l| l.contains("ts.version = ")) + .expect( + "Failed to find the pattern `ts.version = ` in typescript source code", + ) + .chars() + .skip_while(|c| !char::is_numeric(*c)) + .take_while(|c| *c != '"') + .collect::() + } } -fn ts_version() -> String { - std::fs::read_to_string("tsc/00_typescript.js") - .unwrap() - .lines() - .find(|l| l.contains("ts.version = ")) - .expect( - "Failed to find the pattern `ts.version = ` in typescript source code", - ) - .chars() - .skip_while(|c| !char::is_numeric(*c)) - .take_while(|c| *c != '"') - .collect::() +fn create_cli_snapshot(snapshot_path: PathBuf, files: Vec) { + let extensions: Vec = vec![ + deno_webidl::init(), + deno_console::init(), + deno_url::init(), + deno_tls::init(), + deno_web::init::( + deno_web::BlobStore::default(), + Default::default(), + ), + deno_fetch::init::(Default::default()), + deno_cache::init::(None), + deno_websocket::init::("".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::(None), // No --unstable. + deno_ffi::init::(false), + deno_net::init::( + None, false, // No --unstable. + None, + ), + deno_napi::init::(false), + deno_http::init(), + deno_flash::init::(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 { @@ -386,7 +399,7 @@ fn main() { println!("cargo:rustc-env=GIT_COMMIT_HASH={}", 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!( @@ -440,11 +453,14 @@ fn main() { let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - // Main snapshot 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"); - create_compiler_snapshot(&compiler_snapshot_path, js_files, &c); + let cli_snapshot_path = o.join("CLI_SNAPSHOT.bin"); + 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")] { @@ -462,17 +478,3 @@ fn deno_webgpu_get_declaration() -> PathBuf { let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); manifest_dir.join("dts").join("lib.deno_webgpu.d.ts") } - -fn get_js_files(d: &str) -> Vec { - 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::>(); - js_files.sort(); - js_files -} diff --git a/cli/js.rs b/cli/js.rs new file mode 100644 index 0000000000..4bf3da6273 --- /dev/null +++ b/cli/js.rs @@ -0,0 +1,57 @@ +use deno_core::Snapshot; +use log::debug; +use once_cell::sync::Lazy; + +pub static CLI_SNAPSHOT: Lazy> = 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( + "", + r#" + if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) { + throw Error("bad"); + } + console.log("we have console.log!!!"); + "#, + ) + .unwrap(); + } +} diff --git a/runtime/js/40_testing.js b/cli/js/40_testing.js similarity index 99% rename from runtime/js/40_testing.js rename to cli/js/40_testing.js index e8590faf10..864d501044 100644 --- a/runtime/js/40_testing.js +++ b/cli/js/40_testing.js @@ -1397,12 +1397,16 @@ return testFn; } - window.__bootstrap.testing = { - bench, - enableBench, - enableTest, - runBenchmarks, - runTests, - test, + window.__bootstrap.internals = { + ...window.__bootstrap.internals ?? {}, + testing: { + runTests, + runBenchmarks, + enableTest, + enableBench, + }, }; + + window.__bootstrap.denoNs.bench = bench; + window.__bootstrap.denoNs.test = test; })(this); diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs index 950f2a96ed..d839cda568 100644 --- a/cli/lsp/testing/execution.rs +++ b/cli/lsp/testing/execution.rs @@ -7,7 +7,6 @@ use super::lsp_custom; use crate::args::flags_from_vec; use crate::args::DenoSubcommand; use crate::checksum; -use crate::create_main_worker; use crate::lsp::client::Client; use crate::lsp::client::TestingNotification; use crate::lsp::config; @@ -16,6 +15,7 @@ use crate::ops; use crate::proc_state; use crate::tools::test; use crate::tools::test::TestEventSender; +use crate::worker::create_main_worker_for_test_or_bench; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; @@ -154,7 +154,7 @@ async fn test_specifier( filter: test::TestFilter, ) -> Result<(), AnyError> { if !token.is_cancelled() { - let mut worker = create_main_worker( + let mut worker = create_main_worker_for_test_or_bench( &ps, specifier.clone(), permissions, diff --git a/cli/main.rs b/cli/main.rs index ed3d459e13..b91540c379 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -18,6 +18,7 @@ mod fs_util; mod graph_util; mod http_cache; mod http_util; +mod js; mod lockfile; mod logger; mod lsp; @@ -289,14 +290,8 @@ async fn eval_command( resolve_url_or_path(&format!("./$deno$eval.{}", eval_flags.ext))?; let permissions = Permissions::from_options(&flags.permissions_options())?; let ps = ProcState::build(flags).await?; - let mut worker = create_main_worker( - &ps, - main_module.clone(), - permissions, - vec![], - Default::default(), - ) - .await?; + let mut worker = + create_main_worker(&ps, main_module.clone(), permissions).await?; // Create a dummy source file. let source_code = if eval_flags.print { format!("console.log({})", eval_flags.code) @@ -602,8 +597,6 @@ async fn repl_command( &ps, main_module.clone(), Permissions::from_options(&ps.options.permissions_options())?, - vec![], - Default::default(), ) .await?; worker.setup_repl().await?; @@ -623,8 +616,6 @@ async fn run_from_stdin(flags: Flags) -> Result { &ps.clone(), main_module.clone(), Permissions::from_options(&ps.options.permissions_options())?, - vec![], - Default::default(), ) .await?; @@ -664,14 +655,8 @@ async fn run_with_watch(flags: Flags, script: String) -> Result { let ps = ProcState::build_for_file_watcher((*flags).clone(), sender.clone()) .await?; - let worker = create_main_worker( - &ps, - main_module.clone(), - permissions, - vec![], - Default::default(), - ) - .await?; + let worker = + create_main_worker(&ps, main_module.clone(), permissions).await?; worker.run_for_watcher().await?; Ok(()) @@ -722,14 +707,8 @@ async fn run_command( }; let permissions = Permissions::from_options(&ps.options.permissions_options())?; - let mut worker = create_main_worker( - &ps, - main_module.clone(), - permissions, - vec![], - Default::default(), - ) - .await?; + let mut worker = + create_main_worker(&ps, main_module.clone(), permissions).await?; let exit_code = worker.run().await?; Ok(exit_code) diff --git a/cli/standalone.rs b/cli/standalone.rs index 5e66bcb186..2742f9bbdb 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -287,7 +287,7 @@ pub async fn run( inspect: ps.options.is_inspecting(), }, 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, root_cert_store: Some(root_cert_store), diff --git a/cli/tests/testdata/test/steps/failing_steps.out b/cli/tests/testdata/test/steps/failing_steps.out index 1e5f2f64df..4df104bd70 100644 --- a/cli/tests/testdata/test/steps/failing_steps.out +++ b/cli/tests/testdata/test/steps/failing_steps.out @@ -37,13 +37,13 @@ failing step in failing test ... FAILED ([WILDCARD]) nested failure => ./test/steps/failing_steps.ts:[WILDCARD] error: Error: 1 test step failed. - at runTest (deno:runtime/js/40_testing.js:[WILDCARD]) - at async runTests (deno:runtime/js/40_testing.js:[WILDCARD]) + at runTest (deno:cli/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] error: Error: 2 test steps failed. - at runTest (deno:runtime/js/40_testing.js:[WILDCARD]) - at async runTests (deno:runtime/js/40_testing.js:[WILDCARD]) + at runTest (deno:cli/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] error: Error: Fail test. diff --git a/cli/tools/bench.rs b/cli/tools/bench.rs index c055d8a9c7..a81c0a406e 100644 --- a/cli/tools/bench.rs +++ b/cli/tools/bench.rs @@ -4,7 +4,6 @@ use crate::args::BenchFlags; use crate::args::Flags; use crate::args::TypeCheckMode; use crate::colors; -use crate::create_main_worker; use crate::file_watcher; use crate::file_watcher::ResolutionResult; use crate::fs_util::collect_specifiers; @@ -15,6 +14,7 @@ use crate::ops; use crate::proc_state::ProcState; use crate::tools::test::format_test_error; 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::AnyError; @@ -352,7 +352,7 @@ async fn bench_specifier( options: BenchSpecifierOptions, ) -> Result<(), AnyError> { let filter = TestFilter::from_flag(&options.filter); - let mut worker = create_main_worker( + let mut worker = create_main_worker_for_test_or_bench( &ps, specifier.clone(), permissions, diff --git a/cli/tools/test.rs b/cli/tools/test.rs index 09257efff2..1bb891a1ea 100644 --- a/cli/tools/test.rs +++ b/cli/tools/test.rs @@ -5,7 +5,6 @@ use crate::args::TestFlags; use crate::args::TypeCheckMode; use crate::checksum; use crate::colors; -use crate::create_main_worker; use crate::display; use crate::file_fetcher::File; use crate::file_watcher; @@ -18,6 +17,7 @@ use crate::graph_util::contains_specifier; use crate::graph_util::graph_valid; use crate::ops; 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::MediaType; @@ -715,7 +715,7 @@ async fn test_specifier( sender: &TestEventSender, options: TestSpecifierOptions, ) -> Result<(), AnyError> { - let mut worker = create_main_worker( + let mut worker = create_main_worker_for_test_or_bench( &ps, specifier.clone(), permissions, diff --git a/cli/worker.rs b/cli/worker.rs index 7fe1f3c0ba..d06864634b 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -7,6 +7,9 @@ use deno_core::error::AnyError; use deno_core::futures::task::LocalFutureObj; use deno_core::futures::FutureExt; 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::ModuleId; use deno_runtime::colors; @@ -38,6 +41,11 @@ pub struct CliMainWorker { is_main_cjs: bool, worker: MainWorker, ps: ProcState, + + js_run_tests_callback: Option>, + js_run_benchmarks_callback: Option>, + js_enable_test_callback: Option>, + js_enable_bench_callback: Option>, } impl CliMainWorker { @@ -168,7 +176,7 @@ impl CliMainWorker { &mut self, mode: TestMode, ) -> Result<(), AnyError> { - self.worker.enable_test(); + self.enable_test(); // Enable op call tracing in core to enable better debugging of op sanitizer // failures. @@ -194,10 +202,7 @@ impl CliMainWorker { } self.worker.dispatch_load_event(&located_script_name!())?; - self - .worker - .run_tests(&self.ps.options.shuffle_tests()) - .await?; + self.run_tests(&self.ps.options.shuffle_tests()).await?; loop { if !self .worker @@ -223,7 +228,7 @@ impl CliMainWorker { &mut self, mode: TestMode, ) -> Result<(), AnyError> { - self.worker.enable_test(); + self.enable_test(); self .worker @@ -239,7 +244,7 @@ impl CliMainWorker { } self.worker.dispatch_load_event(&located_script_name!())?; - self.worker.run_tests(&None).await?; + self.run_tests(&None).await?; loop { if !self .worker @@ -254,13 +259,13 @@ impl CliMainWorker { } 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. self.execute_side_module_possibly_with_npm().await?; self.worker.dispatch_load_event(&located_script_name!())?; - self.worker.run_benchmarks().await?; + self.run_benchmarks().await?; loop { if !self .worker @@ -340,14 +345,104 @@ impl CliMainWorker { 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, + ) -> 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( ps: &ProcState, main_module: ModuleSpecifier, permissions: Permissions, +) -> Result { + 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, + stdio: deno_runtime::ops::io::Stdio, +) -> Result { + 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, stdio: deno_runtime::ops::io::Stdio, + bench_or_test: bool, ) -> Result { let (main_module, is_main_cjs) = if let Ok(package_ref) = NpmPackageReference::from_specifier(&main_module) @@ -426,7 +521,7 @@ pub async fn create_main_worker( inspect: ps.options.is_inspecting(), }, extensions, - startup_snapshot: None, + startup_snapshot: Some(crate::js::deno_isolate_init()), unsafely_ignore_certificate_errors: ps .options .unsafely_ignore_certificate_errors() @@ -452,16 +547,59 @@ pub async fn create_main_worker( stdio, }; - let worker = MainWorker::bootstrap_from_options( + let mut worker = MainWorker::bootstrap_from_options( main_module.clone(), permissions, 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::( + scope, + "Deno[Deno.internal].testing.runTests", + ) + .unwrap(); + let js_run_benchmarks_callback = + deno_core::JsRuntime::eval::( + scope, + "Deno[Deno.internal].testing.runBenchmarks", + ) + .unwrap(); + let js_enable_tests_callback = deno_core::JsRuntime::eval::( + scope, + "Deno[Deno.internal].testing.enableTest", + ) + .unwrap(); + let js_enable_bench_callback = deno_core::JsRuntime::eval::( + 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 { main_module, is_main_cjs, worker, 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(), }, extensions, + startup_snapshot: Some(crate::js::deno_isolate_init()), unsafely_ignore_certificate_errors: ps .options .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()); + } +} diff --git a/core/lib.rs b/core/lib.rs index 607d90f0ba..011fa6c503 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -18,6 +18,7 @@ mod ops_builtin_v8; mod ops_metrics; mod resources; mod runtime; +pub mod snapshot_util; mod source_map; // Re-exports diff --git a/core/snapshot_util.rs b/core/snapshot_util.rs new file mode 100644 index 0000000000..b408bad564 --- /dev/null +++ b/core/snapshot_util.rs @@ -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]); + +pub struct CreateSnapshotOptions { + pub cargo_manifest_dir: &'static str, + pub snapshot_path: PathBuf, + pub startup_snapshot: Option, + pub extensions: Vec, + pub additional_files: Vec, + pub compression_cb: Option>, +} + +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> = + 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 { + 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::>(); + js_files.sort(); + js_files +} diff --git a/runtime/build.rs b/runtime/build.rs index 3feca3e765..bab7745cc3 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -9,63 +9,8 @@ use std::path::PathBuf; mod not_docs { use super::*; use deno_cache::SqliteBackedCache; + use deno_core::snapshot_util::*; 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, - ) { - // 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; @@ -174,7 +119,7 @@ mod not_docs { } } - fn create_runtime_snapshot(snapshot_path: &Path, files: Vec) { + fn create_runtime_snapshot(snapshot_path: PathBuf, files: Vec) { let extensions: Vec = vec![ deno_webidl::init(), deno_console::init(), @@ -205,31 +150,26 @@ mod not_docs { deno_flash::init::(false), // No --unstable ]; - let js_runtime = JsRuntime::new(RuntimeOptions { - will_snapshot: true, + create_snapshot(CreateSnapshotOptions { + cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"), + snapshot_path, + startup_snapshot: None, 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 { - 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::>(); - js_files.sort(); - js_files } pub fn build_snapshot(runtime_snapshot_path: PathBuf) { - let js_files = get_js_files("js"); - create_runtime_snapshot(&runtime_snapshot_path, js_files); + let js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js"); + create_runtime_snapshot(runtime_snapshot_path, js_files); } } @@ -242,7 +182,7 @@ fn main() { let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); // 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 // and empty snapshot file and return, because `rusty_v8` diff --git a/runtime/js.rs b/runtime/js.rs index 54a08d9b42..3ad2416700 100644 --- a/runtime/js.rs +++ b/runtime/js.rs @@ -2,17 +2,18 @@ use deno_core::Snapshot; use log::debug; use once_cell::sync::Lazy; +use std::path::PathBuf; -pub static CLI_SNAPSHOT: Lazy> = Lazy::new( +pub static RUNTIME_SNAPSHOT: Lazy> = Lazy::new( #[allow(clippy::uninit_vec)] #[cold] #[inline(never)] || { - static COMPRESSED_CLI_SNAPSHOT: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin")); + static COMPRESSED_RUNTIME_SNAPSHOT: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/RUNTIME_SNAPSHOT.bin")); 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; let mut vec = Vec::with_capacity(size); @@ -22,7 +23,8 @@ pub static CLI_SNAPSHOT: Lazy> = Lazy::new( 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() }, @@ -30,29 +32,11 @@ pub static CLI_SNAPSHOT: Lazy> = Lazy::new( pub fn deno_isolate_init() -> Snapshot { debug!("Deno isolate init with snapshots."); - Snapshot::Static(&CLI_SNAPSHOT) + Snapshot::Static(&RUNTIME_SNAPSHOT) } -#[cfg(test)] -mod tests { - use super::*; - - #[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( - "", - r#" - if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) { - throw Error("bad"); - } - console.log("we have console.log!!!"); - "#, - ) - .unwrap(); - } +pub fn get_99_main() -> PathBuf { + let manifest = env!("CARGO_MANIFEST_DIR"); + let path = PathBuf::from(manifest); + path.join("js").join("99_main.js") } diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 16dd5c72f7..cd6c07464c 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -7,8 +7,6 @@ __bootstrap.denoNs = { metrics: core.metrics, - test: __bootstrap.testing.test, - bench: __bootstrap.testing.bench, Process: __bootstrap.process.Process, run: __bootstrap.process.run, isatty: __bootstrap.tty.isatty, diff --git a/runtime/js/98_global_scope.js b/runtime/js/98_global_scope.js new file mode 100644 index 0000000000..7afb4472c7 --- /dev/null +++ b/runtime/js/98_global_scope.js @@ -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); diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index c7faefcdda..2ea3504e2c 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -41,48 +41,37 @@ delete Intl.v8BreakIterator; const util = window.__bootstrap.util; const event = window.__bootstrap.event; const eventTarget = window.__bootstrap.eventTarget; - const globalInterfaces = window.__bootstrap.globalInterfaces; const location = window.__bootstrap.location; const build = window.__bootstrap.build; const version = window.__bootstrap.version; const os = window.__bootstrap.os; const timers = window.__bootstrap.timers; - const base64 = window.__bootstrap.base64; - const encoding = window.__bootstrap.encoding; const colors = window.__bootstrap.colors; - const Console = window.__bootstrap.console.Console; - const caches = window.__bootstrap.caches; const inspectArgs = window.__bootstrap.console.inspectArgs; const quoteString = window.__bootstrap.console.quoteString; - const compression = window.__bootstrap.compression; - const worker = window.__bootstrap.worker; const internals = window.__bootstrap.internals; const performance = window.__bootstrap.performance; const net = window.__bootstrap.net; - 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 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 prompt = window.__bootstrap.prompt; const messagePort = window.__bootstrap.messagePort; const denoNs = window.__bootstrap.denoNs; const denoNsUnstable = window.__bootstrap.denoNsUnstable; const errors = window.__bootstrap.errors.errors; const webidl = window.__bootstrap.webidl; const domException = window.__bootstrap.domException; - const abortSignal = window.__bootstrap.abortSignal; const { defineEventHandler, reportException } = window.__bootstrap.event; const { deserializeJsMessageData, serializeJsMessageData } = window.__bootstrap.messagePort; + const { + windowOrWorkerGlobalScope, + unstableWindowOrWorkerGlobalScope, + workerRuntimeGlobalProperties, + mainRuntimeGlobalProperties, + setNumCpus, + setUserAgent, + setLanguage, + } = window.__bootstrap.globalScope; 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 pendingRejectionsReasons = new SafeWeakMap(); @@ -726,6 +423,10 @@ delete Intl.v8BreakIterator; ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope); } ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties); + ObjectDefineProperties(globalThis, { + close: util.writable(windowClose), + closed: util.getterOnly(() => windowIsClosing), + }); ObjectSetPrototypeOf(globalThis, Window.prototype); if (runtimeOptions.inspectFlag) { @@ -754,9 +455,9 @@ delete Intl.v8BreakIterator; runtimeStart(runtimeOptions); - numCpus = runtimeOptions.cpuCount; - userAgent = runtimeOptions.userAgent; - language = runtimeOptions.locale; + setNumCpus(runtimeOptions.cpuCount); + setUserAgent(runtimeOptions.userAgent); + setLanguage(runtimeOptions.locale); const internalSymbol = Symbol("Deno.internal"); @@ -849,7 +550,12 @@ delete Intl.v8BreakIterator; ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope); } 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) { ObjectDefineProperty( globalThis, @@ -882,8 +588,9 @@ delete Intl.v8BreakIterator; ); location.setLocationHref(runtimeOptions.location); - numCpus = runtimeOptions.cpuCount; - language = runtimeOptions.locale; + + setNumCpus(runtimeOptions.cpuCount); + setLanguage(runtimeOptions.locale); globalThis.pollForMessages = pollForMessages; diff --git a/runtime/lib.rs b/runtime/lib.rs index d0d27b71c5..de104f2ca9 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -7,6 +7,7 @@ pub use deno_core; pub use deno_crypto; pub use deno_fetch; pub use deno_ffi; +pub use deno_flash; pub use deno_http; pub use deno_napi; pub use deno_net; diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 10d7e440ed..a8e0842f22 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -17,7 +17,6 @@ use deno_core::futures::channel::mpsc; use deno_core::futures::future::poll_fn; use deno_core::futures::stream::StreamExt; use deno_core::futures::task::AtomicWaker; -use deno_core::located_script_name; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json::json; @@ -33,6 +32,7 @@ use deno_core::ModuleSpecifier; use deno_core::RuntimeOptions; use deno_core::SharedArrayBufferStore; use deno_core::SourceMapGetter; +use deno_core::{located_script_name, Snapshot}; use deno_node::RequireNpmResolver; use deno_tls::rustls::RootCertStore; use deno_web::create_entangled_message_port; @@ -322,6 +322,7 @@ pub struct WebWorker { pub struct WebWorkerOptions { pub bootstrap: BootstrapOptions, pub extensions: Vec, + pub startup_snapshot: Option, pub unsafely_ignore_certificate_errors: Option>, pub root_cert_store: Option, pub seed: Option, @@ -451,7 +452,11 @@ impl WebWorker { let mut js_runtime = JsRuntime::new(RuntimeOptions { 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, get_error_class_fn: options.get_error_class_fn, shared_array_buffer_store: options.shared_array_buffer_store.clone(), diff --git a/runtime/worker.rs b/runtime/worker.rs index c7226b76b7..0c439c703b 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -13,9 +13,6 @@ use deno_core::error::AnyError; use deno_core::error::JsError; use deno_core::futures::Future; 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::Extension; use deno_core::FsModuleLoader; @@ -66,10 +63,6 @@ pub struct MainWorker { pub js_runtime: JsRuntime, should_break_on_first_statement: bool, exit_code: ExitCode, - js_run_tests_callback: v8::Global, - js_run_benchmarks_callback: v8::Global, - js_enable_test_callback: v8::Global, - js_enable_bench_callback: v8::Global, } pub struct WorkerOptions { @@ -99,15 +92,6 @@ pub struct WorkerOptions { pub stdio: Stdio, } -fn grab_cb( - scope: &mut v8::HandleScope, - path: &str, -) -> v8::Global { - let cb = JsRuntime::eval::(scope, path) - .unwrap_or_else(|| panic!("{} must be defined", path)); - v8::Global::new(scope, cb) -} - impl Default for WorkerOptions { fn default() -> 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 { js_runtime, should_break_on_first_statement: options.should_break_on_first_statement, 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 } - /// 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, - ) -> 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) { if self.should_break_on_first_statement { self @@ -542,107 +448,3 @@ impl MainWorker { 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()); - } -}