diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs index e381ba16b6..4a5123a731 100644 --- a/bench_util/js_runtime.rs +++ b/bench_util/js_runtime.rs @@ -103,7 +103,8 @@ pub fn bench_js_async_with( opts.benching_inner }; let looped = loop_code(inner_iters, src); - let src = looped.as_ref(); + // Get a &'static str by leaking -- this is fine because it's benchmarking code + let src = Box::leak(looped.into_boxed_str()); if is_profiling() { for _ in 0..opts.profiling_outer { tokio_runtime.block_on(inner_async(src, &mut runtime)); @@ -115,7 +116,7 @@ pub fn bench_js_async_with( } } -async fn inner_async(src: &str, runtime: &mut JsRuntime) { +async fn inner_async(src: &'static str, runtime: &mut JsRuntime) { runtime.execute_script("inner_loop", src).unwrap(); runtime.run_event_loop(false).await.unwrap(); } diff --git a/cli/emit.rs b/cli/emit.rs index d322fe38e6..f69f70cc73 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -5,6 +5,7 @@ use crate::cache::FastInsecureHasher; use crate::cache::ParsedSourceCache; use deno_core::error::AnyError; +use deno_core::ModuleCode; use deno_core::ModuleSpecifier; use deno_graph::MediaType; use std::sync::Arc; @@ -27,11 +28,11 @@ pub fn emit_parsed_source( source: &Arc, emit_options: &deno_ast::EmitOptions, emit_config_hash: u64, -) -> Result { +) -> Result { let source_hash = get_source_hash(source, emit_config_hash); if let Some(emit_code) = emit_cache.get_emit_code(specifier, source_hash) { - Ok(emit_code) + Ok(emit_code.into()) } else { // this will use a cached version if it exists let parsed_source = parsed_source_cache.get_or_parse_module( @@ -42,6 +43,6 @@ pub fn emit_parsed_source( let transpiled_source = parsed_source.transpile(emit_options)?; debug_assert!(transpiled_source.source_map.is_none()); emit_cache.set_emit_code(specifier, source_hash, &transpiled_source.text); - Ok(transpiled_source.text) + Ok(transpiled_source.text.into()) } } diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index c8d8103b9d..fedaae5884 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -2854,7 +2854,7 @@ fn start(runtime: &mut JsRuntime, debug: bool) -> Result<(), AnyError> { let init_config = json!({ "debug": debug }); let init_src = format!("globalThis.serverInit({init_config});"); - runtime.execute_script(&located_script_name!(), &init_src)?; + runtime.execute_script(located_script_name!(), init_src)?; Ok(()) } @@ -3442,7 +3442,7 @@ pub fn request( }; let mark = performance.mark("request", Some(request_params.clone())); let request_src = format!("globalThis.serverRequest({request_params});"); - runtime.execute_script(&located_script_name!(), &request_src)?; + runtime.execute_script(located_script_name!(), request_src)?; let op_state = runtime.op_state(); let mut op_state = op_state.borrow_mut(); diff --git a/cli/module_loader.rs b/cli/module_loader.rs index c5224a8b90..7f6101d809 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -14,6 +14,7 @@ use deno_core::error::AnyError; use deno_core::futures::future::FutureExt; use deno_core::futures::Future; use deno_core::resolve_url; +use deno_core::ModuleCode; use deno_core::ModuleLoader; use deno_core::ModuleSource; use deno_core::ModuleSpecifier; @@ -30,7 +31,7 @@ use std::rc::Rc; use std::str; struct ModuleCodeSource { - pub code: String, + pub code: ModuleCode, pub found_url: ModuleSpecifier, pub media_type: MediaType, } @@ -91,7 +92,7 @@ impl CliModuleLoader { specifier, .. })) => Ok(ModuleCodeSource { - code: source.to_string(), + code: source.into(), found_url: specifier.clone(), media_type: *media_type, }), @@ -101,13 +102,15 @@ impl CliModuleLoader { specifier, .. })) => { - let code = match media_type { + let code: ModuleCode = match media_type { MediaType::JavaScript | MediaType::Unknown | MediaType::Cjs | MediaType::Mjs - | MediaType::Json => source.to_string(), - MediaType::Dts | MediaType::Dcts | MediaType::Dmts => "".to_string(), + | MediaType::Json => source.into(), + MediaType::Dts | MediaType::Dcts | MediaType::Dmts => { + Default::default() + } MediaType::TypeScript | MediaType::Mts | MediaType::Cts @@ -191,7 +194,7 @@ impl CliModuleLoader { )? }; ModuleCodeSource { - code, + code: code.into(), found_url: specifier.clone(), media_type: MediaType::from_specifier(specifier), } @@ -208,7 +211,7 @@ impl CliModuleLoader { code_without_source_map(code_source.code) }; Ok(ModuleSource { - code: code.into_bytes().into_boxed_slice(), + code, module_url_specified: specifier.to_string(), module_url_found: code_source.found_url.to_string(), module_type: match code_source.media_type { diff --git a/cli/standalone.rs b/cli/standalone.rs index 8f74d50a80..254cb9de51 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -178,7 +178,7 @@ impl ModuleLoader for EmbeddedModuleLoader { async move { if let Some((source, _)) = is_data_uri { return Ok(deno_core::ModuleSource { - code: source.into_bytes().into_boxed_slice(), + code: source.into(), module_type: deno_core::ModuleType::JavaScript, module_url_specified: module_specifier.to_string(), module_url_found: module_specifier.to_string(), @@ -192,7 +192,7 @@ impl ModuleLoader for EmbeddedModuleLoader { .to_owned(); Ok(deno_core::ModuleSource { - code: code.into_bytes().into_boxed_slice(), + code: code.into(), module_type: match module.kind { eszip::ModuleKind::JavaScript => deno_core::ModuleType::JavaScript, eszip::ModuleKind::Json => deno_core::ModuleType::Json, @@ -384,16 +384,16 @@ pub async fn run( options, ); worker.execute_main_module(main_module).await?; - worker.dispatch_load_event(&located_script_name!())?; + worker.dispatch_load_event(located_script_name!())?; loop { worker.run_event_loop(false).await?; - if !worker.dispatch_beforeunload_event(&located_script_name!())? { + if !worker.dispatch_beforeunload_event(located_script_name!())? { break; } } - worker.dispatch_unload_event(&located_script_name!())?; + worker.dispatch_unload_event(located_script_name!())?; std::process::exit(0); } diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index eaa0871719..9fead6e370 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -20,6 +20,7 @@ use deno_core::serde_json; use deno_core::sourcemap::SourceMap; use deno_core::url::Url; use deno_core::LocalInspectorSession; +use deno_core::ModuleCode; use regex::Regex; use std::fs; use std::fs::File; @@ -170,16 +171,16 @@ struct CoverageReport { fn generate_coverage_report( script_coverage: &ScriptCoverage, - script_source: &str, + script_source: String, maybe_source_map: &Option>, output: &Option, ) -> CoverageReport { let maybe_source_map = maybe_source_map .as_ref() .map(|source_map| SourceMap::from_slice(source_map).unwrap()); - let text_lines = TextLines::new(script_source); + let text_lines = TextLines::new(&script_source); - let comment_ranges = deno_ast::lex(script_source, MediaType::JavaScript) + let comment_ranges = deno_ast::lex(&script_source, MediaType::JavaScript) .into_iter() .filter(|item| { matches!(item.inner, deno_ast::TokenOrComment::Comment { .. }) @@ -680,14 +681,14 @@ pub async fn cover_files( })?; // Check if file was transpiled - let original_source = &file.source; - let transpiled_code = match file.media_type { + let original_source = file.source.clone(); + let transpiled_code: ModuleCode = match file.media_type { MediaType::JavaScript | MediaType::Unknown | MediaType::Cjs | MediaType::Mjs - | MediaType::Json => file.source.as_ref().to_string(), - MediaType::Dts | MediaType::Dmts | MediaType::Dcts => "".to_string(), + | MediaType::Json => file.source.into(), + MediaType::Dts | MediaType::Dmts | MediaType::Dcts => Default::default(), MediaType::TypeScript | MediaType::Jsx | MediaType::Mts @@ -695,7 +696,7 @@ pub async fn cover_files( | MediaType::Tsx => { let source_hash = get_source_hash(&file.source, ps.emit_options_hash); match ps.emit_cache.get_emit_code(&file.specifier, source_hash) { - Some(code) => code, + Some(code) => code.into(), None => { return Err(anyhow!( "Missing transpiled source code for: \"{}\". @@ -710,15 +711,16 @@ pub async fn cover_files( } }; + let source_map = source_map_from_code(&transpiled_code); let coverage_report = generate_coverage_report( &script_coverage, - &transpiled_code, - &source_map_from_code(&transpiled_code), + transpiled_code.take_as_string(), + &source_map, &out_mode, ); if !coverage_report.found_lines.is_empty() { - reporter.report(&coverage_report, original_source)?; + reporter.report(&coverage_report, &original_source)?; } } diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 2f2015542c..791aa64099 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -824,9 +824,9 @@ pub fn exec(request: Request) -> Result { }); runtime - .execute_script(&located_script_name!(), startup_source) + .execute_script(located_script_name!(), startup_source) .context("Could not properly start the compiler runtime.")?; - runtime.execute_script(&located_script_name!(), &exec_source)?; + runtime.execute_script(located_script_name!(), exec_source)?; let op_state = runtime.op_state(); let mut op_state = op_state.borrow_mut(); diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs index 87067e9094..0111ec82f6 100644 --- a/cli/util/text_encoding.rs +++ b/cli/util/text_encoding.rs @@ -1,5 +1,6 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use deno_core::ModuleCode; use encoding_rs::*; use std::borrow::Cow; use std::io::Error; @@ -53,11 +54,12 @@ pub fn strip_bom(text: &str) -> &str { } } -static SOURCE_MAP_PREFIX: &str = - "//# sourceMappingURL=data:application/json;base64,"; +static SOURCE_MAP_PREFIX: &[u8] = + b"//# sourceMappingURL=data:application/json;base64,"; -pub fn source_map_from_code(code: &str) -> Option> { - let last_line = code.rsplit(|u| u == '\n').next()?; +pub fn source_map_from_code(code: &ModuleCode) -> Option> { + let bytes = code.as_bytes(); + let last_line = bytes.rsplit(|u| *u == b'\n').next()?; if last_line.starts_with(SOURCE_MAP_PREFIX) { let input = last_line.split_at(SOURCE_MAP_PREFIX.len()).1; let decoded_map = base64::decode(input) @@ -68,17 +70,18 @@ pub fn source_map_from_code(code: &str) -> Option> { } } -pub fn code_without_source_map(mut code: String) -> String { - if let Some(last_line_index) = code.rfind('\n') { - if code[last_line_index + 1..].starts_with(SOURCE_MAP_PREFIX) { - code.truncate(last_line_index + 1); - code - } else { - code +/// Truncate the source code before the source map. +pub fn code_without_source_map(mut code: ModuleCode) -> ModuleCode { + let bytes = code.as_bytes(); + for i in (0..bytes.len()).rev() { + if bytes[i] == b'\n' { + if bytes[i + 1..].starts_with(SOURCE_MAP_PREFIX) { + code.truncate(i + 1); + } + return code; } - } else { - code } + code } #[cfg(test)] @@ -155,8 +158,11 @@ mod tests { "\n", ); - fn run_test(input: &str, output: &str) { - assert_eq!(code_without_source_map(input.to_string()), output); + fn run_test(input: &'static str, output: &'static str) { + assert_eq!( + code_without_source_map(input.into()).take_as_string(), + output + ); } } } diff --git a/cli/worker.rs b/cli/worker.rs index c505516a07..a0168a1f3a 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -78,7 +78,7 @@ impl CliMainWorker { self.execute_main_module_possibly_with_npm().await?; } - self.worker.dispatch_load_event(&located_script_name!())?; + self.worker.dispatch_load_event(located_script_name!())?; loop { self @@ -87,13 +87,13 @@ impl CliMainWorker { .await?; if !self .worker - .dispatch_beforeunload_event(&located_script_name!())? + .dispatch_beforeunload_event(located_script_name!())? { break; } } - self.worker.dispatch_unload_event(&located_script_name!())?; + self.worker.dispatch_unload_event(located_script_name!())?; if let Some(coverage_collector) = maybe_coverage_collector.as_mut() { self @@ -129,7 +129,7 @@ impl CliMainWorker { self .inner .worker - .dispatch_load_event(&located_script_name!())?; + .dispatch_load_event(located_script_name!())?; self.pending_unload = true; let result = loop { @@ -140,7 +140,7 @@ impl CliMainWorker { match self .inner .worker - .dispatch_beforeunload_event(&located_script_name!()) + .dispatch_beforeunload_event(located_script_name!()) { Ok(default_prevented) if default_prevented => {} // continue loop Ok(_) => break Ok(()), @@ -154,7 +154,7 @@ impl CliMainWorker { self .inner .worker - .dispatch_unload_event(&located_script_name!())?; + .dispatch_unload_event(located_script_name!())?; Ok(()) } @@ -166,7 +166,7 @@ impl CliMainWorker { let _ = self .inner .worker - .dispatch_unload_event(&located_script_name!()); + .dispatch_unload_event(located_script_name!()); } } } @@ -185,7 +185,7 @@ impl CliMainWorker { // failures. if self.ps.options.trace_ops() { self.worker.js_runtime.execute_script( - &located_script_name!(), + located_script_name!(), "Deno[Deno.internal].core.enableOpCallTracing();", )?; } @@ -200,19 +200,19 @@ impl CliMainWorker { self.execute_side_module_possibly_with_npm().await?; } - self.worker.dispatch_load_event(&located_script_name!())?; + self.worker.dispatch_load_event(located_script_name!())?; self.run_tests(&self.ps.options.shuffle_tests()).await?; loop { if !self .worker - .dispatch_beforeunload_event(&located_script_name!())? + .dispatch_beforeunload_event(located_script_name!())? { break; } self.worker.run_event_loop(false).await?; } - self.worker.dispatch_unload_event(&located_script_name!())?; + self.worker.dispatch_unload_event(located_script_name!())?; if let Some(coverage_collector) = maybe_coverage_collector.as_mut() { self @@ -230,7 +230,7 @@ impl CliMainWorker { self.enable_test(); self.worker.execute_script( - &located_script_name!(), + located_script_name!(), "Deno[Deno.internal].core.enableOpCallTracing();", )?; @@ -239,18 +239,18 @@ impl CliMainWorker { self.execute_side_module_possibly_with_npm().await?; } - self.worker.dispatch_load_event(&located_script_name!())?; + self.worker.dispatch_load_event(located_script_name!())?; self.run_tests(&None).await?; loop { if !self .worker - .dispatch_beforeunload_event(&located_script_name!())? + .dispatch_beforeunload_event(located_script_name!())? { break; } self.worker.run_event_loop(false).await?; } - self.worker.dispatch_unload_event(&located_script_name!())?; + self.worker.dispatch_unload_event(located_script_name!())?; Ok(()) } @@ -260,18 +260,18 @@ impl CliMainWorker { // 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.dispatch_load_event(located_script_name!())?; self.run_benchmarks().await?; loop { if !self .worker - .dispatch_beforeunload_event(&located_script_name!())? + .dispatch_beforeunload_event(located_script_name!())? { break; } self.worker.run_event_loop(false).await?; } - self.worker.dispatch_unload_event(&located_script_name!())?; + self.worker.dispatch_unload_event(located_script_name!())?; Ok(()) } diff --git a/core/examples/eval_js_value.rs b/core/examples/eval_js_value.rs index 6990abb856..e5b823a095 100644 --- a/core/examples/eval_js_value.rs +++ b/core/examples/eval_js_value.rs @@ -26,7 +26,7 @@ fn main() { fn eval( context: &mut JsRuntime, - code: &str, + code: &'static str, ) -> Result { let res = context.execute_script("", code); match res { diff --git a/core/examples/ts_module_loader.rs b/core/examples/ts_module_loader.rs index c7097fc919..4a38073abc 100644 --- a/core/examples/ts_module_loader.rs +++ b/core/examples/ts_module_loader.rs @@ -82,7 +82,7 @@ impl ModuleLoader for TypescriptModuleLoader { code }; let module = ModuleSource { - code: code.into_bytes().into_boxed_slice(), + code: code.into(), module_type, module_url_specified: module_specifier.to_string(), module_url_found: module_specifier.to_string(), diff --git a/core/extensions.rs b/core/extensions.rs index 2a578429b9..94c4a2a794 100644 --- a/core/extensions.rs +++ b/core/extensions.rs @@ -1,4 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use crate::modules::ModuleCode; use crate::OpState; use anyhow::Context as _; use anyhow::Error; @@ -23,13 +24,12 @@ pub enum ExtensionFileSourceCode { } impl ExtensionFileSourceCode { - pub fn load(&self) -> Result { + pub fn load(&self) -> Result { match self { - ExtensionFileSourceCode::IncludedInBinary(code) => Ok(code.to_string()), + ExtensionFileSourceCode::IncludedInBinary(code) => Ok((*code).into()), ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) => { - let msg = format!("Failed to read \"{}\"", path.display()); - let code = std::fs::read_to_string(path).context(msg)?; - Ok(code) + let msg = || format!("Failed to read \"{}\"", path.display()); + Ok(std::fs::read_to_string(path).with_context(msg)?.into()) } } } diff --git a/core/lib.rs b/core/lib.rs index 7751be6e8a..ba0a026bc0 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -77,6 +77,7 @@ pub use crate::module_specifier::ModuleSpecifier; pub use crate::modules::ExtModuleLoader; pub use crate::modules::ExtModuleLoaderCb; pub use crate::modules::FsModuleLoader; +pub use crate::modules::ModuleCode; pub use crate::modules::ModuleId; pub use crate::modules::ModuleLoader; pub use crate::modules::ModuleSource; @@ -144,7 +145,7 @@ pub mod _ops { #[macro_export] macro_rules! located_script_name { () => { - format!("[ext:{}:{}:{}]", std::file!(), std::line!(), std::column!()); + concat!("[ext:{}:{}:{}]", std::file!(), std::line!(), std::column!()); }; } diff --git a/core/modules.rs b/core/modules.rs index 2d80071aa6..78efdedfdf 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -10,6 +10,7 @@ use crate::snapshot_util::SnapshottedData; use crate::JsRuntime; use crate::OpState; use anyhow::Error; +use core::panic; use futures::future::FutureExt; use futures::stream::FuturesUnordered; use futures::stream::Stream; @@ -18,6 +19,7 @@ use futures::stream::TryStreamExt; use log::debug; use serde::Deserialize; use serde::Serialize; +use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; use std::collections::HashSet; @@ -25,6 +27,7 @@ use std::collections::VecDeque; use std::future::Future; use std::pin::Pin; use std::rc::Rc; +use std::sync::Arc; use std::task::Context; use std::task::Poll; @@ -192,14 +195,155 @@ impl std::fmt::Display for ModuleType { // that happened; not only first and final target. It would simplify a lot // of things throughout the codebase otherwise we may end up requesting // intermediate redirects from file loader. -#[derive(Debug, Clone, Eq, PartialEq)] +// NOTE: This should _not_ be made #[derive(Clone)] unless we take some precautions to avoid excessive string copying. +#[derive(Debug)] pub struct ModuleSource { - pub code: Box<[u8]>, + pub code: ModuleCode, pub module_type: ModuleType, pub module_url_specified: String, pub module_url_found: String, } +/// Module code can be sourced from strings or bytes that are either owned or borrowed. This enumeration allows us +/// to perform a minimal amount of cloning and format-shifting of the underlying data. +/// +/// Note that any [`ModuleCode`] created from a `'static` byte array or string must contain ASCII characters. +/// +/// Examples of ways to construct a [`ModuleCode`] object: +/// +/// ```rust +/// # use deno_core::ModuleCode; +/// +/// let code: ModuleCode = "a string".into(); +/// let code: ModuleCode = b"a string".into(); +/// ``` +#[derive(Debug)] +pub enum ModuleCode { + /// Created from static data -- must be 100% 7-bit ASCII! + Static(&'static [u8]), + + /// An owned chunk of data. + Owned(Vec), + + /// Scripts loaded from the `deno_graph` infrastructure. + Arc(Arc), +} + +impl ModuleCode { + #[inline(always)] + pub fn as_bytes(&self) -> &[u8] { + match self { + Self::Static(b) => b, + Self::Owned(b) => b, + Self::Arc(s) => s.as_bytes(), + } + } + + pub fn try_static_ascii(&self) -> Option<&'static [u8]> { + match self { + Self::Static(b) => Some(b), + _ => None, + } + } + + /// Takes a [`ModuleCode`] value as an owned [`String`]. May be slow. + pub fn take_as_string(self) -> String { + match self { + Self::Static(b) => String::from_utf8(b.to_vec()).unwrap(), + Self::Owned(b) => String::from_utf8(b).unwrap(), + Self::Arc(s) => (*s).to_owned(), + } + } + + /// Truncates a `ModuleCode`] value, possibly re-allocating or memcpy'ing. May be slow. + pub fn truncate(&mut self, index: usize) { + match self { + Self::Static(b) => *self = Self::Static(&b[..index]), + Self::Owned(b) => b.truncate(index), + // We can't do much if we have an Arc, so we'll just take ownership of the truncated version + Self::Arc(s) => *self = s[..index].to_owned().into(), + } + } +} + +impl Default for ModuleCode { + fn default() -> Self { + ModuleCode::Static(&[]) + } +} + +impl From> for ModuleCode { + #[inline(always)] + fn from(value: Arc) -> Self { + Self::Arc(value) + } +} + +impl From<&Arc> for ModuleCode { + #[inline(always)] + fn from(value: &Arc) -> Self { + Self::Arc(value.clone()) + } +} + +impl From> for ModuleCode { + #[inline(always)] + fn from(value: Cow<'static, str>) -> Self { + match value { + Cow::Borrowed(b) => b.into(), + Cow::Owned(b) => b.into(), + } + } +} + +impl From> for ModuleCode { + #[inline(always)] + fn from(value: Cow<'static, [u8]>) -> Self { + match value { + Cow::Borrowed(b) => b.into(), + Cow::Owned(b) => b.into(), + } + } +} + +impl From<&'static str> for ModuleCode { + #[inline(always)] + fn from(value: &'static str) -> Self { + assert!(value.is_ascii()); + ModuleCode::Static(value.as_bytes()) + } +} + +impl From for ModuleCode { + #[inline(always)] + fn from(value: String) -> Self { + value.into_bytes().into() + } +} + +impl From> for ModuleCode { + #[inline(always)] + fn from(value: Vec) -> Self { + ModuleCode::Owned(value) + } +} + +impl From<&'static [u8]> for ModuleCode { + #[inline(always)] + fn from(value: &'static [u8]) -> Self { + assert!(value.is_ascii()); + ModuleCode::Static(value) + } +} + +impl From<&'static [u8; N]> for ModuleCode { + #[inline(always)] + fn from(value: &'static [u8; N]) -> Self { + assert!(value.is_ascii()); + ModuleCode::Static(value) + } +} + pub(crate) type PrepareLoadFuture = dyn Future)>; pub type ModuleSourceFuture = dyn Future>; @@ -323,7 +467,7 @@ pub(crate) fn resolve_helper( /// Function that can be passed to the `ExtModuleLoader` that allows to /// transpile sources before passing to V8. pub type ExtModuleLoaderCb = - Box Result>; + Box Result>; pub struct ExtModuleLoader { module_loader: Rc, @@ -448,7 +592,7 @@ impl ModuleLoader for ExtModuleLoader { return async move { let code = result?; let source = ModuleSource { - code: code.into_bytes().into_boxed_slice(), + code, module_type: ModuleType::JavaScript, module_url_specified: specifier.clone(), module_url_found: specifier.clone(), @@ -529,7 +673,7 @@ impl ModuleLoader for FsModuleLoader { let code = std::fs::read(path)?; let module = ModuleSource { - code: code.into_boxed_slice(), + code: code.into(), module_type, module_url_specified: module_specifier.to_string(), module_url_found: module_specifier.to_string(), @@ -1002,6 +1146,32 @@ pub(crate) enum ModuleError { Other(Error), } +pub enum ModuleName<'a> { + Static(&'static str), + NotStatic(&'a str), +} + +impl<'a> ModuleName<'a> { + pub fn as_ref(&self) -> &'a str { + match self { + ModuleName::Static(s) => s, + ModuleName::NotStatic(s) => s, + } + } +} + +impl<'a, S: AsRef> From<&'a S> for ModuleName<'a> { + fn from(s: &'a S) -> Self { + Self::NotStatic(s.as_ref()) + } +} + +impl From<&'static str> for ModuleName<'static> { + fn from(value: &'static str) -> Self { + Self::Static(value) + } +} + /// A collection of JS modules. pub(crate) struct ModuleMap { // Handling of specifiers and v8 objects @@ -1326,16 +1496,54 @@ impl ModuleMap { } } - fn new_json_module( + fn string_from_code<'a>( + scope: &mut v8::HandleScope<'a>, + code: &ModuleCode, + ) -> Option> { + if let Some(code) = code.try_static_ascii() { + v8::String::new_external_onebyte_static(scope, code) + } else { + v8::String::new_from_utf8( + scope, + code.as_bytes(), + v8::NewStringType::Normal, + ) + } + } + + fn string_from_module_name<'a>( + scope: &mut v8::HandleScope<'a>, + name: &ModuleName, + ) -> Option> { + match name { + ModuleName::Static(s) => { + assert!(s.is_ascii()); + v8::String::new_external_onebyte_static(scope, s.as_bytes()) + } + ModuleName::NotStatic(s) => v8::String::new(scope, s), + } + } + + fn new_json_module<'a, N: Into>>( &mut self, scope: &mut v8::HandleScope, - name: &str, - source: &[u8], + name: N, + source: &ModuleCode, ) -> Result { - let name_str = v8::String::new(scope, name).unwrap(); + // Manual monomorphization (TODO: replace w/momo) + self.new_json_module_inner(scope, name.into(), source) + } + + fn new_json_module_inner( + &mut self, + scope: &mut v8::HandleScope, + name: ModuleName, + source: &ModuleCode, + ) -> Result { + let name_str = Self::string_from_module_name(scope, &name).unwrap(); let source_str = v8::String::new_from_utf8( scope, - strip_bom(source), + strip_bom(source.as_bytes()), v8::NewStringType::Normal, ) .unwrap(); @@ -1364,25 +1572,47 @@ impl ModuleMap { let value_handle = v8::Global::::new(tc_scope, parsed_json); self.json_value_store.insert(handle.clone(), value_handle); - let id = - self.create_module_info(name, ModuleType::Json, handle, false, vec![]); + let id = self.create_module_info( + name.as_ref(), + ModuleType::Json, + handle, + false, + vec![], + ); Ok(id) } - // Create and compile an ES module. - pub(crate) fn new_es_module( + /// Create and compile an ES module. Generic interface that can receive either a `&'static str` or a string with a lifetime. Prefer + /// to pass `&'static str` as this allows us to use v8 external strings. + pub(crate) fn new_es_module<'a, N: Into>>( &mut self, scope: &mut v8::HandleScope, main: bool, - name: &str, - source: &[u8], + name: N, + source: &ModuleCode, is_dynamic_import: bool, ) -> Result { - let name_str = v8::String::new(scope, name).unwrap(); - let source_str = - v8::String::new_from_utf8(scope, source, v8::NewStringType::Normal) - .unwrap(); + // Manual monomorphization (TODO: replace w/momo) + self.new_es_module_inner( + scope, + main, + name.into(), + source, + is_dynamic_import, + ) + } + + fn new_es_module_inner( + &mut self, + scope: &mut v8::HandleScope, + main: bool, + name: ModuleName, + source: &ModuleCode, + is_dynamic_import: bool, + ) -> Result { + let name_str = Self::string_from_module_name(scope, &name).unwrap(); + let source_str = Self::string_from_code(scope, source).unwrap(); let origin = bindings::module_origin(scope, name_str); let source = v8::script_compiler::Source::new(source_str, Some(&origin)); @@ -1432,7 +1662,7 @@ impl ModuleMap { self.snapshot_loaded_and_not_snapshotting, self.loader.clone(), &import_specifier, - name, + name.as_ref(), if is_dynamic_import { ResolutionKind::DynamicImport } else { @@ -1456,7 +1686,7 @@ impl ModuleMap { if let Some(main_module) = maybe_main_module { return Err(ModuleError::Other(generic_error( format!("Trying to create \"main\" module ({:?}), when one already exists ({:?})", - name, + name.as_ref(), main_module.name, )))); } @@ -1464,7 +1694,7 @@ impl ModuleMap { let handle = v8::Global::::new(tc_scope, module); let id = self.create_module_info( - name, + name.as_ref(), ModuleType::JavaScript, handle, main, @@ -1846,7 +2076,7 @@ import "/a.js"; } match mock_source_code(&inner.url) { Some(src) => Poll::Ready(Ok(ModuleSource { - code: src.0.as_bytes().to_vec().into_boxed_slice(), + code: src.0.into(), module_type: ModuleType::JavaScript, module_url_specified: inner.url.clone(), module_url_found: src.1.to_owned(), @@ -2043,12 +2273,13 @@ import "/a.js"; scope, true, &specifier_a, - br#" + &br#" import { b } from './b.js' if (b() != 'b') throw Error(); let control = 42; Deno.core.ops.op_test(control); - "#, + "# + .into(), false, ) .unwrap(); @@ -2068,7 +2299,7 @@ import "/a.js"; scope, false, "file:///b.js", - b"export function b() { return 'b' }", + &b"export function b() { return 'b' }".into(), false, ) .unwrap(); @@ -2153,11 +2384,12 @@ import "/a.js"; scope, true, &specifier_a, - br#" + &br#" import jsonData from './b.json' assert {type: "json"}; assert(jsonData.a == "b"); assert(jsonData.c.d == 10); - "#, + "# + .into(), false, ) .unwrap(); @@ -2175,7 +2407,7 @@ import "/a.js"; .new_json_module( scope, "file:///b.json", - b"{\"a\": \"b\", \"c\": {\"d\": 10}}", + &b"{\"a\": \"b\", \"c\": {\"d\": 10}}".into(), ) .unwrap(); let imports = module_map.get_requested_modules(mod_b).unwrap(); @@ -2285,9 +2517,7 @@ import "/a.js"; let info = ModuleSource { module_url_specified: specifier.to_string(), module_url_found: specifier.to_string(), - code: b"export function b() { return 'b' }" - .to_vec() - .into_boxed_slice(), + code: b"export function b() { return 'b' }".into(), module_type: ModuleType::JavaScript, }; async move { Ok(info) }.boxed() @@ -2427,7 +2657,7 @@ import "/a.js"; let info = ModuleSource { module_url_specified: specifier.to_string(), module_url_found: specifier.to_string(), - code: code.as_bytes().to_vec().into_boxed_slice(), + code: code.into(), module_type: ModuleType::JavaScript, }; async move { Ok(info) }.boxed() @@ -2703,7 +2933,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); // The behavior should be very similar to /a.js. let spec = resolve_url("file:///main_with_code.js").unwrap(); let main_id_fut = runtime - .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned())) + .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.into())) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).unwrap(); @@ -2795,17 +3025,13 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); "file:///main_module.js" => Ok(ModuleSource { module_url_specified: "file:///main_module.js".to_string(), module_url_found: "file:///main_module.js".to_string(), - code: b"if (!import.meta.main) throw Error();" - .to_vec() - .into_boxed_slice(), + code: b"if (!import.meta.main) throw Error();".into(), module_type: ModuleType::JavaScript, }), "file:///side_module.js" => Ok(ModuleSource { module_url_specified: "file:///side_module.js".to_string(), module_url_found: "file:///side_module.js".to_string(), - code: b"if (import.meta.main) throw Error();" - .to_vec() - .into_boxed_slice(), + code: b"if (import.meta.main) throw Error();".into(), module_type: ModuleType::JavaScript, }), _ => unreachable!(), @@ -2866,7 +3092,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); // The behavior should be very similar to /a.js. let spec = resolve_url("file:///main_with_code.js").unwrap(); let main_id_fut = runtime - .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned())) + .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.into())) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).unwrap(); @@ -2906,7 +3132,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); // The behavior should be very similar to /a.js. let spec = resolve_url("file:///main_with_code.js").unwrap(); let main_id_fut = runtime - .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned())) + .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.into())) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).unwrap(); @@ -2976,4 +3202,23 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); Some("Cannot load extension module from external code".to_string()) ); } + + #[test] + fn code_truncate() { + let mut s = "123456".to_owned(); + s.truncate(3); + + let mut code: ModuleCode = "123456".into(); + code.truncate(3); + assert_eq!(s, code.take_as_string()); + + let mut code: ModuleCode = "123456".to_owned().into(); + code.truncate(3); + assert_eq!(s, code.take_as_string()); + + let arc_str: Arc = "123456".into(); + let mut code: ModuleCode = arc_str.into(); + code.truncate(3); + assert_eq!(s, code.take_as_string()); + } } diff --git a/core/runtime.rs b/core/runtime.rs index 7b6dfba923..f9f2e55230 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -9,6 +9,7 @@ use crate::extensions::OpEventLoopFn; use crate::inspector::JsRuntimeInspector; use crate::module_specifier::ModuleSpecifier; use crate::modules::ExtModuleLoaderCb; +use crate::modules::ModuleCode; use crate::modules::ModuleError; use crate::modules::ModuleId; use crate::modules::ModuleLoadId; @@ -50,6 +51,8 @@ use std::sync::Mutex; use std::sync::Once; use std::task::Context; use std::task::Poll; +use v8::HandleScope; +use v8::Local; use v8::OwnedIsolate; type PendingOpFuture = OpCall<(RealmIdx, PromiseId, OpId, OpResult)>; @@ -748,7 +751,7 @@ impl JsRuntime { realm.execute_script( self.v8_isolate(), file_source.specifier, - &file_source.code.load()?, + file_source.code.load()?, )?; } } @@ -902,7 +905,7 @@ impl JsRuntime { /// The execution takes place on the current global context, so it is possible /// to maintain local JS state and invoke this method multiple times. /// - /// `name` can be a filepath or any other string, eg. + /// `name` can be a filepath or any other string, but it is required to be 7-bit ASCII, eg. /// /// - "/some/file/path.js" /// - "" @@ -911,10 +914,10 @@ impl JsRuntime { /// The same `name` value can be used for multiple executions. /// /// `Error` can usually be downcast to `JsError`. - pub fn execute_script( + pub fn execute_script>( &mut self, - name: &str, - source_code: &str, + name: &'static str, + source_code: S, ) -> Result, Error> { self .global_realm() @@ -2056,21 +2059,15 @@ impl JsRuntime { pub async fn load_main_module( &mut self, specifier: &ModuleSpecifier, - code: Option, + code: Option, ) -> Result { let module_map_rc = Self::module_map(self.v8_isolate()); if let Some(code) = code { let scope = &mut self.handle_scope(); + // true for main module module_map_rc .borrow_mut() - .new_es_module( - scope, - // main module - true, - specifier.as_str(), - code.as_bytes(), - false, - ) + .new_es_module(scope, true, specifier, &code, false) .map_err(|e| match e { ModuleError::Exception(exception) => { let exception = v8::Local::new(scope, exception); @@ -2116,21 +2113,15 @@ impl JsRuntime { pub async fn load_side_module( &mut self, specifier: &ModuleSpecifier, - code: Option, + code: Option, ) -> Result { let module_map_rc = Self::module_map(self.v8_isolate()); if let Some(code) = code { let scope = &mut self.handle_scope(); + // false for side module (not main module) module_map_rc .borrow_mut() - .new_es_module( - scope, - // not main module - false, - specifier.as_str(), - code.as_bytes(), - false, - ) + .new_es_module(scope, false, specifier, &code, false) .map_err(|e| match e { ModuleError::Exception(exception) => { let exception = v8::Local::new(scope, exception); @@ -2476,6 +2467,21 @@ impl JsRealm { self.0.open(scope).global(scope) } + fn string_from_code<'a>( + scope: &mut HandleScope<'a>, + code: &ModuleCode, + ) -> Option> { + if let Some(code) = code.try_static_ascii() { + v8::String::new_external_onebyte_static(scope, code) + } else { + v8::String::new_from_utf8( + scope, + code.as_bytes(), + v8::NewStringType::Normal, + ) + } + } + /// Executes traditional JavaScript code (traditional = not ES modules) in the /// realm's context. /// @@ -2488,16 +2494,28 @@ impl JsRealm { /// The same `name` value can be used for multiple executions. /// /// `Error` can usually be downcast to `JsError`. - pub fn execute_script( + pub fn execute_script>( &self, isolate: &mut v8::Isolate, - name: &str, - source_code: &str, + name: &'static str, + source_code: S, + ) -> Result, Error> { + // Manual monomorphization (TODO: replace w/momo) + self.execute_script_inner(isolate, name, source_code.into()) + } + + fn execute_script_inner( + &self, + isolate: &mut v8::Isolate, + name: &'static str, + source_code: ModuleCode, ) -> Result, Error> { let scope = &mut self.handle_scope(isolate); - let source = v8::String::new(scope, source_code).unwrap(); - let name = v8::String::new(scope, name).unwrap(); + let source = Self::string_from_code(scope, &source_code).unwrap(); + assert!(name.is_ascii()); + let name = + v8::String::new_external_onebyte_static(scope, name.as_bytes()).unwrap(); let origin = bindings::script_origin(scope, name); let tc_scope = &mut v8::TryCatch::new(scope); @@ -3104,7 +3122,8 @@ pub mod tests { runtime .execute_script( "encode_decode_test.js", - include_str!("encode_decode_test.js"), + // Note: We make this to_owned because it contains non-ASCII chars + include_str!("encode_decode_test.js").to_owned(), ) .unwrap(); if let Poll::Ready(Err(_)) = runtime.poll_event_loop(cx, false) { @@ -3320,7 +3339,7 @@ pub mod tests { export const a = "b"; export default 1 + 2; "# - .to_string(); + .into(); let module_id = futures::executor::block_on( runtime.load_main_module(&specifier, Some(source_code)), @@ -3490,7 +3509,8 @@ pub mod tests { import {{ f{prev} }} from "file:///{prev}.js"; export function f{i}() {{ return f{prev}() }} "# - ); + ) + .into(); let id = if main { futures::executor::block_on( @@ -3559,8 +3579,7 @@ pub mod tests { }); let specifier = crate::resolve_url("file:///0.js").unwrap(); - let source_code = - r#"export function f0() { return "hello world" }"#.to_string(); + let source_code = r#"export function f0() { return "hello world" }"#.into(); let id = futures::executor::block_on( runtime.load_side_module(&specifier, Some(source_code)), ) @@ -3620,7 +3639,7 @@ pub mod tests { return mod.f400() + " " + Deno.core.ops.op_test(); })();"# .to_string(); - let val = runtime3.execute_script(".", &source_code).unwrap(); + let val = runtime3.execute_script(".", source_code).unwrap(); let val = futures::executor::block_on(runtime3.resolve_value(val)).unwrap(); { let scope = &mut runtime3.handle_scope(); @@ -4163,7 +4182,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { ) -> Pin> { async move { Ok(ModuleSource { - code: b"console.log('hello world');".to_vec().into_boxed_slice(), + code: b"console.log('hello world');".into(), module_url_specified: "file:///main.js".to_string(), module_url_found: "file:///main.js".to_string(), module_type: ModuleType::JavaScript, @@ -4180,7 +4199,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }); let specifier = crate::resolve_url("file:///main.js").unwrap(); - let source_code = "Deno.core.print('hello\\n')".to_string(); + let source_code = "Deno.core.print('hello\\n')".into(); let module_id = futures::executor::block_on( runtime.load_main_module(&specifier, Some(source_code)), @@ -4273,7 +4292,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { .execute_script( runtime.v8_isolate(), "", - &format!( + format!( r#" globalThis.rejectValue = undefined; @@ -4282,7 +4301,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }}); Deno.core.opAsync("op_void_async").then(() => Promise.reject({number})); "# - ), + ) ) .unwrap(); } @@ -4344,7 +4363,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { async move { Ok(ModuleSource { - code: source.as_bytes().to_vec().into_boxed_slice(), + code: source.into(), module_url_specified: "file:///main.js".to_string(), module_url_found: "file:///main.js".to_string(), module_type: ModuleType::JavaScript, @@ -4858,7 +4877,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { async move { Ok(ModuleSource { - code: source.as_bytes().to_vec().into_boxed_slice(), + code: source.into(), module_url_specified: "file:///main.js".to_string(), module_url_found: "file:///main.js".to_string(), module_type: ModuleType::JavaScript, diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 5684bf1727..bde3cfd863 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -390,7 +390,7 @@ pub fn initialize_runtime( } else { "undefined".to_string() }; - let source_code = &format!( + let source_code = format!( r#"(function loadBuiltinNodeModules(nodeGlobalThisName, usesLocalNodeModulesDir, argv0) {{ Deno[Deno.internal].node.initialize( nodeGlobalThisName, @@ -403,7 +403,7 @@ pub fn initialize_runtime( argv0 ); - js_runtime.execute_script(&located_script_name!(), source_code)?; + js_runtime.execute_script(located_script_name!(), source_code)?; Ok(()) } @@ -417,7 +417,7 @@ pub fn load_cjs_module( text.replace('\\', r"\\").replace('\'', r"\'") } - let source_code = &format!( + let source_code = format!( r#"(function loadCjsModule(moduleName, isMain, inspectBrk) {{ Deno[Deno.internal].node.loadCjsModule(moduleName, isMain, inspectBrk); }})('{module}', {main}, {inspect_brk});"#, @@ -426,6 +426,6 @@ pub fn load_cjs_module( inspect_brk = inspect_brk, ); - js_runtime.execute_script(&located_script_name!(), source_code)?; + js_runtime.execute_script(located_script_name!(), source_code)?; Ok(()) } diff --git a/runtime/build.rs b/runtime/build.rs index 016ece810c..ec7c9642c5 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -17,11 +17,12 @@ mod startup_snapshot { use deno_core::snapshot_util::*; use deno_core::Extension; use deno_core::ExtensionFileSource; + use deno_core::ModuleCode; use std::path::Path; fn transpile_ts_for_snapshotting( file_source: &ExtensionFileSource, - ) -> Result { + ) -> Result { let media_type = MediaType::from_path(Path::new(&file_source.specifier)); let should_transpile = match media_type { @@ -41,7 +42,7 @@ mod startup_snapshot { let parsed = deno_ast::parse_module(ParseParams { specifier: file_source.specifier.to_string(), - text_info: SourceTextInfo::from_string(code), + text_info: SourceTextInfo::from_string(code.take_as_string()), media_type, capture_tokens: false, scope_analysis: false, @@ -53,7 +54,7 @@ mod startup_snapshot { ..Default::default() })?; - Ok(transpiled_source.text) + Ok(transpiled_source.text.into()) } #[derive(Clone)] diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index c6fbb83707..0aa142da8e 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -25,6 +25,7 @@ use deno_core::CompiledWasmModuleStore; use deno_core::Extension; use deno_core::GetErrorClassFn; use deno_core::JsRuntime; +use deno_core::ModuleCode; use deno_core::ModuleId; use deno_core::ModuleLoader; use deno_core::ModuleSpecifier; @@ -575,16 +576,16 @@ impl WebWorker { "#; let poll_for_messages_fn = self .js_runtime - .execute_script(&located_script_name!(), script) + .execute_script(located_script_name!(), script) .expect("Failed to execute worker bootstrap script"); self.poll_for_messages_fn = Some(poll_for_messages_fn); } /// See [JsRuntime::execute_script](deno_core::JsRuntime::execute_script) - pub fn execute_script( + pub fn execute_script>( &mut self, - name: &str, - source_code: &str, + name: &'static str, + source_code: S, ) -> Result<(), AnyError> { self.js_runtime.execute_script(name, source_code)?; Ok(()) @@ -744,7 +745,7 @@ fn print_worker_error( pub fn run_web_worker( worker: WebWorker, specifier: ModuleSpecifier, - maybe_source_code: Option, + mut maybe_source_code: Option, preload_module_cb: Arc, pre_execute_module_cb: Arc, format_js_error_fn: Option>, @@ -772,8 +773,8 @@ pub fn run_web_worker( }; // Execute provided source code immediately - let result = if let Some(source_code) = maybe_source_code { - let r = worker.execute_script(&located_script_name!(), &source_code); + let result = if let Some(source_code) = maybe_source_code.take() { + let r = worker.execute_script(located_script_name!(), source_code); worker.start_polling_for_messages(); r } else { diff --git a/runtime/worker.rs b/runtime/worker.rs index a995861c5f..a24a22c965 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -21,6 +21,7 @@ use deno_core::FsModuleLoader; use deno_core::GetErrorClassFn; use deno_core::JsRuntime; use deno_core::LocalInspectorSession; +use deno_core::ModuleCode; use deno_core::ModuleId; use deno_core::ModuleLoader; use deno_core::ModuleSpecifier; @@ -364,10 +365,10 @@ impl MainWorker { } /// See [JsRuntime::execute_script](deno_core::JsRuntime::execute_script) - pub fn execute_script( + pub fn execute_script>( &mut self, - script_name: &str, - source_code: &str, + script_name: &'static str, + source_code: S, ) -> Result, AnyError> { self.js_runtime.execute_script(script_name, source_code) } @@ -502,7 +503,7 @@ impl MainWorker { /// Does not poll event loop, and thus not await any of the "load" event handlers. pub fn dispatch_load_event( &mut self, - script_name: &str, + script_name: &'static str, ) -> Result<(), AnyError> { self.execute_script( script_name, @@ -519,7 +520,7 @@ impl MainWorker { /// Does not poll event loop, and thus not await any of the "unload" event handlers. pub fn dispatch_unload_event( &mut self, - script_name: &str, + script_name: &'static str, ) -> Result<(), AnyError> { self.execute_script( script_name, @@ -536,7 +537,7 @@ impl MainWorker { /// running. pub fn dispatch_beforeunload_event( &mut self, - script_name: &str, + script_name: &'static str, ) -> Result { let value = self.js_runtime.execute_script( script_name,