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.
This commit is contained in:
Matt Mastracci 2023-04-04 06:46:31 -06:00 committed by GitHub
parent 2dc2016837
commit a1764f7690
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 817 additions and 641 deletions

View File

@ -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();
}

View File

@ -22,7 +22,7 @@ mod tests {
..Default::default()
});
js_runtime
.execute_script(
.execute_script_static(
"<anon>",
r#"
if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) {

View File

@ -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();

View File

@ -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

View File

@ -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()
}

View File

@ -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,
);

View File

@ -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)) {

View File

@ -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
);
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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();
}

View File

@ -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();

View File

@ -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()

View File

@ -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

View File

@ -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();
}

View File

@ -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);"#,
)

View File

@ -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()
}
}

View File

@ -62,6 +62,6 @@ fn main() {
});
runtime
.execute_script("<usage>", include_str!("wasm.js"))
.execute_script("<usage>", include_ascii_string!("wasm.js"))
.unwrap();
}

View File

@ -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());

243
core/fast_string.rs Normal file
View File

@ -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());
}
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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()
}
}

View File

@ -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(())

View File

@ -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,

View File

@ -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 {

View File

@ -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())