summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2023-04-04 06:46:31 -0600
committerGitHub <noreply@github.com>2023-04-04 06:46:31 -0600
commita1764f7690cfdc3e42724fcad29ef954b7e576a4 (patch)
tree1b621ebd7a6ef50687eeb2061740895096136e8a
parent2dc20168371e827b86e2ce0d1d7787139fba68f3 (diff)
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.
-rw-r--r--bench_util/js_runtime.rs2
-rw-r--r--cli/js.rs2
-rw-r--r--cli/lsp/tsc.rs4
-rw-r--r--cli/module_loader.rs22
-rw-r--r--cli/standalone.rs35
-rw-r--r--cli/tools/coverage/mod.rs4
-rw-r--r--cli/tsc/mod.rs12
-rw-r--r--cli/util/text_encoding.rs4
-rw-r--r--cli/worker.rs5
-rw-r--r--core/bindings.rs6
-rw-r--r--core/examples/disable_ops.rs2
-rw-r--r--core/examples/eval_js_value.rs2
-rw-r--r--core/examples/hello_world.rs2
-rw-r--r--core/examples/http_bench_json_ops/main.rs2
-rw-r--r--core/examples/panik.rs2
-rw-r--r--core/examples/schedule_task.rs2
-rw-r--r--core/examples/ts_module_loader.rs21
-rw-r--r--core/examples/wasm.rs2
-rw-r--r--core/extensions.rs2
-rw-r--r--core/fast_string.rs243
-rw-r--r--core/lib.rs2
-rw-r--r--core/modules.rs716
-rw-r--r--core/runtime.rs327
-rw-r--r--ext/node/lib.rs5
-rw-r--r--runtime/build.rs2
-rw-r--r--runtime/web_worker.rs13
-rw-r--r--runtime/worker.rs17
27 files changed, 817 insertions, 641 deletions
diff --git a/bench_util/js_runtime.rs b/bench_util/js_runtime.rs
index 4a5123a73..57085ef96 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 fac771fd5..e3a5b94be 100644
--- a/cli/js.rs
+++ b/cli/js.rs
@@ -22,7 +22,7 @@ mod tests {
..Default::default()
});
js_runtime
- .execute_script(
+ .execute_script_static(
"<anon>",
r#"
if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) {
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index ef5d0e645..e236eee0a 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 7f6101d80..b7df15e31 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<ModuleSpecifier>,
+ maybe_referrer: Option<&ModuleSpecifier>,
) -> Result<ModuleCodeSource, AnyError> {
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<ModuleSpecifier>,
+ maybe_referrer: Option<&ModuleSpecifier>,
is_dynamic: bool,
) -> Result<ModuleSource, AnyError> {
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<ModuleSpecifier>,
+ maybe_referrer: Option<&ModuleSpecifier>,
is_dynamic: bool,
) -> Pin<Box<deno_core::ModuleSourceFuture>> {
// NOTE: this block is async only because of `deno_core` interface
diff --git a/cli/standalone.rs b/cli/standalone.rs
index 527e8d975..08caacda6 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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dynamic: bool,
) -> Pin<Box<deno_core::ModuleSourceFuture>> {
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 2346b3614..7f2598811 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 a9dc5b7f3..3bd8efefa 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<Vec<AssetText>, 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::<Vec<AssetText>>(scope, local)?)
@@ -792,15 +793,14 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
},
);
- 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(
"<anon>",
r#"
if (!(startup)) {
diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs
index 0111ec82f..29a8d4069 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 5beef84ff..edd604519 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 00d0cf2e6..8e701c903 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 b9a5e7fca..c75af1c3f 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("<usage>", r#"Deno.core.print("I'm broken")"#)
+ .execute_script_static("<usage>", 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 e5b823a09..7b7af7c96 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<serde_json::Value, String> {
- let res = context.execute_script("<anon>", code);
+ let res = context.execute_script_static("<anon>", 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 50cbe4e2c..cce6e2218 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(
"<usage>",
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 f0bbec0d9..7c15f7bf2 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 1d2286a88..54b46d337 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 42d00022d..348ba7666 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(
"<usage>",
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 4a38073ab..6adb27977 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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
- let module_specifier = module_specifier.clone();
- async move {
+ fn load(
+ module_specifier: &ModuleSpecifier,
+ ) -> Result<ModuleSource, AnyError> {
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 7c2d98bc9..5d5c5f6ff 100644
--- a/core/examples/wasm.rs
+++ b/core/examples/wasm.rs
@@ -62,6 +62,6 @@ fn main() {
});
runtime
- .execute_script("<usage>", include_str!("wasm.js"))
+ .execute_script("<usage>", include_ascii_string!("wasm.js"))
.unwrap();
}
diff --git a/core/extensions.rs b/core/extensions.rs
index ca618c9b7..4a7b49414 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 000000000..95dfb4939
--- /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<str>),
+
+ // Scripts loaded from the `deno_graph` infrastructure.
+ Arc(Arc<str>),
+}
+
+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<str> = 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<str>, 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<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.as_str().hash(state)
+ }
+}
+
+impl AsRef<str> for FastString {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl Borrow<str> 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<Url> 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<String> for FastString {
+ fn from(value: String) -> Self {
+ FastString::Owned(value.into_boxed_str())
+ }
+}
+
+/// [`FastString`] can be made cheaply from [`Arc<str>`] as we know it's shared and don't need to do an
+/// ASCII check.
+impl From<Arc<str>> for FastString {
+ fn from(value: Arc<str>) -> 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<str> = "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 e8ca36559..e6088304e 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 cfd68d245..c63c4dd30 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<ModuleName>,
}
-/// 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<u8>),
-
- /// Scripts loaded from the `deno_graph` infrastructure.
- Arc(Arc<str>),
-}
-
-impl ModuleCode {
- #[inline(always)]
- pub fn as_bytes(&self) -> &[u8] {
- match self {
- Self::Static(b) => b,
- Self::Owned(b) => b,
- Self::Arc(s) => s.as_bytes(),
- }
- }
-
- pub fn try_static_ascii(&self) -> Option<&'static [u8]> {
- match self {
- Self::Static(b) => Some(b),
- _ => None,
- }
- }
-
- /// Takes a [`ModuleCode`] value as an owned [`String`]. May be slow.
- pub fn take_as_string(self) -> String {
- match self {
- Self::Static(b) => String::from_utf8(b.to_vec()).unwrap(),
- Self::Owned(b) => String::from_utf8(b).unwrap(),
- Self::Arc(s) => (*s).to_owned(),
+impl ModuleSource {
+ /// Create a [`ModuleSource`] without a redirect.
+ pub fn new(
+ module_type: impl Into<ModuleType>,
+ 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,
}
}
- /// 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<str>, so we'll just take ownership of the truncated version
- Self::Arc(s) => *self = s[..index].to_owned().into(),
+ /// 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<ModuleType>,
+ 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,
}
}
-}
-impl Default for ModuleCode {
- fn default() -> Self {
- ModuleCode::Static(&[])
- }
-}
-
-impl From<Arc<str>> for ModuleCode {
- #[inline(always)]
- fn from(value: Arc<str>) -> Self {
- Self::Arc(value)
- }
-}
-
-impl From<&Arc<str>> for ModuleCode {
- #[inline(always)]
- fn from(value: &Arc<str>) -> Self {
- Self::Arc(value.clone())
- }
-}
-
-impl From<Cow<'static, str>> for ModuleCode {
- #[inline(always)]
- fn from(value: Cow<'static, str>) -> Self {
- match value {
- Cow::Borrowed(b) => b.into(),
- Cow::Owned(b) => b.into(),
+ #[cfg(test)]
+ pub fn for_test(code: &'static str, file: impl AsRef<str>) -> Self {
+ Self {
+ code: ModuleCode::from_static(code),
+ module_type: ModuleType::JavaScript,
+ module_url_specified: file.as_ref().to_owned().into(),
+ module_url_found: None,
}
}
-}
-impl From<Cow<'static, [u8]>> for ModuleCode {
- #[inline(always)]
- fn from(value: Cow<'static, [u8]>) -> Self {
- match value {
- Cow::Borrowed(b) => b.into(),
- Cow::Owned(b) => b.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<str>,
+ found: impl AsRef<str>,
+ ) -> 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 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<String> for ModuleCode {
- #[inline(always)]
- fn from(value: String) -> Self {
- value.into_bytes().into()
- }
-}
-
-impl From<Vec<u8>> for ModuleCode {
- #[inline(always)]
- fn from(value: Vec<u8>) -> 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<const N: usize> 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<Output = (ModuleLoadId, Result<RecursiveModuleLoad, Error>)>;
pub type ModuleSourceFuture = dyn Future<Output = Result<ModuleSource, Error>>;
@@ -391,7 +325,7 @@ pub trait ModuleLoader {
fn load(
&self,
module_specifier: &ModuleSpecifier,
- maybe_referrer: Option<ModuleSpecifier>,
+ maybe_referrer: Option<&ModuleSpecifier>,
is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>>;
@@ -433,7 +367,7 @@ impl ModuleLoader for NoopModuleLoader {
fn load(
&self,
module_specifier: &ModuleSpecifier,
- maybe_referrer: Option<ModuleSpecifier>,
+ maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
let err = generic_error(
@@ -556,7 +490,7 @@ impl ModuleLoader for ExtModuleLoader {
fn load(
&self,
module_specifier: &ModuleSpecifier,
- maybe_referrer: Option<ModuleSpecifier>,
+ maybe_referrer: Option<&ModuleSpecifier>,
is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dynamic: bool,
) -> Pin<Box<ModuleSourceFuture>> {
- let module_specifier = module_specifier.clone();
- async move {
+ fn load(
+ module_specifier: &ModuleSpecifier,
+ ) -> Result<ModuleSource, AnyError> {
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<ModuleRequest>,
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<str>> 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<v8::Global<v8::Module>>,
pub info: Vec<ModuleInfo>,
- pub(crate) by_name: HashMap<(String, AssertedModuleType), SymbolicModule>,
+ pub(crate) by_name_js: HashMap<ModuleName, SymbolicModule>,
+ pub(crate) by_name_json: HashMap<ModuleName, SymbolicModule>,
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<v8::Value> = match &elem.1 {
+ let symbolic_module: v8::Local<v8::Value> = 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<v8::Array> = 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<v8::Array> =
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<v8::Array> = 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<str>,
asserted_module_type: AssertedModuleType,
) -> Option<ModuleId> {
- 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<v8::Local<'a, v8::String>> {
- 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<v8::Local<'a, v8::String>> {
- 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<ModuleName<'a>>>(
- &mut self,
- scope: &mut v8::HandleScope,
- name: N,
- source: &ModuleCode,
- ) -> Result<ModuleId, ModuleError> {
- // 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<ModuleId, ModuleError> {
- 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::<v8::Value>::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<ModuleName<'a>>>(
- &mut self,
- scope: &mut v8::HandleScope,
- main: bool,
- name: N,
- source: &ModuleCode,
- is_dynamic_import: bool,
- ) -> Result<ModuleId, ModuleError> {
- // 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<ModuleId, ModuleError> {
- 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::<v8::Module>::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<v8::Module>,
main: bool,
requests: Vec<ModuleRequest>,
) -> 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<str>,
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<ModuleName, SymbolicModule> {
+ 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<ModuleName, SymbolicModule> {
+ 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<RefCell<ModuleMap>>,
- specifier: &str,
+ specifier: impl AsRef<str>,
) -> Result<RecursiveModuleLoad, Error> {
- 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<RefCell<ModuleMap>>,
- specifier: &str,
+ specifier: impl AsRef<str>,
) -> Result<RecursiveModuleLoad, Error> {
- 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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
let mut loads = self.loads.lock();
@@ -2220,7 +2110,7 @@ import "/a.js";
fn load(
&self,
_module_specifier: &ModuleSpecifier,
- _maybe_referrer: Option<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<str> = "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 d68cb3616..89487bc6c 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<S: Into<ModuleCode>>(
+ pub fn execute_script(
&mut self,
name: &'static str,
- source_code: S,
+ source_code: ModuleCode,
) -> Result<v8::Global<v8::Value>, 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"
+ /// - "<anon>"
+ /// - "[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<v8::Global<v8::Value>, 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<ModuleId, Error> {
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<ModuleId, Error> {
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(), "<anon>", source_code)
+/// .execute_script_static(runtime.v8_isolate(), "<anon>", 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<S: Into<ModuleCode>>(
+ pub fn execute_script_static(
&self,
isolate: &mut v8::Isolate,
name: &'static str,
- source_code: S,
+ source_code: &'static str,
) -> Result<v8::Global<v8::Value>, 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"
+ /// - "<anon>"
+ /// - "[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::<JsError>().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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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("<none>", "").unwrap();
+ runtime.execute_script_static("<none>", "").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<v8::Value> = 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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
- 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<ModuleSpecifier>,
+ _maybe_referrer: Option<&ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
- 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 f3bdb7e5b..7bf721f0a 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 809e32a76..eb8cc34a6 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 8bd5cf21e..4be40c9b0 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<S: Into<ModuleCode>>(
+ 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 48bf7b09f..ea1e5e046 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<S: Into<ModuleCode>>(
+ pub fn execute_script(
&mut self,
script_name: &'static str,
- source_code: S,
+ source_code: ModuleCode,
) -> Result<v8::Global<v8::Value>, 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())