From f42505215d9669735bba7c58975b54a72cfe1f08 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Sun, 29 Nov 2020 03:29:13 +0100 Subject: [PATCH] feat: deno compile --- cli/main.rs | 4 +- cli/standalone.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++++ cli/worker.rs | 12 +++++ compile.ts | 26 ++++++++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 cli/standalone.rs create mode 100644 compile.ts diff --git a/cli/main.rs b/cli/main.rs index de2e1b40251ee8..f7c7f3600ac6ad 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -39,6 +39,7 @@ mod resolve_addr; mod signal; mod source_maps; mod specifier_handler; +mod standalone; mod text_encoding; mod tokio_util; mod tools; @@ -967,8 +968,9 @@ pub fn main() { colors::enable_ansi(); // For Windows 10 let args: Vec = env::args().collect(); - let flags = flags::flags_from_vec(args); + standalone::standalone(); + let flags = flags::flags_from_vec(args); if let Some(ref v8_flags) = flags.v8_flags { init_v8_flags(v8_flags); } diff --git a/cli/standalone.rs b/cli/standalone.rs new file mode 100644 index 00000000000000..79657748c17d08 --- /dev/null +++ b/cli/standalone.rs @@ -0,0 +1,123 @@ +use std::cell::RefCell; +use std::convert::TryInto; +use std::env::current_exe; +use std::fs::File; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::pin::Pin; +use std::rc::Rc; + +use deno_core::error::AnyError; +use deno_core::futures::FutureExt; +use deno_core::ModuleLoader; +use deno_core::ModuleSpecifier; +use deno_core::OpState; + +use crate::colors; +use crate::flags::Flags; +use crate::permissions::Permissions; +use crate::program_state::ProgramState; +use crate::tokio_util; +use crate::worker::MainWorker; + +pub fn standalone() { + let current_exe_path = + current_exe().expect("expect current exe path to be known"); + + let mut current_exe = File::open(current_exe_path) + .expect("expected to be able to open current exe"); + let magic_trailer_pos = current_exe + .seek(SeekFrom::End(-12)) + .expect("expected to be able to seek to magic trailer in current exe"); + let mut magic_trailer = [0; 12]; + current_exe + .read_exact(&mut magic_trailer) + .expect("expected to be able to read magic trailer from current exe"); + let (magic_trailer, bundle_pos) = magic_trailer.split_at(4); + if magic_trailer == b"DENO" { + let bundle_pos_arr: &[u8; 8] = + bundle_pos.try_into().expect("slice with incorrect length"); + let bundle_pos = u64::from_be_bytes(*bundle_pos_arr); + println!( + "standalone bin! bundle starting at {} and ending at {}.", + bundle_pos, + magic_trailer_pos - 1 + ); + current_exe + .seek(SeekFrom::Start(bundle_pos)) + .expect("expected to be able to seek to bundle pos in current exe"); + + let bundle_len = magic_trailer_pos - bundle_pos; + let mut bundle = String::new(); + current_exe + .take(bundle_len) + .read_to_string(&mut bundle) + .expect("expected to be able to read bundle from current exe"); + // TODO: check amount of bytes read + + // println!("standalone bin bundle:\n{}", bundle); + + let result = tokio_util::run_basic(run(bundle)); + if let Err(err) = result { + eprintln!("{}: {}", colors::red_bold("error"), err.to_string()); + std::process::exit(1); + } + std::process::exit(0); + } +} + +const SPECIFIER: &str = "file://$deno$/bundle.js"; + +struct EmbeddedModuleLoader(String); + +impl ModuleLoader for EmbeddedModuleLoader { + fn resolve( + &self, + _op_state: Rc>, + specifier: &str, + _referrer: &str, + _is_main: bool, + ) -> Result { + assert_eq!(specifier, SPECIFIER); + Ok(ModuleSpecifier::resolve_url(specifier)?) + } + + fn load( + &self, + _op_state: Rc>, + module_specifier: &ModuleSpecifier, + _maybe_referrer: Option, + _is_dynamic: bool, + ) -> Pin> { + let module_specifier = module_specifier.clone(); + let code = self.0.to_string(); + async move { + Ok(deno_core::ModuleSource { + code, + module_url_specified: module_specifier.to_string(), + module_url_found: module_specifier.to_string(), + }) + } + .boxed_local() + } +} + +async fn run(source_code: String) -> Result<(), AnyError> { + let flags = Flags::default(); + let main_module = ModuleSpecifier::resolve_url(SPECIFIER)?; + let program_state = ProgramState::new(flags.clone())?; + let permissions = Permissions::allow_all(); + let module_loader = Rc::new(EmbeddedModuleLoader(source_code)); + let mut worker = MainWorker::from_options( + &program_state, + main_module.clone(), + permissions, + module_loader, + ); + worker.execute_module(&main_module).await?; + worker.execute("window.dispatchEvent(new Event('load'))")?; + worker.run_event_loop().await?; + worker.execute("window.dispatchEvent(new Event('unload'))")?; + Ok(()) +} diff --git a/cli/worker.rs b/cli/worker.rs index f4a919df6aadc6..68f0a2210e570e 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -17,9 +17,11 @@ use deno_core::futures::future::FutureExt; use deno_core::url::Url; use deno_core::JsRuntime; use deno_core::ModuleId; +use deno_core::ModuleLoader; use deno_core::ModuleSpecifier; use deno_core::RuntimeOptions; use std::env; +use std::rc::Rc; use std::sync::Arc; use std::task::Context; use std::task::Poll; @@ -45,6 +47,16 @@ impl MainWorker { ) -> Self { let module_loader = CliModuleLoader::new(program_state.maybe_import_map.clone()); + + Self::from_options(program_state, main_module, permissions, module_loader) + } + + pub fn from_options( + program_state: &Arc, + main_module: ModuleSpecifier, + permissions: Permissions, + module_loader: Rc, + ) -> Self { let global_state_ = program_state.clone(); let js_error_create_fn = Box::new(move |core_js_error| { diff --git a/compile.ts b/compile.ts new file mode 100644 index 00000000000000..bc7f03bce743df --- /dev/null +++ b/compile.ts @@ -0,0 +1,26 @@ +const specifier = Deno.args[0]; +const out = Deno.args[1]; +const [_, bundle] = await Deno.bundle(specifier); + +const originalBinaryPath = Deno.execPath(); +const originalBin = await Deno.readFile(originalBinaryPath); + +const encoder = new TextEncoder(); +const bundleData = encoder.encode(bundle); +const magicTrailer = new Uint8Array(12); +magicTrailer.set(encoder.encode("DENO"), 0); +new DataView(magicTrailer.buffer).setBigUint64( + 4, + BigInt(originalBin.byteLength), + false, +); + +var newBin = new Uint8Array( + originalBin.byteLength + bundleData.byteLength + magicTrailer.byteLength, +); +newBin.set(originalBin, 0); +newBin.set(bundleData, originalBin.byteLength); +newBin.set(magicTrailer, originalBin.byteLength + bundleData.byteLength); + +await Deno.writeFile(out, newBin); +console.log(`Created binary at ${out}`);