From a1764f7690cfdc3e42724fcad29ef954b7e576a4 Mon Sep 17 00:00:00 2001 From: Matt Mastracci Date: Tue, 4 Apr 2023 06:46:31 -0600 Subject: [PATCH] refactor(core): Improve ergonomics of managing ASCII strings (#18498) This is a follow-on to the earlier work in reducing string copies, mainly focused on ensuring that ASCII strings are easy to provide to the JS runtime. While we are replacing a 16-byte reference in a number of places with a 24-byte structure (measured via `std::mem::size_of`), the reduction in copies wins out over the additional size of the arguments passed into functions. Benchmarking shows approximately the same if not slightly less wallclock time/instructions retired, but I believe this continues to open up further refactoring opportunities. --- bench_util/js_runtime.rs | 2 +- cli/js.rs | 2 +- cli/lsp/tsc.rs | 4 +- cli/module_loader.rs | 22 +- cli/standalone.rs | 35 +- cli/tools/coverage/mod.rs | 4 +- cli/tsc/mod.rs | 12 +- cli/util/text_encoding.rs | 4 +- cli/worker.rs | 5 +- core/bindings.rs | 6 +- core/examples/disable_ops.rs | 2 +- core/examples/eval_js_value.rs | 2 +- core/examples/hello_world.rs | 2 +- core/examples/http_bench_json_ops/main.rs | 2 +- core/examples/panik.rs | 2 +- core/examples/schedule_task.rs | 2 +- core/examples/ts_module_loader.rs | 21 +- core/examples/wasm.rs | 2 +- core/extensions.rs | 2 +- core/fast_string.rs | 243 ++++++++ core/lib.rs | 2 + core/modules.rs | 716 +++++++++------------- core/runtime.rs | 327 +++++----- ext/node/lib.rs | 5 +- runtime/build.rs | 2 +- runtime/web_worker.rs | 13 +- runtime/worker.rs | 17 +- 27 files changed, 817 insertions(+), 641 deletions(-) create mode 100644 core/fast_string.rs diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs index 4a5123a731..57085ef964 100644 --- a/bench_util/js_runtime.rs +++ b/bench_util/js_runtime.rs @@ -117,6 +117,6 @@ pub fn bench_js_async_with( } async fn inner_async(src: &'static str, runtime: &mut JsRuntime) { - runtime.execute_script("inner_loop", src).unwrap(); + runtime.execute_script_static("inner_loop", src).unwrap(); runtime.run_event_loop(false).await.unwrap(); } diff --git a/cli/js.rs b/cli/js.rs index fac771fd52..e3a5b94be7 100644 --- a/cli/js.rs +++ b/cli/js.rs @@ -22,7 +22,7 @@ mod tests { ..Default::default() }); js_runtime - .execute_script( + .execute_script_static( "", r#" if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) { diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index ef5d0e645c..e236eee0a7 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -2899,7 +2899,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.into())?; Ok(()) } @@ -3493,7 +3493,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.into())?; 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 7f6101d809..b7df15e31c 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -78,7 +78,7 @@ impl CliModuleLoader { fn load_prepared_module( &self, specifier: &ModuleSpecifier, - maybe_referrer: Option, + maybe_referrer: Option<&ModuleSpecifier>, ) -> Result { if specifier.scheme() == "node" { unreachable!(); // Node built-in modules should be handled internally. @@ -92,7 +92,7 @@ impl CliModuleLoader { specifier, .. })) => Ok(ModuleCodeSource { - code: source.into(), + code: source.clone().into(), found_url: specifier.clone(), media_type: *media_type, }), @@ -107,7 +107,7 @@ impl CliModuleLoader { | MediaType::Unknown | MediaType::Cjs | MediaType::Mjs - | MediaType::Json => source.into(), + | MediaType::Json => source.clone().into(), MediaType::Dts | MediaType::Dcts | MediaType::Dmts => { Default::default() } @@ -154,7 +154,7 @@ impl CliModuleLoader { fn load_sync( &self, specifier: &ModuleSpecifier, - maybe_referrer: Option, + maybe_referrer: Option<&ModuleSpecifier>, is_dynamic: bool, ) -> Result { let code_source = if self.ps.npm_resolver.in_npm_package(specifier) { @@ -210,15 +210,15 @@ impl CliModuleLoader { // because we don't need it code_without_source_map(code_source.code) }; - Ok(ModuleSource { - code, - module_url_specified: specifier.to_string(), - module_url_found: code_source.found_url.to_string(), - module_type: match code_source.media_type { + Ok(ModuleSource::new_with_redirect( + match code_source.media_type { MediaType::Json => ModuleType::Json, _ => ModuleType::JavaScript, }, - }) + code, + specifier, + &code_source.found_url, + )) } } @@ -240,7 +240,7 @@ impl ModuleLoader for CliModuleLoader { fn load( &self, specifier: &ModuleSpecifier, - maybe_referrer: Option, + maybe_referrer: Option<&ModuleSpecifier>, is_dynamic: bool, ) -> Pin> { // NOTE: this block is async only because of `deno_core` interface diff --git a/cli/standalone.rs b/cli/standalone.rs index 527e8d9757..08caacda6f 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -25,6 +25,7 @@ use deno_core::url::Url; use deno_core::v8_set_flags; use deno_core::ModuleLoader; use deno_core::ModuleSpecifier; +use deno_core::ModuleType; use deno_core::ResolutionKind; use deno_graph::source::Resolver; use deno_runtime::fmt_errors::format_js_error; @@ -165,7 +166,7 @@ impl ModuleLoader for EmbeddedModuleLoader { fn load( &self, module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dynamic: bool, ) -> Pin> { let is_data_uri = get_source_from_data_url(module_specifier).ok(); @@ -173,33 +174,33 @@ impl ModuleLoader for EmbeddedModuleLoader { .eszip .get_module(module_specifier.as_str()) .ok_or_else(|| type_error("Module not found")); - + // TODO(mmastrac): This clone can probably be removed in the future if ModuleSpecifier is no longer a full-fledged URL let module_specifier = module_specifier.clone(); + async move { if let Some((source, _)) = is_data_uri { - return Ok(deno_core::ModuleSource { - code: source.into(), - module_type: deno_core::ModuleType::JavaScript, - module_url_specified: module_specifier.to_string(), - module_url_found: module_specifier.to_string(), - }); + return Ok(deno_core::ModuleSource::new( + deno_core::ModuleType::JavaScript, + source.into(), + &module_specifier, + )); } let module = module?; let code = module.source().await; let code = std::str::from_utf8(&code) .map_err(|_| type_error("Module source is not utf-8"))? - .to_owned(); + .to_owned() + .into(); - Ok(deno_core::ModuleSource { - code: code.into(), - module_type: match module.kind { - eszip::ModuleKind::JavaScript => deno_core::ModuleType::JavaScript, - eszip::ModuleKind::Json => deno_core::ModuleType::Json, + Ok(deno_core::ModuleSource::new( + match module.kind { + eszip::ModuleKind::JavaScript => ModuleType::JavaScript, + eszip::ModuleKind::Json => ModuleType::Json, }, - module_url_specified: module_specifier.to_string(), - module_url_found: module_specifier.to_string(), - }) + code, + &module_specifier, + )) } .boxed_local() } diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 2346b3614b..7f25988114 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -691,7 +691,7 @@ pub async fn cover_files( | MediaType::Unknown | MediaType::Cjs | MediaType::Mjs - | MediaType::Json => file.source.into(), + | MediaType::Json => file.source.clone().into(), MediaType::Dts | MediaType::Dmts | MediaType::Dcts => Default::default(), MediaType::TypeScript | MediaType::Jsx @@ -718,7 +718,7 @@ pub async fn cover_files( let source_map = source_map_from_code(&transpiled_code); let coverage_report = generate_coverage_report( &script_coverage, - transpiled_code.take_as_string(), + transpiled_code.as_str().to_owned(), &source_map, &out_mode, ); diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index a9dc5b7f32..3bd8efefa8 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -13,6 +13,7 @@ use crate::util::path::mapped_specifier_for_tsc; use deno_ast::MediaType; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; +use deno_core::ascii_str; use deno_core::error::AnyError; use deno_core::located_script_name; use deno_core::op; @@ -131,8 +132,8 @@ fn get_asset_texts_from_new_runtime() -> Result, AnyError> { extensions: vec![deno_cli_tsc::init_ops()], ..Default::default() }); - let global = - runtime.execute_script("get_assets.js", "globalThis.getAssets()")?; + let global = runtime + .execute_script("get_assets.js", ascii_str!("globalThis.getAssets()"))?; let scope = &mut runtime.handle_scope(); let local = deno_core::v8::Local::new(scope, global); Ok(serde_v8::from_v8::>(scope, local)?) @@ -792,15 +793,14 @@ pub fn exec(request: Request) -> Result { }, ); - let startup_source = "globalThis.startup({ legacyFlag: false })"; + let startup_source = ascii_str!("globalThis.startup({ legacyFlag: false })"); let request_value = json!({ "config": request.config, "debug": request.debug, "rootNames": root_names, "localOnly": request.check_mode == TypeCheckMode::Local, }); - let request_str = request_value.to_string(); - let exec_source = format!("globalThis.exec({request_str})"); + let exec_source = format!("globalThis.exec({request_value})").into(); let mut runtime = JsRuntime::new(RuntimeOptions { startup_snapshot: Some(compiler_snapshot()), @@ -974,7 +974,7 @@ mod tests { ..Default::default() }); js_runtime - .execute_script( + .execute_script_static( "", r#" if (!(startup)) { diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs index 0111ec82f6..29a8d4069c 100644 --- a/cli/util/text_encoding.rs +++ b/cli/util/text_encoding.rs @@ -160,7 +160,9 @@ mod tests { fn run_test(input: &'static str, output: &'static str) { assert_eq!( - code_without_source_map(input.into()).take_as_string(), + code_without_source_map(ModuleCode::from_static(input)) + .as_str() + .to_owned(), output ); } diff --git a/cli/worker.rs b/cli/worker.rs index 5beef84ff1..edd604519a 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use std::sync::Arc; use deno_ast::ModuleSpecifier; +use deno_core::ascii_str; use deno_core::error::AnyError; use deno_core::futures::task::LocalFutureObj; use deno_core::futures::FutureExt; @@ -184,7 +185,7 @@ impl CliMainWorker { // Enable op call tracing in core to enable better debugging of op sanitizer // failures. if self.ps.options.trace_ops() { - self.worker.js_runtime.execute_script( + self.worker.js_runtime.execute_script_static( located_script_name!(), "Deno[Deno.internal].core.enableOpCallTracing();", )?; @@ -231,7 +232,7 @@ impl CliMainWorker { self.worker.execute_script( located_script_name!(), - "Deno[Deno.internal].core.enableOpCallTracing();", + ascii_str!("Deno[Deno.internal].core.enableOpCallTracing();"), )?; if mode != TestMode::Documentation { diff --git a/core/bindings.rs b/core/bindings.rs index 00d0cf2e6f..8e701c9034 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -345,7 +345,7 @@ pub extern "C" fn host_initialize_import_meta_object_callback( .expect("Module not found"); let url_key = v8::String::new_external_onebyte_static(scope, b"url").unwrap(); - let url_val = v8::String::new(scope, &info.name).unwrap(); + let url_val = info.name.v8(scope); meta.create_data_property(scope, url_key.into(), url_val.into()); let main_key = @@ -616,7 +616,7 @@ pub fn module_resolve_callback<'s>( let referrer_info = module_map .get_info(&referrer_global) .expect("ModuleInfo not found"); - let referrer_name = referrer_info.name.to_string(); + let referrer_name = referrer_info.name.as_str(); let specifier_str = specifier.to_rust_string_lossy(scope); @@ -628,7 +628,7 @@ pub fn module_resolve_callback<'s>( let maybe_module = module_map.resolve_callback( scope, &specifier_str, - &referrer_name, + referrer_name, assertions, ); if let Some(module) = maybe_module { diff --git a/core/examples/disable_ops.rs b/core/examples/disable_ops.rs index b9a5e7fca4..c75af1c3ff 100644 --- a/core/examples/disable_ops.rs +++ b/core/examples/disable_ops.rs @@ -22,6 +22,6 @@ fn main() { // Deno.core.print() will now be a NOP runtime - .execute_script("", r#"Deno.core.print("I'm broken")"#) + .execute_script_static("", r#"Deno.core.print("I'm broken")"#) .unwrap(); } diff --git a/core/examples/eval_js_value.rs b/core/examples/eval_js_value.rs index e5b823a095..7b7af7c966 100644 --- a/core/examples/eval_js_value.rs +++ b/core/examples/eval_js_value.rs @@ -28,7 +28,7 @@ fn eval( context: &mut JsRuntime, code: &'static str, ) -> Result { - let res = context.execute_script("", code); + let res = context.execute_script_static("", code); match res { Ok(global) => { let scope = &mut context.handle_scope(); diff --git a/core/examples/hello_world.rs b/core/examples/hello_world.rs index 50cbe4e2c7..cce6e2218b 100644 --- a/core/examples/hello_world.rs +++ b/core/examples/hello_world.rs @@ -41,7 +41,7 @@ fn main() { // contains a Deno.core object with several functions for interacting with it. // You can find its definition in core.js. runtime - .execute_script( + .execute_script_static( "", r#" // Print helper function, calling Deno.core.print() diff --git a/core/examples/http_bench_json_ops/main.rs b/core/examples/http_bench_json_ops/main.rs index f0bbec0d9d..7c15f7bf24 100644 --- a/core/examples/http_bench_json_ops/main.rs +++ b/core/examples/http_bench_json_ops/main.rs @@ -165,7 +165,7 @@ fn main() { js_runtime .execute_script( "http_bench_json_ops.js", - include_str!("http_bench_json_ops.js"), + include_ascii_string!("http_bench_json_ops.js"), ) .unwrap(); js_runtime.run_event_loop(false).await diff --git a/core/examples/panik.rs b/core/examples/panik.rs index 1d2286a881..54b46d3371 100644 --- a/core/examples/panik.rs +++ b/core/examples/panik.rs @@ -31,6 +31,6 @@ fn main() { extensions, ..Default::default() }); - rt.execute_script("panik", "Deno.core.ops.op_panik()") + rt.execute_script_static("panik", "Deno.core.ops.op_panik()") .unwrap(); } diff --git a/core/examples/schedule_task.rs b/core/examples/schedule_task.rs index 42d00022d3..348ba76667 100644 --- a/core/examples/schedule_task.rs +++ b/core/examples/schedule_task.rs @@ -50,7 +50,7 @@ fn main() { let future = async move { // Schedule 10 tasks. js_runtime - .execute_script( + .execute_script_static( "", r#"for (let i = 1; i <= 10; i++) Deno.core.ops.op_schedule_task(i);"#, ) diff --git a/core/examples/ts_module_loader.rs b/core/examples/ts_module_loader.rs index 4a38073abc..6adb27977c 100644 --- a/core/examples/ts_module_loader.rs +++ b/core/examples/ts_module_loader.rs @@ -14,6 +14,7 @@ use anyhow::Error; use deno_ast::MediaType; use deno_ast::ParseParams; use deno_ast::SourceTextInfo; +use deno_core::error::AnyError; use deno_core::resolve_import; use deno_core::resolve_path; use deno_core::JsRuntime; @@ -41,11 +42,12 @@ impl ModuleLoader for TypescriptModuleLoader { fn load( &self, module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { - let module_specifier = module_specifier.clone(); - async move { + fn load( + module_specifier: &ModuleSpecifier, + ) -> Result { let path = module_specifier .to_file_path() .map_err(|_| anyhow!("Only file:// URLs are supported."))?; @@ -81,15 +83,14 @@ impl ModuleLoader for TypescriptModuleLoader { } else { code }; - let module = ModuleSource { - code: code.into(), + Ok(ModuleSource::new( module_type, - module_url_specified: module_specifier.to_string(), - module_url_found: module_specifier.to_string(), - }; - Ok(module) + code.into(), + module_specifier, + )) } - .boxed_local() + + futures::future::ready(load(module_specifier)).boxed_local() } } diff --git a/core/examples/wasm.rs b/core/examples/wasm.rs index 7c2d98bc91..5d5c5f6ff0 100644 --- a/core/examples/wasm.rs +++ b/core/examples/wasm.rs @@ -62,6 +62,6 @@ fn main() { }); runtime - .execute_script("", include_str!("wasm.js")) + .execute_script("", include_ascii_string!("wasm.js")) .unwrap(); } diff --git a/core/extensions.rs b/core/extensions.rs index ca618c9b73..4a7b494146 100644 --- a/core/extensions.rs +++ b/core/extensions.rs @@ -43,7 +43,7 @@ impl ExtensionFileSource { self.specifier, Self::find_non_ascii(code) ); - Ok((*code).into()) + Ok(ModuleCode::from_static(code)) } ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) => { let msg = || format!("Failed to read \"{}\"", path.display()); diff --git a/core/fast_string.rs b/core/fast_string.rs new file mode 100644 index 0000000000..95dfb4939b --- /dev/null +++ b/core/fast_string.rs @@ -0,0 +1,243 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use std::borrow::Borrow; +use std::fmt::Debug; +use std::hash::Hash; +use std::sync::Arc; +use url::Url; +use v8::NewStringType; + +/// Module names and 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 [`FastString`] created from a `'static` byte array or string must contain ASCII characters. +/// +/// Examples of ways to construct a [`FastString`]: +/// +/// ```rust +/// # use deno_core::{ascii_str, FastString}; +/// +/// let code: FastString = ascii_str!("a string"); +/// let code: FastString = format!("a string").into(); +/// ``` +pub enum FastString { + /// Created from static data. + Static(&'static str), + + /// Created from static data, known to contain only ASCII chars. + StaticAscii(&'static str), + + /// An owned chunk of data. Note that we use `Box` rather than `Vec` to avoid the + /// storage overhead. + Owned(Box), + + // Scripts loaded from the `deno_graph` infrastructure. + Arc(Arc), +} + +impl FastString { + /// Compile-time function to determine if a string is ASCII. Note that UTF-8 chars + /// longer than one byte have the high-bit set and thus, are not ASCII. + const fn is_ascii(s: &'static [u8]) -> bool { + let mut i = 0; + while i < s.len() { + if !s[i].is_ascii() { + return false; + } + i += 1; + } + true + } + + /// Create a [`FastString`] from a static string. The string may contain non-ASCII characters, and if + /// so, will take the slower path when used in v8. + pub const fn from_static(s: &'static str) -> Self { + if Self::is_ascii(s.as_bytes()) { + Self::StaticAscii(s) + } else { + Self::Static(s) + } + } + + /// Create a [`FastString`] from a static string. If the string contains non-ASCII characters, the compiler + /// will abort. + pub const fn ensure_static_ascii(s: &'static str) -> Self { + if Self::is_ascii(s.as_bytes()) { + Self::StaticAscii(s) + } else { + panic!("This string contained non-ASCII characters and cannot be created with ensure_static_ascii") + } + } + + /// Creates a cheap copy of this [`FastString`], potentially transmuting it to a faster form. Note that this + /// is not a clone operation as it consumes the old [`FastString`]. + pub fn into_cheap_copy(self) -> (Self, Self) { + match self { + Self::Static(s) => (Self::Static(s), Self::Static(s)), + Self::StaticAscii(s) => (Self::StaticAscii(s), Self::StaticAscii(s)), + Self::Arc(s) => (Self::Arc(s.clone()), Self::Arc(s)), + Self::Owned(s) => { + let s: Arc = s.into(); + (Self::Arc(s.clone()), Self::Arc(s)) + } + } + } + + pub const fn try_static_ascii(&self) -> Option<&'static [u8]> { + match self { + Self::StaticAscii(s) => Some(s.as_bytes()), + _ => None, + } + } + + pub fn as_bytes(&self) -> &[u8] { + // TODO(mmastrac): This can be const eventually (waiting for Arc const deref) + match self { + Self::Arc(s) => s.as_bytes(), + Self::Owned(s) => s.as_bytes(), + Self::Static(s) => s.as_bytes(), + Self::StaticAscii(s) => s.as_bytes(), + } + } + + pub fn as_str(&self) -> &str { + // TODO(mmastrac): This can be const eventually (waiting for Arc const deref) + match self { + Self::Arc(s) => s, + Self::Owned(s) => s, + Self::Static(s) => s, + Self::StaticAscii(s) => s, + } + } + + /// Create a v8 string from this [`FastString`]. If the string is static and contains only ASCII characters, + /// an external one-byte static is created. + pub fn v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> v8::Local<'a, v8::String> { + match self.try_static_ascii() { + Some(s) => v8::String::new_external_onebyte_static(scope, s).unwrap(), + None => { + v8::String::new_from_utf8(scope, self.as_bytes(), NewStringType::Normal) + .unwrap() + } + } + } + + /// Truncates a [`FastString`] 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::StaticAscii(b) => *self = Self::StaticAscii(&b[..index]), + Self::Owned(b) => *self = Self::Owned(b[..index].to_owned().into()), + // 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 Hash for FastString { + fn hash(&self, state: &mut H) { + self.as_str().hash(state) + } +} + +impl AsRef for FastString { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl Borrow for FastString { + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl Debug for FastString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(self.as_str(), f) + } +} + +impl Default for FastString { + fn default() -> Self { + Self::StaticAscii("") + } +} + +impl PartialEq for FastString { + fn eq(&self, other: &Self) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl Eq for FastString {} + +/// [`FastString`] can be made cheaply from [`Url`] as we know it's owned and don't need to do an +/// ASCII check. +impl From for FastString { + fn from(value: Url) -> Self { + let s: String = value.into(); + s.into() + } +} + +/// [`FastString`] can be made cheaply from [`String`] as we know it's owned and don't need to do an +/// ASCII check. +impl From for FastString { + fn from(value: String) -> Self { + FastString::Owned(value.into_boxed_str()) + } +} + +/// [`FastString`] can be made cheaply from [`Arc`] as we know it's shared and don't need to do an +/// ASCII check. +impl From> for FastString { + fn from(value: Arc) -> Self { + FastString::Arc(value) + } +} + +/// Include a fast string in the binary. This string is asserted at compile-time to be 7-bit ASCII for optimal +/// v8 performance. +#[macro_export] +macro_rules! include_ascii_string { + ($file:literal) => { + $crate::FastString::ensure_static_ascii(include_str!($file)) + }; +} + +/// Include a fast string in the binary from a string literal. This string is asserted at compile-time to be +/// 7-bit ASCII for optimal v8 performance. +#[macro_export] +macro_rules! ascii_str { + ($str:literal) => { + $crate::FastString::ensure_static_ascii($str) + }; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn truncate() { + let mut s = "123456".to_owned(); + s.truncate(3); + + let mut code: FastString = FastString::from_static("123456"); + code.truncate(3); + assert_eq!(s, code.as_ref()); + + let mut code: FastString = "123456".to_owned().into(); + code.truncate(3); + assert_eq!(s, code.as_ref()); + + let arc_str: Arc = "123456".into(); + let mut code: FastString = arc_str.into(); + code.truncate(3); + assert_eq!(s, code.as_ref()); + } +} diff --git a/core/lib.rs b/core/lib.rs index e8ca36559d..e6088304e2 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -5,6 +5,7 @@ mod bindings; pub mod error; mod error_codes; mod extensions; +mod fast_string; mod flags; mod gotham_state; mod inspector; @@ -59,6 +60,7 @@ pub use crate::extensions::ExtensionFileSource; pub use crate::extensions::ExtensionFileSourceCode; pub use crate::extensions::OpDecl; pub use crate::extensions::OpMiddlewareFn; +pub use crate::fast_string::FastString; pub use crate::flags::v8_set_flags; pub use crate::inspector::InspectorMsg; pub use crate::inspector::InspectorMsgKind; diff --git a/core/modules.rs b/core/modules.rs index cfd68d245e..c63c4dd30b 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -2,7 +2,9 @@ use crate::bindings; use crate::error::generic_error; +use crate::error::AnyError; use crate::extensions::ExtensionFileSource; +use crate::fast_string::FastString; use crate::module_specifier::ModuleSpecifier; use crate::resolve_import; use crate::resolve_url; @@ -19,7 +21,6 @@ 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; @@ -27,12 +28,13 @@ 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; pub type ModuleId = usize; pub(crate) type ModuleLoadId = i32; +pub type ModuleCode = FastString; +pub type ModuleName = FastString; pub const BOM_CHAR: &[u8] = &[0xef, 0xbb, 0xbf]; @@ -200,150 +202,82 @@ impl std::fmt::Display for ModuleType { pub struct ModuleSource { pub code: ModuleCode, pub module_type: ModuleType, - pub module_url_specified: String, - pub module_url_found: String, + module_url_specified: ModuleName, + /// If the module was found somewhere other than the specified address, this will be [`Some`]. + module_url_found: Option, } -/// 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(), +impl ModuleSource { + /// Create a [`ModuleSource`] without a redirect. + pub fn new( + module_type: impl Into, + code: ModuleCode, + specifier: &ModuleSpecifier, + ) -> Self { + let module_url_specified = specifier.as_ref().to_owned().into(); + Self { + code, + module_type: module_type.into(), + module_url_specified, + module_url_found: None, } } - pub fn try_static_ascii(&self) -> Option<&'static [u8]> { - match self { - Self::Static(b) => Some(b), - _ => None, + /// Create a [`ModuleSource`] with a potential redirect. If the `specifier_found` parameter is the same as the + /// specifier, the code behaves the same was as `ModuleSource::new`. + pub fn new_with_redirect( + module_type: impl Into, + code: ModuleCode, + specifier: &ModuleSpecifier, + specifier_found: &ModuleSpecifier, + ) -> Self { + let module_url_found = if specifier == specifier_found { + None + } else { + Some(specifier_found.as_ref().to_owned().into()) + }; + let module_url_specified = specifier.as_ref().to_owned().into(); + Self { + code, + module_type: module_type.into(), + module_url_specified, + module_url_found, } } - /// 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(), + #[cfg(test)] + pub fn for_test(code: &'static str, file: impl AsRef) -> Self { + Self { + code: ModuleCode::from_static(code), + module_type: ModuleType::JavaScript, + module_url_specified: file.as_ref().to_owned().into(), + module_url_found: None, } } - /// 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(), + /// If the `found` parameter is the same as the `specified` parameter, the code behaves the same was as `ModuleSource::for_test`. + #[cfg(test)] + pub fn for_test_with_redirect( + code: &'static str, + specified: impl AsRef, + found: impl AsRef, + ) -> Self { + let specified = specified.as_ref().to_string(); + let found = found.as_ref().to_string(); + let found = if found == specified { + None + } else { + Some(found.into()) + }; + Self { + code: ModuleCode::from_static(code), + module_type: ModuleType::JavaScript, + module_url_specified: specified.into(), + module_url_found: found, } } } -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 { - debug_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 { - debug_assert!(value.is_ascii()); - ModuleCode::Static(value) - } -} - -impl From<&'static [u8; N]> for ModuleCode { - #[inline(always)] - fn from(value: &'static [u8; N]) -> Self { - debug_assert!(value.is_ascii()); - ModuleCode::Static(value) - } -} - pub(crate) type PrepareLoadFuture = dyn Future)>; pub type ModuleSourceFuture = dyn Future>; @@ -391,7 +325,7 @@ pub trait ModuleLoader { fn load( &self, module_specifier: &ModuleSpecifier, - maybe_referrer: Option, + maybe_referrer: Option<&ModuleSpecifier>, is_dyn_import: bool, ) -> Pin>; @@ -433,7 +367,7 @@ impl ModuleLoader for NoopModuleLoader { fn load( &self, module_specifier: &ModuleSpecifier, - maybe_referrer: Option, + maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { let err = generic_error( @@ -556,7 +490,7 @@ impl ModuleLoader for ExtModuleLoader { fn load( &self, module_specifier: &ModuleSpecifier, - maybe_referrer: Option, + maybe_referrer: Option<&ModuleSpecifier>, is_dyn_import: bool, ) -> Pin> { if module_specifier.scheme() != "ext" { @@ -583,23 +517,17 @@ impl ModuleLoader for ExtModuleLoader { let result = if let Some(load_callback) = &self.maybe_load_callback { load_callback(file_source) } else { - match file_source.load() { - Ok(code) => Ok(code), - Err(err) => return futures::future::err(err).boxed_local(), - } + file_source.load() }; - return async move { - let code = result?; - let source = ModuleSource { - code, - module_type: ModuleType::JavaScript, - module_url_specified: specifier.clone(), - module_url_found: specifier.clone(), - }; - Ok(source) + match result { + Ok(code) => { + let res = + ModuleSource::new(ModuleType::JavaScript, code, module_specifier); + return futures::future::ok(res).boxed_local(); + } + Err(err) => return futures::future::err(err).boxed_local(), } - .boxed_local(); } async move { @@ -650,11 +578,12 @@ impl ModuleLoader for FsModuleLoader { fn load( &self, module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dynamic: bool, ) -> Pin> { - let module_specifier = module_specifier.clone(); - async move { + fn load( + module_specifier: &ModuleSpecifier, + ) -> Result { let path = module_specifier.to_file_path().map_err(|_| { generic_error(format!( "Provided module specifier \"{module_specifier}\" is not a file URL." @@ -671,16 +600,12 @@ impl ModuleLoader for FsModuleLoader { ModuleType::JavaScript }; - let code = std::fs::read(path)?; - let module = ModuleSource { - code: code.into(), - module_type, - module_url_specified: module_specifier.to_string(), - module_url_found: module_specifier.to_string(), - }; + let code = std::fs::read_to_string(path)?.into(); + let module = ModuleSource::new(module_type, code, module_specifier); Ok(module) } - .boxed_local() + + futures::future::ready(load(module_specifier)).boxed_local() } } @@ -788,7 +713,7 @@ impl RecursiveModuleLoad { if let Ok(root_specifier) = load.resolve_root() { if let Some(module_id) = module_map_rc .borrow() - .get_id(root_specifier.as_str(), asserted_module_type) + .get_id(root_specifier, asserted_module_type) { load.root_module_id = Some(module_id); load.root_asserted_module_type = Some(asserted_module_type); @@ -893,9 +818,12 @@ impl RecursiveModuleLoad { &mut self, scope: &mut v8::HandleScope, module_request: &ModuleRequest, - module_source: &ModuleSource, + module_source: ModuleSource, ) -> Result<(), ModuleError> { let expected_asserted_module_type = module_source.module_type.into(); + let module_url_found = module_source.module_url_found; + let module_url_specified = module_source.module_url_specified; + if module_request.asserted_module_type != expected_asserted_module_type { return Err(ModuleError::Other(generic_error(format!( "Expected a \"{}\" module but loaded a \"{}\" module.", @@ -905,22 +833,28 @@ impl RecursiveModuleLoad { // Register the module in the module map unless it's already there. If the // specified URL and the "true" URL are different, register the alias. - if module_source.module_url_specified != module_source.module_url_found { + let module_url_found = if let Some(module_url_found) = module_url_found { + let (module_url_found1, module_url_found2) = + module_url_found.into_cheap_copy(); self.module_map_rc.borrow_mut().alias( - &module_source.module_url_specified, + module_url_specified, expected_asserted_module_type, - &module_source.module_url_found, + module_url_found1, ); - } - let maybe_module_id = self.module_map_rc.borrow().get_id( - &module_source.module_url_found, - expected_asserted_module_type, - ); + module_url_found2 + } else { + module_url_specified + }; + + let maybe_module_id = self + .module_map_rc + .borrow() + .get_id(&module_url_found, expected_asserted_module_type); let module_id = match maybe_module_id { Some(id) => { debug!( - "Already-registered module fetched again: {}", - module_source.module_url_found + "Already-registered module fetched again: {:?}", + module_url_found ); id } @@ -929,15 +863,15 @@ impl RecursiveModuleLoad { self.module_map_rc.borrow_mut().new_es_module( scope, self.is_currently_loading_main_module(), - &module_source.module_url_found, - &module_source.code, + module_url_found, + module_source.code, self.is_dynamic_import(), )? } ModuleType::Json => self.module_map_rc.borrow_mut().new_json_module( scope, - &module_source.module_url_found, - &module_source.code, + module_url_found, + module_source.code, )?, }, }; @@ -978,7 +912,7 @@ impl RecursiveModuleLoad { let is_dynamic_import = self.is_dynamic_import(); let fut = async move { let load_result = loader - .load(&specifier, Some(referrer.clone()), is_dynamic_import) + .load(&specifier, Some(&referrer), is_dynamic_import) .await; load_result.map(|s| (request, s)) }; @@ -1033,14 +967,13 @@ impl Stream for RecursiveModuleLoad { specifier: module_specifier.to_string(), asserted_module_type, }; - let module_source = ModuleSource { - module_url_specified: module_specifier.to_string(), - module_url_found: module_specifier.to_string(), - // The code will be discarded, since this module is already in the - // module map. - code: Default::default(), + // The code will be discarded, since this module is already in the + // module map. + let module_source = ModuleSource::new( module_type, - }; + Default::default(), + &module_specifier, + ); futures::future::ok((module_request, module_source)).boxed() } else { let maybe_referrer = match inner.init { @@ -1061,7 +994,11 @@ impl Stream for RecursiveModuleLoad { let is_dynamic_import = inner.is_dynamic_import(); async move { let result = loader - .load(&module_specifier, maybe_referrer, is_dynamic_import) + .load( + &module_specifier, + maybe_referrer.as_ref(), + is_dynamic_import, + ) .await; result.map(|s| (module_request, s)) } @@ -1118,24 +1055,24 @@ pub(crate) struct ModuleRequest { pub asserted_module_type: AssertedModuleType, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, PartialEq)] pub(crate) struct ModuleInfo { #[allow(unused)] pub id: ModuleId, // Used in "bindings.rs" for "import.meta.main" property value. pub main: bool, - pub name: String, + pub name: ModuleName, pub requests: Vec, pub module_type: ModuleType, } /// A symbolic module entity. -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[derive(Debug, PartialEq)] pub(crate) enum SymbolicModule { /// This module is an alias to another module. /// This is useful such that multiple names could point to /// the same underlying module (particularly due to redirects). - Alias(String), + Alias(ModuleName), /// This module associates with a V8 module by id. Mod(ModuleId), } @@ -1146,38 +1083,13 @@ 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 pub handles: Vec>, pub info: Vec, - pub(crate) by_name: HashMap<(String, AssertedModuleType), SymbolicModule>, + pub(crate) by_name_js: HashMap, + pub(crate) by_name_json: HashMap, pub(crate) next_load_id: ModuleLoadId, // Handling of futures for loading module sources @@ -1198,6 +1110,24 @@ pub(crate) struct ModuleMap { } impl ModuleMap { + pub fn collect_modules( + &self, + ) -> Vec<(AssertedModuleType, &ModuleName, &SymbolicModule)> { + let mut output = vec![]; + for module_type in [ + AssertedModuleType::JavaScriptOrWasm, + AssertedModuleType::Json, + ] { + output.extend( + self + .by_name(module_type) + .iter() + .map(|x| (module_type, x.0, x.1)), + ) + } + output + } + pub fn serialize_for_snapshotting( &self, scope: &mut v8::HandleScope, @@ -1217,12 +1147,7 @@ impl ModuleMap { let main = v8::Boolean::new(scope, info.main); module_info_arr.set_index(scope, 1, main.into()); - let name = v8::String::new_from_one_byte( - scope, - info.name.as_bytes(), - v8::NewStringType::Normal, - ) - .unwrap(); + let name = info.name.v8(scope); module_info_arr.set_index(scope, 2, name.into()); let array_len = 2 * info.requests.len() as i32; @@ -1253,25 +1178,19 @@ impl ModuleMap { } array.set_index(scope, 1, info_arr.into()); - let by_name_array = v8::Array::new(scope, self.by_name.len() as i32); + let by_name = self.collect_modules(); + let by_name_array = v8::Array::new(scope, by_name.len() as i32); { - for (i, elem) in self.by_name.iter().enumerate() { + for (i, (module_type, name, module)) in by_name.into_iter().enumerate() { let arr = v8::Array::new(scope, 3); - let (specifier, asserted_module_type) = elem.0; - let specifier = v8::String::new_from_one_byte( - scope, - specifier.as_bytes(), - v8::NewStringType::Normal, - ) - .unwrap(); + let specifier = name.v8(scope); arr.set_index(scope, 0, specifier.into()); - let asserted_module_type = - v8::Integer::new(scope, *asserted_module_type as i32); + let asserted_module_type = v8::Integer::new(scope, module_type as i32); arr.set_index(scope, 1, asserted_module_type.into()); - let symbolic_module: v8::Local = match &elem.1 { + let symbolic_module: v8::Local = match module { SymbolicModule::Alias(alias) => { let alias = v8::String::new_from_one_byte( scope, @@ -1348,7 +1267,8 @@ impl ModuleMap { let name = module_info_arr .get_index(scope, 2) .unwrap() - .to_rust_string_lossy(scope); + .to_rust_string_lossy(scope) + .into(); let requests_arr: v8::Local = module_info_arr .get_index(scope, 3) @@ -1404,11 +1324,15 @@ impl ModuleMap { self.info = info; } + self + .by_name_mut(AssertedModuleType::JavaScriptOrWasm) + .clear(); + self.by_name_mut(AssertedModuleType::Json).clear(); + { let by_name_arr: v8::Local = local_data.get_index(scope, 2).unwrap().try_into().unwrap(); let len = by_name_arr.length() as usize; - let mut by_name = HashMap::with_capacity(len); for i in 0..len { let arr: v8::Local = by_name_arr @@ -1430,7 +1354,6 @@ impl ModuleMap { 1 => AssertedModuleType::Json, _ => unreachable!(), }; - let key = (specifier, asserted_module_type); let symbolic_module_val = arr.get_index(scope, 2).unwrap(); let val = if symbolic_module_val.is_number() { @@ -1443,13 +1366,15 @@ impl ModuleMap { .unwrap(), ) } else { - SymbolicModule::Alias(symbolic_module_val.to_rust_string_lossy(scope)) + SymbolicModule::Alias( + symbolic_module_val.to_rust_string_lossy(scope).into(), + ) }; - by_name.insert(key, val); + self + .by_name_mut(asserted_module_type) + .insert(specifier.into(), val); } - - self.by_name = by_name; } self.handles = snapshotted_data.module_handles; @@ -1463,7 +1388,8 @@ impl ModuleMap { Self { handles: vec![], info: vec![], - by_name: HashMap::new(), + by_name_js: HashMap::new(), + by_name_json: HashMap::new(), next_load_id: 1, loader, op_state, @@ -1479,16 +1405,20 @@ impl ModuleMap { /// that had been redirected. fn get_id( &self, - name: &str, + name: impl AsRef, asserted_module_type: AssertedModuleType, ) -> Option { - let mut mod_name = name; + let map = self.by_name(asserted_module_type); + let first_symbolic_module = map.get(name.as_ref())?; + let mut mod_name = match first_symbolic_module { + SymbolicModule::Mod(mod_id) => return Some(*mod_id), + SymbolicModule::Alias(target) => target, + }; loop { - let symbolic_module = self - .by_name - .get(&(mod_name.to_string(), asserted_module_type))?; + let symbolic_module = map.get(mod_name.as_ref())?; match symbolic_module { SymbolicModule::Alias(target) => { + debug_assert!(mod_name != target); mod_name = target; } SymbolicModule::Mod(mod_id) => return Some(*mod_id), @@ -1496,51 +1426,13 @@ impl ModuleMap { } } - 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) => { - debug_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: N, - source: &ModuleCode, - ) -> Result { - // Manual monomorphization (TODO: replace w/momo) - self.new_json_module_inner(scope, name.into(), source) - } - - fn new_json_module_inner( + fn new_json_module( &mut self, scope: &mut v8::HandleScope, name: ModuleName, - source: &ModuleCode, + source: ModuleCode, ) -> Result { - let name_str = Self::string_from_module_name(scope, &name).unwrap(); + let name_str = name.v8(scope); let source_str = v8::String::new_from_utf8( scope, strip_bom(source.as_bytes()), @@ -1572,47 +1464,23 @@ 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.as_ref(), - ModuleType::Json, - handle, - false, - vec![], - ); + let id = + self.create_module_info(name, ModuleType::Json, handle, false, vec![]); Ok(id) } - /// 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: N, - source: &ModuleCode, - is_dynamic_import: bool, - ) -> Result { - // Manual monomorphization (TODO: replace w/momo) - self.new_es_module_inner( - scope, - main, - name.into(), - source, - is_dynamic_import, - ) - } - - fn new_es_module_inner( + /// Create and compile an ES module. + pub(crate) fn new_es_module( &mut self, scope: &mut v8::HandleScope, main: bool, name: ModuleName, - source: &ModuleCode, + 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 name_str = name.v8(scope); + let source_str = source.v8(scope); let origin = bindings::module_origin(scope, name_str); let source = v8::script_compiler::Source::new(source_str, Some(&origin)); @@ -1694,7 +1562,7 @@ impl ModuleMap { let handle = v8::Global::::new(tc_scope, module); let id = self.create_module_info( - name.as_ref(), + name, ModuleType::JavaScript, handle, main, @@ -1706,22 +1574,22 @@ impl ModuleMap { fn create_module_info( &mut self, - name: &str, + name: FastString, module_type: ModuleType, handle: v8::Global, main: bool, requests: Vec, ) -> ModuleId { let id = self.handles.len(); - self.by_name.insert( - (name.to_string(), module_type.into()), - SymbolicModule::Mod(id), - ); + let (name1, name2) = name.into_cheap_copy(); + self + .by_name_mut(module_type.into()) + .insert(name1, SymbolicModule::Mod(id)); self.handles.push(handle); self.info.push(ModuleInfo { id, main, - name: name.to_string(), + name: name2, requests, module_type, }); @@ -1735,10 +1603,10 @@ impl ModuleMap { fn is_registered( &self, - specifier: &ModuleSpecifier, + specifier: impl AsRef, asserted_module_type: AssertedModuleType, ) -> bool { - if let Some(id) = self.get_id(specifier.as_str(), asserted_module_type) { + if let Some(id) = self.get_id(specifier.as_ref(), asserted_module_type) { let info = self.get_info_by_id(id).unwrap(); return asserted_module_type == info.module_type.into(); } @@ -1746,16 +1614,36 @@ impl ModuleMap { false } + pub(crate) fn by_name( + &self, + asserted_module_type: AssertedModuleType, + ) -> &HashMap { + match asserted_module_type { + AssertedModuleType::Json => &self.by_name_json, + AssertedModuleType::JavaScriptOrWasm => &self.by_name_js, + } + } + + pub(crate) fn by_name_mut( + &mut self, + asserted_module_type: AssertedModuleType, + ) -> &mut HashMap { + match asserted_module_type { + AssertedModuleType::Json => &mut self.by_name_json, + AssertedModuleType::JavaScriptOrWasm => &mut self.by_name_js, + } + } + fn alias( &mut self, - name: &str, + name: FastString, asserted_module_type: AssertedModuleType, - target: &str, + target: FastString, ) { - self.by_name.insert( - (name.to_string(), asserted_module_type), - SymbolicModule::Alias(target.to_string()), - ); + debug_assert_ne!(name, target); + self + .by_name_mut(asserted_module_type) + .insert(name, SymbolicModule::Alias(target)); } #[cfg(test)] @@ -1764,7 +1652,7 @@ impl ModuleMap { name: &str, asserted_module_type: AssertedModuleType, ) -> bool { - let cond = self.by_name.get(&(name.to_string(), asserted_module_type)); + let cond = self.by_name(asserted_module_type).get(name); matches!(cond, Some(SymbolicModule::Alias(_))) } @@ -1792,18 +1680,20 @@ impl ModuleMap { pub(crate) async fn load_main( module_map_rc: Rc>, - specifier: &str, + specifier: impl AsRef, ) -> Result { - let load = RecursiveModuleLoad::main(specifier, module_map_rc.clone()); + let load = + RecursiveModuleLoad::main(specifier.as_ref(), module_map_rc.clone()); load.prepare().await?; Ok(load) } pub(crate) async fn load_side( module_map_rc: Rc>, - specifier: &str, + specifier: impl AsRef, ) -> Result { - let load = RecursiveModuleLoad::side(specifier, module_map_rc.clone()); + let load = + RecursiveModuleLoad::side(specifier.as_ref(), module_map_rc.clone()); load.prepare().await?; Ok(load) } @@ -1845,7 +1735,7 @@ impl ModuleMap { Ok(module_specifier) => { if module_map_rc .borrow() - .is_registered(&module_specifier, asserted_module_type) + .is_registered(module_specifier, asserted_module_type) { async move { (load.id, Ok(load)) }.boxed_local() } else { @@ -1899,6 +1789,7 @@ impl ModuleMap { #[cfg(test)] mod tests { use super::*; + use crate::ascii_str; use crate::JsRuntime; use crate::RuntimeOptions; use crate::Snapshot; @@ -2075,12 +1966,11 @@ import "/a.js"; return Poll::Pending; } match mock_source_code(&inner.url) { - Some(src) => Poll::Ready(Ok(ModuleSource { - code: src.0.into(), - module_type: ModuleType::JavaScript, - module_url_specified: inner.url.clone(), - module_url_found: src.1.to_owned(), - })), + Some(src) => Poll::Ready(Ok(ModuleSource::for_test_with_redirect( + src.0, + inner.url.as_str(), + src.1, + ))), None => Poll::Ready(Err(MockError::LoadErr.into())), } } @@ -2114,7 +2004,7 @@ import "/a.js"; fn load( &self, module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { let mut loads = self.loads.lock(); @@ -2220,7 +2110,7 @@ import "/a.js"; fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { unreachable!() @@ -2248,7 +2138,7 @@ import "/a.js"; }); runtime - .execute_script( + .execute_script_static( "setup.js", r#" function assert(cond) { @@ -2267,19 +2157,20 @@ import "/a.js"; let (mod_a, mod_b) = { let scope = &mut runtime.handle_scope(); let mut module_map = module_map_rc.borrow_mut(); - let specifier_a = "file:///a.js".to_string(); + let specifier_a = ascii_str!("file:///a.js"); let mod_a = module_map .new_es_module( scope, true, - &specifier_a, - &br#" + specifier_a, + ascii_str!( + r#" import { b } from './b.js' if (b() != 'b') throw Error(); let control = 42; Deno.core.ops.op_test(control); "# - .into(), + ), false, ) .unwrap(); @@ -2298,8 +2189,8 @@ import "/a.js"; .new_es_module( scope, false, - "file:///b.js", - &b"export function b() { return 'b' }".into(), + ascii_str!("file:///b.js"), + ascii_str!("export function b() { return 'b' }"), false, ) .unwrap(); @@ -2344,7 +2235,7 @@ import "/a.js"; fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { unreachable!() @@ -2361,7 +2252,7 @@ import "/a.js"; }); runtime - .execute_script( + .execute_script_static( "setup.js", r#" function assert(cond) { @@ -2378,18 +2269,19 @@ import "/a.js"; let (mod_a, mod_b) = { let scope = &mut runtime.handle_scope(); let mut module_map = module_map_rc.borrow_mut(); - let specifier_a = "file:///a.js".to_string(); + let specifier_a = ascii_str!("file:///a.js"); let mod_a = module_map .new_es_module( scope, true, - &specifier_a, - &br#" + specifier_a, + ascii_str!( + r#" import jsonData from './b.json' assert {type: "json"}; assert(jsonData.a == "b"); assert(jsonData.c.d == 10); "# - .into(), + ), false, ) .unwrap(); @@ -2406,8 +2298,8 @@ import "/a.js"; let mod_b = module_map .new_json_module( scope, - "file:///b.json", - &b"{\"a\": \"b\", \"c\": {\"d\": 10}}".into(), + ascii_str!("file:///b.json"), + ascii_str!("{\"a\": \"b\", \"c\": {\"d\": 10}}"), ) .unwrap(); let imports = module_map.get_requested_modules(mod_b).unwrap(); @@ -2449,7 +2341,7 @@ import "/a.js"; fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { async { Err(io::Error::from(io::ErrorKind::NotFound).into()) }.boxed() @@ -2466,7 +2358,7 @@ import "/a.js"; // Test an erroneous dynamic import where the specified module isn't found. run_in_task(move |cx| { runtime - .execute_script( + .execute_script_static( "file:///dyn_import2.js", r#" (async () => { @@ -2510,16 +2402,12 @@ import "/a.js"; fn load( &self, specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { self.load_count.fetch_add(1, Ordering::Relaxed); - let info = ModuleSource { - module_url_specified: specifier.to_string(), - module_url_found: specifier.to_string(), - code: b"export function b() { return 'b' }".into(), - module_type: ModuleType::JavaScript, - }; + let info = + ModuleSource::for_test("export function b() { return 'b' }", specifier); async move { Ok(info) }.boxed() } @@ -2548,7 +2436,7 @@ import "/a.js"; run_in_task(move |cx| { // Dynamically import mod_b runtime - .execute_script( + .execute_script_static( "file:///dyn_import3.js", r#" (async () => { @@ -2594,7 +2482,7 @@ import "/a.js"; run_in_task(move |cx| { runtime - .execute_script( + .execute_script_static( "file:///dyn_import3.js", r#" (async () => { @@ -2638,7 +2526,7 @@ import "/a.js"; fn load( &self, specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { self.load_count.fetch_add(1, Ordering::Relaxed); @@ -2654,12 +2542,7 @@ import "/a.js"; "d.js" => "// pass", _ => unreachable!(), }; - let info = ModuleSource { - module_url_specified: specifier.to_string(), - module_url_found: specifier.to_string(), - code: code.into(), - module_type: ModuleType::JavaScript, - }; + let info = ModuleSource::for_test(code, specifier); async move { Ok(info) }.boxed() } } @@ -2671,7 +2554,7 @@ import "/a.js"; }); runtime - .execute_script( + .execute_script_static( "file:///entry.js", "import('./b.js');\nimport('./a.js');", ) @@ -2913,14 +2796,16 @@ import "/a.js"; #[test] fn recursive_load_main_with_code() { - const MAIN_WITH_CODE_SRC: &str = r#" + const MAIN_WITH_CODE_SRC: FastString = ascii_str!( + r#" import { b } from "/b.js"; import { c } from "/c.js"; if (b() != 'b') throw Error(); if (c() != 'c') throw Error(); if (!import.meta.main) throw Error(); if (import.meta.url != 'file:///main_with_code.js') throw Error(); -"#; +"# + ); let loader = MockLoader::new(); let loads = loader.loads.clone(); @@ -2933,7 +2818,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.into())) + .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC)) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).unwrap(); @@ -3018,25 +2903,21 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); fn load( &self, module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { let module_source = match module_specifier.as_str() { - "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();".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();".into(), - module_type: ModuleType::JavaScript, - }), + "file:///main_module.js" => ModuleSource::for_test( + "if (!import.meta.main) throw Error();", + "file:///main_module.js", + ), + "file:///side_module.js" => ModuleSource::for_test( + "if (import.meta.main) throw Error();", + "file:///side_module.js", + ), _ => unreachable!(), }; - async move { module_source }.boxed() + async move { Ok(module_source) }.boxed() } } @@ -3077,9 +2958,11 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); //TODO: Once the issue with the ModuleNamespaceEntryGetter is fixed, we can maintain a reference to the module // and use it when loading the snapshot let snapshot = { - const MAIN_WITH_CODE_SRC: &str = r#" + const MAIN_WITH_CODE_SRC: FastString = ascii_str!( + r#" await import("./b.js"); - "#; + "# + ); let loader = MockLoader::new(); let mut runtime = JsRuntime::new(RuntimeOptions { @@ -3092,7 +2975,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.into())) + .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC)) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).unwrap(); @@ -3109,17 +2992,19 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); }); //Evaluate the snapshot with an empty function - runtime2.execute_script("check.js", "true").unwrap(); + runtime2.execute_script_static("check.js", "true").unwrap(); } #[test] fn import_meta_snapshot() { let snapshot = { - const MAIN_WITH_CODE_SRC: &str = r#" + const MAIN_WITH_CODE_SRC: ModuleCode = ascii_str!( + r#" if (import.meta.url != 'file:///main_with_code.js') throw Error(); globalThis.meta = import.meta; globalThis.url = import.meta.url; - "#; + "# + ); let loader = MockLoader::new(); let mut runtime = JsRuntime::new(RuntimeOptions { @@ -3132,7 +3017,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.into())) + .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC)) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).unwrap(); @@ -3149,7 +3034,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); }); runtime2 - .execute_script( + .execute_script_static( "check.js", "if (globalThis.url !== 'file:///main_with_code.js') throw Error('x')", ) @@ -3202,23 +3087,4 @@ 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 d68cb36162..89487bc6ca 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -747,7 +747,6 @@ impl JsRuntime { { if let Some(js_files) = ext.get_js_sources() { for file_source in js_files { - // TODO(@AaronO): use JsRuntime::execute_static() here to move src off heap realm.execute_script( self.v8_isolate(), file_source.specifier, @@ -914,16 +913,42 @@ 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: &'static str, - source_code: S, + source_code: ModuleCode, ) -> Result, Error> { self .global_realm() .execute_script(self.v8_isolate(), name, source_code) } + /// Executes traditional JavaScript code (traditional = not ES modules). + /// + /// 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, but it is required to be 7-bit ASCII, eg. + /// + /// - "/some/file/path.js" + /// - "" + /// - "[native code]" + /// + /// The same `name` value can be used for multiple executions. + /// + /// `Error` can usually be downcast to `JsError`. + pub fn execute_script_static( + &mut self, + name: &'static str, + source_code: &'static str, + ) -> Result, Error> { + self.global_realm().execute_script( + self.v8_isolate(), + name, + ModuleCode::from_static(source_code), + ) + } + /// Takes a snapshot. The isolate should have been created with will_snapshot /// set to true. /// @@ -1895,7 +1920,7 @@ impl JsRuntime { let register_result = load.register_and_recurse( &mut self.handle_scope(), &request, - &info, + info, ); match register_result { @@ -2071,11 +2096,12 @@ impl JsRuntime { ) -> Result { let module_map_rc = Self::module_map(self.v8_isolate()); if let Some(code) = code { + let specifier = specifier.as_str().to_owned().into(); let scope = &mut self.handle_scope(); // true for main module module_map_rc .borrow_mut() - .new_es_module(scope, true, specifier, &code, false) + .new_es_module(scope, true, specifier, code, false) .map_err(|e| match e { ModuleError::Exception(exception) => { let exception = v8::Local::new(scope, exception); @@ -2086,12 +2112,12 @@ impl JsRuntime { } let mut load = - ModuleMap::load_main(module_map_rc.clone(), specifier.as_str()).await?; + ModuleMap::load_main(module_map_rc.clone(), &specifier).await?; while let Some(load_result) = load.next().await { let (request, info) = load_result?; let scope = &mut self.handle_scope(); - load.register_and_recurse(scope, &request, &info).map_err( + load.register_and_recurse(scope, &request, info).map_err( |e| match e { ModuleError::Exception(exception) => { let exception = v8::Local::new(scope, exception); @@ -2125,11 +2151,12 @@ impl JsRuntime { ) -> Result { let module_map_rc = Self::module_map(self.v8_isolate()); if let Some(code) = code { + let specifier = specifier.as_str().to_owned().into(); let scope = &mut self.handle_scope(); // false for side module (not main module) module_map_rc .borrow_mut() - .new_es_module(scope, false, specifier, &code, false) + .new_es_module(scope, false, specifier, code, false) .map_err(|e| match e { ModuleError::Exception(exception) => { let exception = v8::Local::new(scope, exception); @@ -2140,12 +2167,12 @@ impl JsRuntime { } let mut load = - ModuleMap::load_side(module_map_rc.clone(), specifier.as_str()).await?; + ModuleMap::load_side(module_map_rc.clone(), &specifier).await?; while let Some(load_result) = load.next().await { let (request, info) = load_result?; let scope = &mut self.handle_scope(); - load.register_and_recurse(scope, &request, &info).map_err( + load.register_and_recurse(scope, &request, info).map_err( |e| match e { ModuleError::Exception(exception) => { let exception = v8::Local::new(scope, exception); @@ -2439,7 +2466,7 @@ impl JsRuntime { /// .expect("Handle the error properly"); /// let source_code = "var a = 0; a + 1"; /// let result = new_realm -/// .execute_script(runtime.v8_isolate(), "", source_code) +/// .execute_script_static(runtime.v8_isolate(), "", source_code) /// .expect("Handle the error properly"); /// # drop(result); /// ``` @@ -2525,17 +2552,30 @@ 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_static( &self, isolate: &mut v8::Isolate, name: &'static str, - source_code: S, + source_code: &'static str, ) -> Result, Error> { - // Manual monomorphization (TODO: replace w/momo) - self.execute_script_inner(isolate, name, source_code.into()) + self.execute_script(isolate, name, ModuleCode::from_static(source_code)) } - fn execute_script_inner( + /// Executes traditional JavaScript code (traditional = not ES modules) in the + /// realm's context. + /// + /// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`]. + /// + /// The `name` parameter can be a filepath or any other string. E.g.: + /// + /// - "/some/file/path.js" + /// - "" + /// - "[native code]" + /// + /// The same `name` value can be used for multiple executions. + /// + /// `Error` can usually be downcast to `JsError`. + pub fn execute_script( &self, isolate: &mut v8::Isolate, name: &'static str, @@ -2687,8 +2727,10 @@ pub fn queue_async_op( #[cfg(test)] pub mod tests { use super::*; + use crate::ascii_str; use crate::error::custom_error; use crate::error::AnyError; + use crate::include_ascii_string; use crate::modules::AssertedModuleType; use crate::modules::ModuleInfo; use crate::modules::ModuleSource; @@ -2787,7 +2829,7 @@ pub mod tests { }); runtime - .execute_script( + .execute_script_static( "setup.js", r#" function assert(cond) { @@ -2806,7 +2848,7 @@ pub mod tests { fn test_ref_unref_ops() { let (mut runtime, _dispatch_count) = setup(Mode::AsyncDeferred); runtime - .execute_script( + .execute_script_static( "filename.js", r#" @@ -2824,7 +2866,7 @@ pub mod tests { assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 0); } runtime - .execute_script( + .execute_script_static( "filename.js", r#" Deno.core.ops.op_unref_op(p1[promiseIdSymbol]); @@ -2840,7 +2882,7 @@ pub mod tests { assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 2); } runtime - .execute_script( + .execute_script_static( "filename.js", r#" Deno.core.ops.op_ref_op(p1[promiseIdSymbol]); @@ -2861,7 +2903,7 @@ pub mod tests { fn test_dispatch() { let (mut runtime, dispatch_count) = setup(Mode::Async); runtime - .execute_script( + .execute_script_static( "filename.js", r#" let control = 42; @@ -2881,7 +2923,7 @@ pub mod tests { fn test_op_async_promise_id() { let (mut runtime, _dispatch_count) = setup(Mode::Async); runtime - .execute_script( + .execute_script_static( "filename.js", r#" @@ -2898,7 +2940,7 @@ pub mod tests { fn test_dispatch_no_zero_copy_buf() { let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(false)); runtime - .execute_script( + .execute_script_static( "filename.js", r#" @@ -2913,7 +2955,7 @@ pub mod tests { fn test_dispatch_stack_zero_copy_bufs() { let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(true)); runtime - .execute_script( + .execute_script_static( "filename.js", r#" @@ -2928,13 +2970,16 @@ pub mod tests { #[test] fn test_execute_script_return_value() { let mut runtime = JsRuntime::new(Default::default()); - let value_global = runtime.execute_script("a.js", "a = 1 + 2").unwrap(); + let value_global = + runtime.execute_script_static("a.js", "a = 1 + 2").unwrap(); { let scope = &mut runtime.handle_scope(); let value = value_global.open(scope); assert_eq!(value.integer_value(scope).unwrap(), 3); } - let value_global = runtime.execute_script("b.js", "b = 'foobar'").unwrap(); + let value_global = runtime + .execute_script_static("b.js", "b = 'foobar'") + .unwrap(); { let scope = &mut runtime.handle_scope(); let value = value_global.open(scope); @@ -2951,7 +2996,7 @@ pub mod tests { let mut runtime = JsRuntime::new(Default::default()); run_in_task(move |cx| { let value_global = runtime - .execute_script("a.js", "Promise.resolve(1 + 2)") + .execute_script_static("a.js", "Promise.resolve(1 + 2)") .unwrap(); let v = runtime.poll_value(&value_global, cx); { @@ -2962,7 +3007,7 @@ pub mod tests { } let value_global = runtime - .execute_script( + .execute_script_static( "a.js", "Promise.resolve(new Promise(resolve => resolve(2 + 2)))", ) @@ -2976,7 +3021,7 @@ pub mod tests { } let value_global = runtime - .execute_script("a.js", "Promise.reject(new Error('fail'))") + .execute_script_static("a.js", "Promise.reject(new Error('fail'))") .unwrap(); let v = runtime.poll_value(&value_global, cx); assert!( @@ -2984,7 +3029,7 @@ pub mod tests { ); let value_global = runtime - .execute_script("a.js", "new Promise(resolve => {})") + .execute_script_static("a.js", "new Promise(resolve => {})") .unwrap(); let v = runtime.poll_value(&value_global, cx); matches!(v, Poll::Ready(Err(e)) if e.to_string() == "Promise resolution is still pending but the event loop has already resolved."); @@ -2995,7 +3040,7 @@ pub mod tests { async fn test_resolve_value() { let mut runtime = JsRuntime::new(Default::default()); let value_global = runtime - .execute_script("a.js", "Promise.resolve(1 + 2)") + .execute_script_static("a.js", "Promise.resolve(1 + 2)") .unwrap(); let result_global = runtime.resolve_value(value_global).await.unwrap(); { @@ -3005,7 +3050,7 @@ pub mod tests { } let value_global = runtime - .execute_script( + .execute_script_static( "a.js", "Promise.resolve(new Promise(resolve => resolve(2 + 2)))", ) @@ -3018,7 +3063,7 @@ pub mod tests { } let value_global = runtime - .execute_script("a.js", "Promise.reject(new Error('fail'))") + .execute_script_static("a.js", "Promise.reject(new Error('fail'))") .unwrap(); let err = runtime.resolve_value(value_global).await.unwrap_err(); assert_eq!( @@ -3027,7 +3072,7 @@ pub mod tests { ); let value_global = runtime - .execute_script("a.js", "new Promise(resolve => {})") + .execute_script_static("a.js", "new Promise(resolve => {})") .unwrap(); let error_string = runtime .resolve_value(value_global) @@ -3046,7 +3091,7 @@ pub mod tests { let v8_isolate_handle = runtime.v8_isolate().thread_safe_handle(); // Run an infinite loop in Webassemby code, which should be terminated. - let promise = runtime.execute_script("infinite_wasm_loop.js", + let promise = runtime.execute_script_static("infinite_wasm_loop.js", r#" (async () => { const wasmCode = new Uint8Array([ @@ -3069,7 +3114,7 @@ pub mod tests { assert!(ok); }); let err = runtime - .execute_script( + .execute_script_static( "infinite_wasm_loop2.js", "globalThis.wasmInstance.exports.infinite_loop();", ) @@ -3082,7 +3127,7 @@ pub mod tests { // Verify that the isolate usable again. runtime - .execute_script("simple.js", "1 + 1") + .execute_script_static("simple.js", "1 + 1") .expect("execution should be possible again"); terminator_thread.join().unwrap(); @@ -3103,7 +3148,7 @@ pub mod tests { }); // Rn an infinite loop, which should be terminated. - match isolate.execute_script("infinite_loop.js", "for(;;) {}") { + match isolate.execute_script_static("infinite_loop.js", "for(;;) {}") { Ok(_) => panic!("execution should be terminated"), Err(e) => { assert_eq!(e.to_string(), "Uncaught Error: execution terminated") @@ -3117,7 +3162,7 @@ pub mod tests { // Verify that the isolate usable again. isolate - .execute_script("simple.js", "1 + 1") + .execute_script_static("simple.js", "1 + 1") .expect("execution should be possible again"); terminator_thread.join().unwrap(); @@ -3139,7 +3184,7 @@ pub mod tests { fn syntax_error() { let mut runtime = JsRuntime::new(Default::default()); let src = "hocuspocus("; - let r = runtime.execute_script("i.js", src); + let r = runtime.execute_script_static("i.js", src); let e = r.unwrap_err(); let js_error = e.downcast::().unwrap(); let frame = js_error.frames.first().unwrap(); @@ -3154,7 +3199,7 @@ pub mod tests { .execute_script( "encode_decode_test.js", // Note: We make this to_owned because it contains non-ASCII chars - include_str!("encode_decode_test.js").to_owned(), + include_str!("encode_decode_test.js").to_owned().into(), ) .unwrap(); if let Poll::Ready(Err(_)) = runtime.poll_event_loop(cx, false) { @@ -3170,7 +3215,7 @@ pub mod tests { runtime .execute_script( "serialize_deserialize_test.js", - include_str!("serialize_deserialize_test.js"), + include_ascii_string!("serialize_deserialize_test.js"), ) .unwrap(); if let Poll::Ready(Err(_)) = runtime.poll_event_loop(cx, false) { @@ -3198,7 +3243,7 @@ pub mod tests { }); run_in_task(move |cx| { runtime - .execute_script( + .execute_script_static( "error_builder_test.js", include_str!("error_builder_test.js"), ) @@ -3216,7 +3261,7 @@ pub mod tests { will_snapshot: true, ..Default::default() }); - runtime.execute_script("a.js", "a = 1 + 2").unwrap(); + runtime.execute_script_static("a.js", "a = 1 + 2").unwrap(); runtime.snapshot() }; @@ -3226,7 +3271,7 @@ pub mod tests { ..Default::default() }); runtime2 - .execute_script("check.js", "if (a != 3) throw Error('x')") + .execute_script_static("check.js", "if (a != 3) throw Error('x')") .unwrap(); } @@ -3237,7 +3282,9 @@ pub mod tests { will_snapshot: true, ..Default::default() }); - runtime.execute_script("a.js", "let a = 1 + 2").unwrap(); + runtime + .execute_script_static("a.js", "let a = 1 + 2") + .unwrap(); runtime.snapshot() }; @@ -3250,9 +3297,9 @@ pub mod tests { let startup_data = { runtime - .execute_script("check_a.js", "if (a != 3) throw Error('x')") + .execute_script_static("check_a.js", "if (a != 3) throw Error('x')") .unwrap(); - runtime.execute_script("b.js", "b = 2 + 3").unwrap(); + runtime.execute_script_static("b.js", "b = 2 + 3").unwrap(); runtime.snapshot() }; @@ -3263,10 +3310,10 @@ pub mod tests { ..Default::default() }); runtime - .execute_script("check_b.js", "if (b != 5) throw Error('x')") + .execute_script_static("check_b.js", "if (b != 5) throw Error('x')") .unwrap(); runtime - .execute_script("check2.js", "if (!Deno.core) throw Error('x')") + .execute_script_static("check2.js", "if (!Deno.core) throw Error('x')") .unwrap(); } } @@ -3279,7 +3326,7 @@ pub mod tests { ..Default::default() }); runtime - .execute_script( + .execute_script_static( "a.js", r#" Deno.core.ops.op_set_macrotask_callback(() => { @@ -3304,7 +3351,7 @@ pub mod tests { ..Default::default() }); runtime2 - .execute_script("check.js", "if (a != 3) throw Error('x')") + .execute_script_static("check.js", "if (a != 3) throw Error('x')") .unwrap(); } @@ -3315,7 +3362,7 @@ pub mod tests { will_snapshot: true, ..Default::default() }); - runtime.execute_script("a.js", "a = 1 + 2").unwrap(); + runtime.execute_script_static("a.js", "a = 1 + 2").unwrap(); let snap: &[u8] = &runtime.snapshot(); Vec::from(snap).into_boxed_slice() }; @@ -3326,7 +3373,7 @@ pub mod tests { ..Default::default() }); runtime2 - .execute_script("check.js", "if (a != 3) throw Error('x')") + .execute_script_static("check.js", "if (a != 3) throw Error('x')") .unwrap(); } @@ -3351,7 +3398,7 @@ pub mod tests { fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { async { Err(generic_error("Module loading is not supported")) } @@ -3366,11 +3413,12 @@ pub mod tests { }); let specifier = crate::resolve_url("file:///main.js").unwrap(); - let source_code = r#" + let source_code = ascii_str!( + r#" export const a = "b"; export default 1 + 2; "# - .into(); + ); let module_id = futures::executor::block_on( runtime.load_main_module(&specifier, Some(source_code)), @@ -3435,7 +3483,7 @@ pub mod tests { }, ); let err = runtime - .execute_script( + .execute_script_static( "script name", r#"let s = ""; while(true) { s += "Hello"; }"#, ) @@ -3488,7 +3536,7 @@ pub mod tests { ); let err = runtime - .execute_script( + .execute_script_static( "script name", r#"let s = ""; while(true) { s += "Hello"; }"#, ) @@ -3520,7 +3568,7 @@ pub mod tests { fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { eprintln!("load() should not be called"); @@ -3563,7 +3611,7 @@ pub mod tests { ModuleInfo { id, main, - name: specifier.to_string(), + name: specifier.into(), requests: vec![crate::modules::ModuleRequest { specifier: format!("file:///{prev}.js"), asserted_module_type: AssertedModuleType::JavaScriptOrWasm, @@ -3577,7 +3625,13 @@ pub mod tests { let module_map = module_map_rc.borrow(); assert_eq!(module_map.handles.len(), modules.len()); assert_eq!(module_map.info.len(), modules.len()); - assert_eq!(module_map.by_name.len(), modules.len()); + assert_eq!( + module_map.by_name(AssertedModuleType::Json).len() + + module_map + .by_name(AssertedModuleType::JavaScriptOrWasm) + .len(), + modules.len() + ); assert_eq!(module_map.next_load_id, (modules.len() + 1) as ModuleLoadId); @@ -3586,8 +3640,8 @@ pub mod tests { assert_eq!(module_map.info.get(info.id).unwrap(), info); assert_eq!( module_map - .by_name - .get(&(info.name.clone(), AssertedModuleType::JavaScriptOrWasm)) + .by_name(AssertedModuleType::JavaScriptOrWasm) + .get(&info.name) .unwrap(), &SymbolicModule::Mod(info.id) ); @@ -3610,7 +3664,8 @@ pub mod tests { }); let specifier = crate::resolve_url("file:///0.js").unwrap(); - let source_code = r#"export function f0() { return "hello world" }"#.into(); + let source_code = + ascii_str!(r#"export function f0() { return "hello world" }"#); let id = futures::executor::block_on( runtime.load_side_module(&specifier, Some(source_code)), ) @@ -3624,7 +3679,7 @@ pub mod tests { modules.push(ModuleInfo { id, main: false, - name: specifier.to_string(), + name: specifier.into(), requests: vec![], module_type: ModuleType::JavaScript, }); @@ -3668,9 +3723,8 @@ pub mod tests { let source_code = r#"(async () => { const mod = await import("file:///400.js"); return mod.f400() + " " + Deno.core.ops.op_test(); - })();"# - .to_string(); - let val = runtime3.execute_script(".", source_code).unwrap(); + })();"#; + let val = runtime3.execute_script_static(".", source_code).unwrap(); let val = futures::executor::block_on(runtime3.resolve_value(val)).unwrap(); { let scope = &mut runtime3.handle_scope(); @@ -3684,7 +3738,7 @@ pub mod tests { fn test_error_without_stack() { let mut runtime = JsRuntime::new(RuntimeOptions::default()); // SyntaxError - let result = runtime.execute_script( + let result = runtime.execute_script_static( "error_without_stack.js", r#" function main() { @@ -3701,7 +3755,7 @@ main(); #[test] fn test_error_stack() { let mut runtime = JsRuntime::new(RuntimeOptions::default()); - let result = runtime.execute_script( + let result = runtime.execute_script_static( "error_stack.js", r#" function assert(cond) { @@ -3727,7 +3781,7 @@ main(); let mut runtime = JsRuntime::new(RuntimeOptions::default()); run_in_task(move |cx| { runtime - .execute_script( + .execute_script_static( "error_async_stack.js", r#" (async () => { @@ -3781,7 +3835,7 @@ main(); run_in_task(move |cx| { runtime - .execute_script( + .execute_script_static( "test_error_context_sync.js", r#" let errMessage; @@ -3798,7 +3852,7 @@ if (errMessage !== "higher-level sync error: original sync error") { .unwrap(); let promise = runtime - .execute_script( + .execute_script_static( "test_error_context_async.js", r#" @@ -3830,7 +3884,7 @@ if (errMessage !== "higher-level sync error: original sync error") { let mut runtime = JsRuntime::new(RuntimeOptions::default()); run_in_task(move |cx| { runtime - .execute_script( + .execute_script_static( "pump_message_loop.js", r#" function assertEquals(a, b) { @@ -3860,12 +3914,15 @@ assertEquals(1, notify_return_value); // noop script, will resolve promise from first script runtime - .execute_script("pump_message_loop2.js", r#"assertEquals(1, 1);"#) + .execute_script_static( + "pump_message_loop2.js", + r#"assertEquals(1, 1);"#, + ) .unwrap(); // check that promise from `Atomics.waitAsync` has been resolved runtime - .execute_script( + .execute_script_static( "pump_message_loop3.js", r#"assertEquals(globalThis.resolved, true);"#, ) @@ -3878,7 +3935,7 @@ assertEquals(1, notify_return_value); let mut runtime = JsRuntime::new(RuntimeOptions::default()); // Call non-existent op so we get error from `core.js` let error = runtime - .execute_script( + .execute_script_static( "core_js_stack_frame.js", "Deno.core.opAsync('non_existent');", ) @@ -3895,7 +3952,7 @@ assertEquals(1, notify_return_value); ..Default::default() }; let mut runtime = JsRuntime::new(options); - runtime.execute_script("", "").unwrap(); + runtime.execute_script_static("", "").unwrap(); } #[ignore] // TODO(@littledivy): Fast API ops when snapshot is not loaded. @@ -3903,7 +3960,7 @@ assertEquals(1, notify_return_value); fn test_is_proxy() { let mut runtime = JsRuntime::new(RuntimeOptions::default()); let all_true: v8::Global = runtime - .execute_script( + .execute_script_static( "is_proxy.js", r#" (function () { @@ -3951,7 +4008,7 @@ assertEquals(1, notify_return_value); }); runtime - .execute_script( + .execute_script_static( "op_async_borrow.js", "Deno.core.opAsync(\"op_async_borrow\")", ) @@ -3982,7 +4039,7 @@ assertEquals(1, notify_return_value); }); runtime - .execute_script( + .execute_script_static( "op_sync_serialize_object_with_numbers_as_keys.js", r#" Deno.core.ops.op_sync_serialize_object_with_numbers_as_keys({ @@ -4024,7 +4081,7 @@ Deno.core.ops.op_sync_serialize_object_with_numbers_as_keys({ }); runtime - .execute_script( + .execute_script_static( "op_async_serialize_object_with_numbers_as_keys.js", r#" @@ -4060,7 +4117,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }); runtime - .execute_script( + .execute_script_static( "macrotasks_and_nextticks.js", r#" @@ -4094,7 +4151,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { let mut runtime = JsRuntime::new(Default::default()); runtime - .execute_script( + .execute_script_static( "multiple_macrotasks_and_nextticks.js", r#" Deno.core.ops.op_set_macrotask_callback(() => { return true; }); @@ -4137,7 +4194,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }); runtime - .execute_script( + .execute_script_static( "has_tick_scheduled.js", r#" Deno.core.ops.op_set_macrotask_callback(() => { @@ -4208,16 +4265,14 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { async move { - Ok(ModuleSource { - 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, - }) + Ok(ModuleSource::for_test( + "console.log('hello world');", + "file:///main.js", + )) } .boxed_local() } @@ -4230,7 +4285,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')".into(); + let source_code = ascii_str!("Deno.core.print('hello\\n')"); let module_id = futures::executor::block_on( runtime.load_main_module(&specifier, Some(source_code)), @@ -4264,7 +4319,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }); runtime - .execute_script( + .execute_script_static( "promise_reject_callback.js", r#" // Note: |promise| is not the promise created below, it's a child. @@ -4287,7 +4342,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { assert_eq!(1, PROMISE_REJECT.load(Ordering::Relaxed)); runtime - .execute_script( + .execute_script_static( "promise_reject_callback.js", r#" { @@ -4332,7 +4387,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }}); Deno.core.opAsync("op_void_async").then(() => Promise.reject({number})); "# - ) + ).into() ) .unwrap(); } @@ -4341,7 +4396,11 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { for (realm, realm_name, number) in realm_expectations { let reject_value = realm - .execute_script(runtime.v8_isolate(), "", "globalThis.rejectValue") + .execute_script_static( + runtime.v8_isolate(), + "", + "globalThis.rejectValue", + ) .unwrap(); let scope = &mut realm.handle_scope(runtime.v8_isolate()); let reject_value = v8::Local::new(scope, reject_value); @@ -4382,25 +4441,18 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { - let source = r#" + let code = r#" Deno.core.ops.op_set_promise_reject_callback((type, promise, reason) => { Deno.core.ops.op_promise_reject(); }); throw new Error('top level throw'); "#; - async move { - Ok(ModuleSource { - code: source.into(), - module_url_specified: "file:///main.js".to_string(), - module_url_found: "file:///main.js".to_string(), - module_type: ModuleType::JavaScript, - }) - } - .boxed_local() + async move { Ok(ModuleSource::for_test(code, "file:///main.js")) } + .boxed_local() } } @@ -4434,7 +4486,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { ..Default::default() }); assert!(runtime - .execute_script( + .execute_script_static( "test_op_return_serde_v8_error.js", "Deno.core.ops.op_err()" ) @@ -4459,7 +4511,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { ..Default::default() }); let r = runtime - .execute_script("test.js", "Deno.core.ops.op_add_4(1, 2, 3, 4)") + .execute_script_static("test.js", "Deno.core.ops.op_add_4(1, 2, 3, 4)") .unwrap(); let scope = &mut runtime.handle_scope(); assert_eq!(r.open(scope).integer_value(scope), Some(10)); @@ -4482,7 +4534,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { ..Default::default() }); let r = runtime - .execute_script("test.js", "Deno.core.ops.op_foo()") + .execute_script_static("test.js", "Deno.core.ops.op_foo()") .unwrap(); let scope = &mut runtime.handle_scope(); assert!(r.open(scope).is_undefined()); @@ -4511,7 +4563,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }); runtime - .execute_script( + .execute_script_static( "test.js", r#" const a1 = new Uint8Array([1,2,3]); @@ -4579,7 +4631,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { ..Default::default() }); runtime - .execute_script( + .execute_script_static( "test.js", r#" if (Deno.core.ops.op_foo() !== 42) { @@ -4607,9 +4659,9 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { assert_ne!(realm.context(), &main_context); assert_ne!(realm.global_object(runtime.v8_isolate()), main_global); - let main_object = runtime.execute_script("", "Object").unwrap(); + let main_object = runtime.execute_script_static("", "Object").unwrap(); let realm_object = realm - .execute_script(runtime.v8_isolate(), "", "Object") + .execute_script_static(runtime.v8_isolate(), "", "Object") .unwrap(); assert_ne!(main_object, realm_object); } @@ -4628,7 +4680,11 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }); let realm = runtime.create_realm().unwrap(); let ret = realm - .execute_script(runtime.v8_isolate(), "", "Deno.core.ops.op_test()") + .execute_script_static( + runtime.v8_isolate(), + "", + "Deno.core.ops.op_test()", + ) .unwrap(); let scope = &mut realm.handle_scope(runtime.v8_isolate()); @@ -4665,7 +4721,11 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { }); let realm = runtime.create_realm().unwrap(); let ret = realm - .execute_script(runtime.v8_isolate(), "", "Deno.core.ops.op_test()") + .execute_script_static( + runtime.v8_isolate(), + "", + "Deno.core.ops.op_test()", + ) .unwrap(); let scope = &mut realm.handle_scope(runtime.v8_isolate()); @@ -4701,7 +4761,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { // Test in both realms for realm in [runtime.global_realm(), new_realm].into_iter() { let ret = realm - .execute_script( + .execute_script_static( runtime.v8_isolate(), "", r#" @@ -4753,7 +4813,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { // Test in both realms for realm in [global_realm, new_realm].into_iter() { let ret = realm - .execute_script( + .execute_script_static( runtime.v8_isolate(), "", r#" @@ -4806,7 +4866,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { let other_realm = runtime.create_realm().unwrap(); main_realm - .execute_script( + .execute_script_static( runtime.v8_isolate(), "", r#" @@ -4816,7 +4876,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { ) .unwrap(); other_realm - .execute_script( + .execute_script_static( runtime.v8_isolate(), "", r#" @@ -4828,7 +4888,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); main_realm - .execute_script( + .execute_script_static( runtime.v8_isolate(), "", r#" @@ -4840,7 +4900,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); other_realm - .execute_script( + .execute_script_static( runtime.v8_isolate(), "", r#" @@ -4861,7 +4921,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { // Verify that "array by copy" proposal is enabled (https://github.com/tc39/proposal-change-array-by-copy) let mut runtime = JsRuntime::new(Default::default()); assert!(runtime - .execute_script( + .execute_script_static( "test_array_by_copy.js", "const a = [1, 2, 3]; const b = a.toReversed(); @@ -4880,7 +4940,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { // Verify that "resizable ArrayBuffer" is disabled let mut runtime = JsRuntime::new(Default::default()); runtime - .execute_script( + .execute_script_static( "test_rab.js", r#"const a = new ArrayBuffer(100, {maxByteLength: 200}); if (a.byteLength !== 100) { @@ -4916,24 +4976,17 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { fn load( &self, _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, + _maybe_referrer: Option<&ModuleSpecifier>, _is_dyn_import: bool, ) -> Pin> { - let source = r#" + let code = r#" // This module doesn't really exist, just verifying that we'll get // an error when specifier starts with "ext:". import { core } from "ext:core.js"; "#; - async move { - Ok(ModuleSource { - code: source.into(), - module_url_specified: "file:///main.js".to_string(), - module_url_found: "file:///main.js".to_string(), - module_type: ModuleType::JavaScript, - }) - } - .boxed_local() + async move { Ok(ModuleSource::for_test(code, "file:///main.js")) } + .boxed_local() } } diff --git a/ext/node/lib.rs b/ext/node/lib.rs index f3bdb7e5b6..7bf721f0ae 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -447,7 +447,7 @@ pub fn initialize_runtime( argv0 ); - js_runtime.execute_script(located_script_name!(), source_code)?; + js_runtime.execute_script(located_script_name!(), source_code.into())?; Ok(()) } @@ -468,7 +468,8 @@ pub fn load_cjs_module( main = main, module = escape_for_single_quote_string(module), inspect_brk = inspect_brk, - ); + ) + .into(); js_runtime.execute_script(located_script_name!(), source_code)?; Ok(()) diff --git a/runtime/build.rs b/runtime/build.rs index 809e32a76d..eb8cc34a66 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -42,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.take_as_string()), + text_info: SourceTextInfo::from_string(code.as_str().to_owned()), media_type, capture_tokens: false, scope_analysis: false, diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 8bd5cf21e4..4be40c9b06 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -9,6 +9,7 @@ use crate::BootstrapOptions; use deno_broadcast_channel::InMemoryBroadcastChannel; use deno_cache::CreateCache; use deno_cache::SqliteBackedCache; +use deno_core::ascii_str; use deno_core::error::AnyError; use deno_core::error::JsError; use deno_core::futures::channel::mpsc; @@ -572,11 +573,13 @@ impl WebWorker { // TODO(bartlomieju): this could be done using V8 API, without calling `execute_script`. // Save a reference to function that will start polling for messages // from a worker host; it will be called after the user code is loaded. - let script = r#" + let script = ascii_str!( + r#" const pollForMessages = globalThis.pollForMessages; delete globalThis.pollForMessages; pollForMessages - "#; + "# + ); let poll_for_messages_fn = self .js_runtime .execute_script(located_script_name!(), script) @@ -585,10 +588,10 @@ impl WebWorker { } /// See [JsRuntime::execute_script](deno_core::JsRuntime::execute_script) - pub fn execute_script>( + pub fn execute_script( &mut self, name: &'static str, - source_code: S, + source_code: ModuleCode, ) -> Result<(), AnyError> { self.js_runtime.execute_script(name, source_code)?; Ok(()) @@ -777,7 +780,7 @@ pub fn run_web_worker( // Execute provided source code immediately let result = if let Some(source_code) = maybe_source_code.take() { - let r = worker.execute_script(located_script_name!(), source_code); + let r = worker.execute_script(located_script_name!(), source_code.into()); worker.start_polling_for_messages(); r } else { diff --git a/runtime/worker.rs b/runtime/worker.rs index 48bf7b09f3..ea1e5e0469 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -11,6 +11,7 @@ use std::task::Poll; use deno_broadcast_channel::InMemoryBroadcastChannel; use deno_cache::CreateCache; use deno_cache::SqliteBackedCache; +use deno_core::ascii_str; use deno_core::error::AnyError; use deno_core::error::JsError; use deno_core::futures::Future; @@ -370,10 +371,10 @@ impl MainWorker { } /// See [JsRuntime::execute_script](deno_core::JsRuntime::execute_script) - pub fn execute_script>( + pub fn execute_script( &mut self, script_name: &'static str, - source_code: S, + source_code: ModuleCode, ) -> Result, AnyError> { self.js_runtime.execute_script(script_name, source_code) } @@ -510,12 +511,12 @@ impl MainWorker { &mut self, script_name: &'static str, ) -> Result<(), AnyError> { - self.execute_script( + self.js_runtime.execute_script( script_name, // NOTE(@bartlomieju): not using `globalThis` here, because user might delete // it. Instead we're using global `dispatchEvent` function which will // used a saved reference to global scope. - "dispatchEvent(new Event('load'))", + ascii_str!("dispatchEvent(new Event('load'))"), )?; Ok(()) } @@ -527,12 +528,12 @@ impl MainWorker { &mut self, script_name: &'static str, ) -> Result<(), AnyError> { - self.execute_script( + self.js_runtime.execute_script( script_name, // NOTE(@bartlomieju): not using `globalThis` here, because user might delete // it. Instead we're using global `dispatchEvent` function which will // used a saved reference to global scope. - "dispatchEvent(new Event('unload'))", + ascii_str!("dispatchEvent(new Event('unload'))"), )?; Ok(()) } @@ -549,7 +550,9 @@ impl MainWorker { // NOTE(@bartlomieju): not using `globalThis` here, because user might delete // it. Instead we're using global `dispatchEvent` function which will // used a saved reference to global scope. - "dispatchEvent(new Event('beforeunload', { cancelable: true }));", + ascii_str!( + "dispatchEvent(new Event('beforeunload', { cancelable: true }));" + ), )?; let local_value = value.open(&mut self.js_runtime.handle_scope()); Ok(local_value.is_false())