diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2022-07-05 00:12:41 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-05 00:12:41 +0200 |
commit | a919a5dd1167b79f267cbed7312b3a7d296d429f (patch) | |
tree | 73b7b1a8b5daef3ab7ad9c07423c4907a23a0269 /cli | |
parent | 06934db883e9ae64c7603f98051676cbcf90994f (diff) |
Revert "refactor(snapshots): to their own crate (#14794)" (#15076)
This reverts commit fd5a12d7e25dc53238e2bbcffe970e646c1035f3.
Diffstat (limited to 'cli')
-rw-r--r-- | cli/Cargo.toml | 13 | ||||
-rw-r--r-- | cli/build.rs | 385 | ||||
-rw-r--r-- | cli/lsp/tsc.rs | 2 | ||||
-rw-r--r-- | cli/main.rs | 2 | ||||
-rw-r--r-- | cli/standalone.rs | 1 | ||||
-rw-r--r-- | cli/tsc.rs | 28 |
6 files changed, 376 insertions, 55 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a82ec5644..094b2ce01 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,7 +25,17 @@ harness = false path = "./bench/lsp_bench_standalone.rs" [build-dependencies] -deno_runtime = { version = "0.67.0", path = "../runtime" } +deno_broadcast_channel = { version = "0.53.0", path = "../ext/broadcast_channel" } +deno_console = { version = "0.59.0", path = "../ext/console" } +deno_core = { version = "0.141.0", path = "../core" } +deno_crypto = { version = "0.73.0", path = "../ext/crypto" } +deno_fetch = { version = "0.82.0", path = "../ext/fetch" } +deno_net = { version = "0.51.0", path = "../ext/net" } +deno_url = { version = "0.59.0", path = "../ext/url" } +deno_web = { version = "0.90.0", path = "../ext/web" } +deno_webgpu = { version = "0.60.0", path = "../ext/webgpu" } +deno_websocket = { version = "0.64.0", path = "../ext/websocket" } +deno_webstorage = { version = "0.54.0", path = "../ext/webstorage" } regex = "=1.5.6" serde = { version = "=1.0.136", features = ["derive"] } zstd = '=0.11.1' @@ -42,7 +52,6 @@ deno_emit = "0.3.0" deno_graph = "0.28.0" deno_lint = { version = "0.31.0", features = ["docs"] } deno_runtime = { version = "0.67.0", path = "../runtime" } -deno_snapshots = { version = "0.1.0", path = "../snapshots" } deno_task_shell = "0.4.0" atty = "=0.2.14" diff --git a/cli/build.rs b/cli/build.rs index 095efa22f..1a4eaa425 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -1,19 +1,323 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use deno_runtime::deno_broadcast_channel; -use deno_runtime::deno_console; -use deno_runtime::deno_crypto; -use deno_runtime::deno_fetch; -use deno_runtime::deno_net; -use deno_runtime::deno_url; -use deno_runtime::deno_web; -use deno_runtime::deno_websocket; -use deno_runtime::deno_webstorage; - +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<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(), + ); + + vec.extend_from_slice( + &zstd::bulk::compress(snapshot_slice, 22) + .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()); +} + +#[derive(Debug, Deserialize)] +struct LoadArgs { + /// The fully qualified specifier that should be loaded. + specifier: String, +} + +fn create_compiler_snapshot( + snapshot_path: &Path, + files: Vec<PathBuf>, + cwd: &Path, +) { + // libs that are being provided by op crates. + let mut op_crate_libs = HashMap::new(); + 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. + for (_, path) in op_crate_libs.iter() { + println!("cargo:rerun-if-changed={}", path.display()); + } + + // 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", + ]; + + 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() + ); + } + + // 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] + fn op_build_info(state: &mut OpState) -> Value { + 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_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!({ + "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), + )) + } + } + 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_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); +} + +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::<String>() +} + +fn git_commit_hash() -> String { + if let Ok(output) = std::process::Command::new("git") + .arg("rev-list") + .arg("-1") + .arg("HEAD") + .output() + { + if output.status.success() { + std::str::from_utf8(&output.stdout[..40]) + .unwrap() + .to_string() + } else { + // When not in git repository + // (e.g. when the user install by `cargo install deno`) + "UNKNOWN".to_string() + } + } else { + // When there is no git command for some reason + "UNKNOWN".to_string() + } +} + fn main() { // Skip building from docs.rs. if env::var_os("DOCS_RS").is_some() { @@ -26,9 +330,8 @@ fn main() { if target != host { panic!("Cross compiling with snapshot is not supported."); } - - println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap()); - println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); + // To debug snapshot issues uncomment: + // op_fetch_asset::trace_serializer(); if let Ok(c) = env::var("DENO_CANARY") { println!("cargo:rustc-env=DENO_CANARY={}", c); @@ -82,6 +385,18 @@ fn main() { deno_net::get_declaration().display() ); + println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap()); + println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); + + 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("tsc"); + create_compiler_snapshot(&compiler_snapshot_path, js_files, &c); + #[cfg(target_os = "windows")] { let mut res = winres::WindowsResource::new(); @@ -99,38 +414,16 @@ fn deno_webgpu_get_declaration() -> PathBuf { manifest_dir.join("dts").join("lib.deno_webgpu.d.ts") } -fn git_commit_hash() -> String { - if let Ok(output) = std::process::Command::new("git") - .arg("rev-list") - .arg("-1") - .arg("HEAD") - .output() - { - if output.status.success() { - std::str::from_utf8(&output.stdout[..40]) - .unwrap() - .to_string() - } else { - // When not in git repository - // (e.g. when the user install by `cargo install deno`) - "UNKNOWN".to_string() - } - } else { - // When there is no git command for some reason - "UNKNOWN".to_string() - } -} - -fn ts_version() -> String { - std::fs::read_to_string("tsc/00_typescript.js") +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() - .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::<String>() + .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 } diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 9988a0ac9..c362d4ce3 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -2618,7 +2618,7 @@ fn op_script_version( fn js_runtime(performance: Arc<Performance>) -> JsRuntime { JsRuntime::new(RuntimeOptions { extensions: vec![init_extension(performance)], - startup_snapshot: Some(deno_snapshots::tsc_snapshot()), + startup_snapshot: Some(tsc::compiler_snapshot()), ..Default::default() }) } diff --git a/cli/main.rs b/cli/main.rs index 2747d763c..18eb58ff5 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -181,7 +181,6 @@ fn create_web_worker_callback( shared_array_buffer_store: Some(ps.shared_array_buffer_store.clone()), compiled_wasm_module_store: Some(ps.compiled_wasm_module_store.clone()), stdio: stdio.clone(), - startup_snapshot: Some(deno_snapshots::cli_snapshot()), }; WebWorker::bootstrap_from_options( @@ -263,7 +262,6 @@ pub fn create_main_worker( shared_array_buffer_store: Some(ps.shared_array_buffer_store.clone()), compiled_wasm_module_store: Some(ps.compiled_wasm_module_store.clone()), stdio, - startup_snapshot: Some(deno_snapshots::cli_snapshot()), }; MainWorker::bootstrap_from_options(main_module, permissions, options) diff --git a/cli/standalone.rs b/cli/standalone.rs index d130fbe2e..cd5390d2d 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -303,7 +303,6 @@ pub async fn run( shared_array_buffer_store: None, compiled_wasm_module_store: None, stdio: Default::default(), - startup_snapshot: Some(deno_snapshots::cli_snapshot()), }; let mut worker = MainWorker::bootstrap_from_options( main_module.clone(), diff --git a/cli/tsc.rs b/cli/tsc.rs index 95fdd305a..4065c6354 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -24,6 +24,7 @@ use deno_core::JsRuntime; use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_core::RuntimeOptions; +use deno_core::Snapshot; use deno_graph::Resolved; use once_cell::sync::Lazy; use std::collections::HashMap; @@ -51,6 +52,27 @@ pub static SHARED_GLOBALS_LIB: &str = pub static WINDOW_LIB: &str = include_str!("dts/lib.deno.window.d.ts"); pub static UNSTABLE_NS_LIB: &str = include_str!("dts/lib.deno.unstable.d.ts"); +pub static COMPILER_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new( + #[cold] + #[inline(never)] + || { + static COMPRESSED_COMPILER_SNAPSHOT: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin")); + + zstd::bulk::decompress( + &COMPRESSED_COMPILER_SNAPSHOT[4..], + u32::from_le_bytes(COMPRESSED_COMPILER_SNAPSHOT[0..4].try_into().unwrap()) + as usize, + ) + .unwrap() + .into_boxed_slice() + }, +); + +pub fn compiler_snapshot() -> Snapshot { + Snapshot::Static(&*COMPILER_SNAPSHOT) +} + macro_rules! inc { ($e:expr) => { include_str!(concat!("dts/", $e)) @@ -635,7 +657,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> { }) .collect(); let mut runtime = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(deno_snapshots::tsc_snapshot()), + startup_snapshot: Some(compiler_snapshot()), extensions: vec![Extension::builder() .ops(vec![ op_cwd::decl(), @@ -819,9 +841,9 @@ mod tests { } #[test] - fn test_tsc_snapshot() { + fn test_compiler_snapshot() { let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions { - startup_snapshot: Some(deno_snapshots::tsc_snapshot()), + startup_snapshot: Some(compiler_snapshot()), ..Default::default() }); js_runtime |