From 0b4770fa7daf274ab01923fb09fd604aeb27e417 Mon Sep 17 00:00:00 2001 From: Matt Mastracci Date: Tue, 21 Mar 2023 16:33:12 -0600 Subject: [PATCH] perf(core) Reduce script name and script code copies (#18298) Reduce the number of copies and allocations of script code by carrying around ownership/reference information from creation time. As an advantage, this allows us to maintain the identity of `&'static str`-based scripts and use v8's external 1-byte strings (to avoid incorrectly passing non-ASCII strings, debug `assert!`s gate all string reference paths). Benchmark results: Perf improvements -- ~0.1 - 0.2ms faster, but should reduce garbage w/external strings and reduces data copies overall. May also unlock some more interesting optimizations in the future. This requires adding some generics to functions, but manual monomorphization has been applied (outer/inner function) to avoid code bloat. --- bench_util/js_runtime.rs | 5 +- cli/emit.rs | 7 +- cli/lsp/tsc.rs | 4 +- cli/module_loader.rs | 17 +- cli/standalone.rs | 10 +- cli/tools/coverage/mod.rs | 24 ++- cli/tsc/mod.rs | 4 +- cli/util/text_encoding.rs | 36 ++-- cli/worker.rs | 36 ++-- core/examples/eval_js_value.rs | 2 +- core/examples/ts_module_loader.rs | 2 +- core/extensions.rs | 10 +- core/lib.rs | 3 +- core/modules.rs | 331 ++++++++++++++++++++++++++---- core/runtime.rs | 99 +++++---- ext/node/lib.rs | 8 +- runtime/build.rs | 7 +- runtime/web_worker.rs | 15 +- runtime/worker.rs | 13 +- 19 files changed, 457 insertions(+), 176 deletions(-) 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,