perf(core) Reduce script name and script code copies (#18298)

Reduce the number of copies and allocations of script code by carrying
around ownership/reference information from creation time.

As an advantage, this allows us to maintain the identity of `&'static
str`-based scripts and use v8's external 1-byte strings (to avoid
incorrectly passing non-ASCII strings, debug `assert!`s gate all string
reference paths).

Benchmark results:

Perf improvements -- ~0.1 - 0.2ms faster, but should reduce garbage
w/external strings and reduces data copies overall. May also unlock some
more interesting optimizations in the future.

This requires adding some generics to functions, but manual
monomorphization has been applied (outer/inner function) to avoid code
bloat.
This commit is contained in:
Matt Mastracci 2023-03-21 16:33:12 -06:00 committed by GitHub
parent 253b556e6f
commit 0b4770fa7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 457 additions and 176 deletions

View File

@ -103,7 +103,8 @@ pub fn bench_js_async_with(
opts.benching_inner
};
let looped = loop_code(inner_iters, src);
let src = looped.as_ref();
// Get a &'static str by leaking -- this is fine because it's benchmarking code
let src = Box::leak(looped.into_boxed_str());
if is_profiling() {
for _ in 0..opts.profiling_outer {
tokio_runtime.block_on(inner_async(src, &mut runtime));
@ -115,7 +116,7 @@ pub fn bench_js_async_with(
}
}
async fn inner_async(src: &str, runtime: &mut JsRuntime) {
async fn inner_async(src: &'static str, runtime: &mut JsRuntime) {
runtime.execute_script("inner_loop", src).unwrap();
runtime.run_event_loop(false).await.unwrap();
}

View File

@ -5,6 +5,7 @@ use crate::cache::FastInsecureHasher;
use crate::cache::ParsedSourceCache;
use deno_core::error::AnyError;
use deno_core::ModuleCode;
use deno_core::ModuleSpecifier;
use deno_graph::MediaType;
use std::sync::Arc;
@ -27,11 +28,11 @@ pub fn emit_parsed_source(
source: &Arc<str>,
emit_options: &deno_ast::EmitOptions,
emit_config_hash: u64,
) -> Result<String, AnyError> {
) -> Result<ModuleCode, AnyError> {
let source_hash = get_source_hash(source, emit_config_hash);
if let Some(emit_code) = emit_cache.get_emit_code(specifier, source_hash) {
Ok(emit_code)
Ok(emit_code.into())
} else {
// this will use a cached version if it exists
let parsed_source = parsed_source_cache.get_or_parse_module(
@ -42,6 +43,6 @@ pub fn emit_parsed_source(
let transpiled_source = parsed_source.transpile(emit_options)?;
debug_assert!(transpiled_source.source_map.is_none());
emit_cache.set_emit_code(specifier, source_hash, &transpiled_source.text);
Ok(transpiled_source.text)
Ok(transpiled_source.text.into())
}
}

View File

@ -2854,7 +2854,7 @@ fn start(runtime: &mut JsRuntime, debug: bool) -> Result<(), AnyError> {
let init_config = json!({ "debug": debug });
let init_src = format!("globalThis.serverInit({init_config});");
runtime.execute_script(&located_script_name!(), &init_src)?;
runtime.execute_script(located_script_name!(), init_src)?;
Ok(())
}
@ -3442,7 +3442,7 @@ pub fn request(
};
let mark = performance.mark("request", Some(request_params.clone()));
let request_src = format!("globalThis.serverRequest({request_params});");
runtime.execute_script(&located_script_name!(), &request_src)?;
runtime.execute_script(located_script_name!(), request_src)?;
let op_state = runtime.op_state();
let mut op_state = op_state.borrow_mut();

View File

@ -14,6 +14,7 @@ use deno_core::error::AnyError;
use deno_core::futures::future::FutureExt;
use deno_core::futures::Future;
use deno_core::resolve_url;
use deno_core::ModuleCode;
use deno_core::ModuleLoader;
use deno_core::ModuleSource;
use deno_core::ModuleSpecifier;
@ -30,7 +31,7 @@ use std::rc::Rc;
use std::str;
struct ModuleCodeSource {
pub code: String,
pub code: ModuleCode,
pub found_url: ModuleSpecifier,
pub media_type: MediaType,
}
@ -91,7 +92,7 @@ impl CliModuleLoader {
specifier,
..
})) => Ok(ModuleCodeSource {
code: source.to_string(),
code: source.into(),
found_url: specifier.clone(),
media_type: *media_type,
}),
@ -101,13 +102,15 @@ impl CliModuleLoader {
specifier,
..
})) => {
let code = match media_type {
let code: ModuleCode = match media_type {
MediaType::JavaScript
| MediaType::Unknown
| MediaType::Cjs
| MediaType::Mjs
| MediaType::Json => source.to_string(),
MediaType::Dts | MediaType::Dcts | MediaType::Dmts => "".to_string(),
| MediaType::Json => source.into(),
MediaType::Dts | MediaType::Dcts | MediaType::Dmts => {
Default::default()
}
MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
@ -191,7 +194,7 @@ impl CliModuleLoader {
)?
};
ModuleCodeSource {
code,
code: code.into(),
found_url: specifier.clone(),
media_type: MediaType::from_specifier(specifier),
}
@ -208,7 +211,7 @@ impl CliModuleLoader {
code_without_source_map(code_source.code)
};
Ok(ModuleSource {
code: code.into_bytes().into_boxed_slice(),
code,
module_url_specified: specifier.to_string(),
module_url_found: code_source.found_url.to_string(),
module_type: match code_source.media_type {

View File

@ -178,7 +178,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
async move {
if let Some((source, _)) = is_data_uri {
return Ok(deno_core::ModuleSource {
code: source.into_bytes().into_boxed_slice(),
code: source.into(),
module_type: deno_core::ModuleType::JavaScript,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
@ -192,7 +192,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
.to_owned();
Ok(deno_core::ModuleSource {
code: code.into_bytes().into_boxed_slice(),
code: code.into(),
module_type: match module.kind {
eszip::ModuleKind::JavaScript => deno_core::ModuleType::JavaScript,
eszip::ModuleKind::Json => deno_core::ModuleType::Json,
@ -384,16 +384,16 @@ pub async fn run(
options,
);
worker.execute_main_module(main_module).await?;
worker.dispatch_load_event(&located_script_name!())?;
worker.dispatch_load_event(located_script_name!())?;
loop {
worker.run_event_loop(false).await?;
if !worker.dispatch_beforeunload_event(&located_script_name!())? {
if !worker.dispatch_beforeunload_event(located_script_name!())? {
break;
}
}
worker.dispatch_unload_event(&located_script_name!())?;
worker.dispatch_unload_event(located_script_name!())?;
std::process::exit(0);
}

View File

@ -20,6 +20,7 @@ use deno_core::serde_json;
use deno_core::sourcemap::SourceMap;
use deno_core::url::Url;
use deno_core::LocalInspectorSession;
use deno_core::ModuleCode;
use regex::Regex;
use std::fs;
use std::fs::File;
@ -170,16 +171,16 @@ struct CoverageReport {
fn generate_coverage_report(
script_coverage: &ScriptCoverage,
script_source: &str,
script_source: String,
maybe_source_map: &Option<Vec<u8>>,
output: &Option<PathBuf>,
) -> CoverageReport {
let maybe_source_map = maybe_source_map
.as_ref()
.map(|source_map| SourceMap::from_slice(source_map).unwrap());
let text_lines = TextLines::new(script_source);
let text_lines = TextLines::new(&script_source);
let comment_ranges = deno_ast::lex(script_source, MediaType::JavaScript)
let comment_ranges = deno_ast::lex(&script_source, MediaType::JavaScript)
.into_iter()
.filter(|item| {
matches!(item.inner, deno_ast::TokenOrComment::Comment { .. })
@ -680,14 +681,14 @@ pub async fn cover_files(
})?;
// Check if file was transpiled
let original_source = &file.source;
let transpiled_code = match file.media_type {
let original_source = file.source.clone();
let transpiled_code: ModuleCode = match file.media_type {
MediaType::JavaScript
| MediaType::Unknown
| MediaType::Cjs
| MediaType::Mjs
| MediaType::Json => file.source.as_ref().to_string(),
MediaType::Dts | MediaType::Dmts | MediaType::Dcts => "".to_string(),
| MediaType::Json => file.source.into(),
MediaType::Dts | MediaType::Dmts | MediaType::Dcts => Default::default(),
MediaType::TypeScript
| MediaType::Jsx
| MediaType::Mts
@ -695,7 +696,7 @@ pub async fn cover_files(
| MediaType::Tsx => {
let source_hash = get_source_hash(&file.source, ps.emit_options_hash);
match ps.emit_cache.get_emit_code(&file.specifier, source_hash) {
Some(code) => code,
Some(code) => code.into(),
None => {
return Err(anyhow!(
"Missing transpiled source code for: \"{}\".
@ -710,15 +711,16 @@ pub async fn cover_files(
}
};
let source_map = source_map_from_code(&transpiled_code);
let coverage_report = generate_coverage_report(
&script_coverage,
&transpiled_code,
&source_map_from_code(&transpiled_code),
transpiled_code.take_as_string(),
&source_map,
&out_mode,
);
if !coverage_report.found_lines.is_empty() {
reporter.report(&coverage_report, original_source)?;
reporter.report(&coverage_report, &original_source)?;
}
}

View File

@ -824,9 +824,9 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
});
runtime
.execute_script(&located_script_name!(), startup_source)
.execute_script(located_script_name!(), startup_source)
.context("Could not properly start the compiler runtime.")?;
runtime.execute_script(&located_script_name!(), &exec_source)?;
runtime.execute_script(located_script_name!(), exec_source)?;
let op_state = runtime.op_state();
let mut op_state = op_state.borrow_mut();

View File

@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::ModuleCode;
use encoding_rs::*;
use std::borrow::Cow;
use std::io::Error;
@ -53,11 +54,12 @@ pub fn strip_bom(text: &str) -> &str {
}
}
static SOURCE_MAP_PREFIX: &str =
"//# sourceMappingURL=data:application/json;base64,";
static SOURCE_MAP_PREFIX: &[u8] =
b"//# sourceMappingURL=data:application/json;base64,";
pub fn source_map_from_code(code: &str) -> Option<Vec<u8>> {
let last_line = code.rsplit(|u| u == '\n').next()?;
pub fn source_map_from_code(code: &ModuleCode) -> Option<Vec<u8>> {
let bytes = code.as_bytes();
let last_line = bytes.rsplit(|u| *u == b'\n').next()?;
if last_line.starts_with(SOURCE_MAP_PREFIX) {
let input = last_line.split_at(SOURCE_MAP_PREFIX.len()).1;
let decoded_map = base64::decode(input)
@ -68,17 +70,18 @@ pub fn source_map_from_code(code: &str) -> Option<Vec<u8>> {
}
}
pub fn code_without_source_map(mut code: String) -> String {
if let Some(last_line_index) = code.rfind('\n') {
if code[last_line_index + 1..].starts_with(SOURCE_MAP_PREFIX) {
code.truncate(last_line_index + 1);
code
} else {
code
/// Truncate the source code before the source map.
pub fn code_without_source_map(mut code: ModuleCode) -> ModuleCode {
let bytes = code.as_bytes();
for i in (0..bytes.len()).rev() {
if bytes[i] == b'\n' {
if bytes[i + 1..].starts_with(SOURCE_MAP_PREFIX) {
code.truncate(i + 1);
}
return code;
}
} else {
code
}
code
}
#[cfg(test)]
@ -155,8 +158,11 @@ mod tests {
"\n",
);
fn run_test(input: &str, output: &str) {
assert_eq!(code_without_source_map(input.to_string()), output);
fn run_test(input: &'static str, output: &'static str) {
assert_eq!(
code_without_source_map(input.into()).take_as_string(),
output
);
}
}
}

View File

@ -78,7 +78,7 @@ impl CliMainWorker {
self.execute_main_module_possibly_with_npm().await?;
}
self.worker.dispatch_load_event(&located_script_name!())?;
self.worker.dispatch_load_event(located_script_name!())?;
loop {
self
@ -87,13 +87,13 @@ impl CliMainWorker {
.await?;
if !self
.worker
.dispatch_beforeunload_event(&located_script_name!())?
.dispatch_beforeunload_event(located_script_name!())?
{
break;
}
}
self.worker.dispatch_unload_event(&located_script_name!())?;
self.worker.dispatch_unload_event(located_script_name!())?;
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
self
@ -129,7 +129,7 @@ impl CliMainWorker {
self
.inner
.worker
.dispatch_load_event(&located_script_name!())?;
.dispatch_load_event(located_script_name!())?;
self.pending_unload = true;
let result = loop {
@ -140,7 +140,7 @@ impl CliMainWorker {
match self
.inner
.worker
.dispatch_beforeunload_event(&located_script_name!())
.dispatch_beforeunload_event(located_script_name!())
{
Ok(default_prevented) if default_prevented => {} // continue loop
Ok(_) => break Ok(()),
@ -154,7 +154,7 @@ impl CliMainWorker {
self
.inner
.worker
.dispatch_unload_event(&located_script_name!())?;
.dispatch_unload_event(located_script_name!())?;
Ok(())
}
@ -166,7 +166,7 @@ impl CliMainWorker {
let _ = self
.inner
.worker
.dispatch_unload_event(&located_script_name!());
.dispatch_unload_event(located_script_name!());
}
}
}
@ -185,7 +185,7 @@ impl CliMainWorker {
// failures.
if self.ps.options.trace_ops() {
self.worker.js_runtime.execute_script(
&located_script_name!(),
located_script_name!(),
"Deno[Deno.internal].core.enableOpCallTracing();",
)?;
}
@ -200,19 +200,19 @@ impl CliMainWorker {
self.execute_side_module_possibly_with_npm().await?;
}
self.worker.dispatch_load_event(&located_script_name!())?;
self.worker.dispatch_load_event(located_script_name!())?;
self.run_tests(&self.ps.options.shuffle_tests()).await?;
loop {
if !self
.worker
.dispatch_beforeunload_event(&located_script_name!())?
.dispatch_beforeunload_event(located_script_name!())?
{
break;
}
self.worker.run_event_loop(false).await?;
}
self.worker.dispatch_unload_event(&located_script_name!())?;
self.worker.dispatch_unload_event(located_script_name!())?;
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
self
@ -230,7 +230,7 @@ impl CliMainWorker {
self.enable_test();
self.worker.execute_script(
&located_script_name!(),
located_script_name!(),
"Deno[Deno.internal].core.enableOpCallTracing();",
)?;
@ -239,18 +239,18 @@ impl CliMainWorker {
self.execute_side_module_possibly_with_npm().await?;
}
self.worker.dispatch_load_event(&located_script_name!())?;
self.worker.dispatch_load_event(located_script_name!())?;
self.run_tests(&None).await?;
loop {
if !self
.worker
.dispatch_beforeunload_event(&located_script_name!())?
.dispatch_beforeunload_event(located_script_name!())?
{
break;
}
self.worker.run_event_loop(false).await?;
}
self.worker.dispatch_unload_event(&located_script_name!())?;
self.worker.dispatch_unload_event(located_script_name!())?;
Ok(())
}
@ -260,18 +260,18 @@ impl CliMainWorker {
// We execute the module module as a side module so that import.meta.main is not set.
self.execute_side_module_possibly_with_npm().await?;
self.worker.dispatch_load_event(&located_script_name!())?;
self.worker.dispatch_load_event(located_script_name!())?;
self.run_benchmarks().await?;
loop {
if !self
.worker
.dispatch_beforeunload_event(&located_script_name!())?
.dispatch_beforeunload_event(located_script_name!())?
{
break;
}
self.worker.run_event_loop(false).await?;
}
self.worker.dispatch_unload_event(&located_script_name!())?;
self.worker.dispatch_unload_event(located_script_name!())?;
Ok(())
}

View File

@ -26,7 +26,7 @@ fn main() {
fn eval(
context: &mut JsRuntime,
code: &str,
code: &'static str,
) -> Result<serde_json::Value, String> {
let res = context.execute_script("<anon>", code);
match res {

View File

@ -82,7 +82,7 @@ impl ModuleLoader for TypescriptModuleLoader {
code
};
let module = ModuleSource {
code: code.into_bytes().into_boxed_slice(),
code: code.into(),
module_type,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),

View File

@ -1,4 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::modules::ModuleCode;
use crate::OpState;
use anyhow::Context as _;
use anyhow::Error;
@ -23,13 +24,12 @@ pub enum ExtensionFileSourceCode {
}
impl ExtensionFileSourceCode {
pub fn load(&self) -> Result<String, Error> {
pub fn load(&self) -> Result<ModuleCode, Error> {
match self {
ExtensionFileSourceCode::IncludedInBinary(code) => Ok(code.to_string()),
ExtensionFileSourceCode::IncludedInBinary(code) => Ok((*code).into()),
ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) => {
let msg = format!("Failed to read \"{}\"", path.display());
let code = std::fs::read_to_string(path).context(msg)?;
Ok(code)
let msg = || format!("Failed to read \"{}\"", path.display());
Ok(std::fs::read_to_string(path).with_context(msg)?.into())
}
}
}

View File

@ -77,6 +77,7 @@ pub use crate::module_specifier::ModuleSpecifier;
pub use crate::modules::ExtModuleLoader;
pub use crate::modules::ExtModuleLoaderCb;
pub use crate::modules::FsModuleLoader;
pub use crate::modules::ModuleCode;
pub use crate::modules::ModuleId;
pub use crate::modules::ModuleLoader;
pub use crate::modules::ModuleSource;
@ -144,7 +145,7 @@ pub mod _ops {
#[macro_export]
macro_rules! located_script_name {
() => {
format!("[ext:{}:{}:{}]", std::file!(), std::line!(), std::column!());
concat!("[ext:{}:{}:{}]", std::file!(), std::line!(), std::column!());
};
}

View File

@ -10,6 +10,7 @@ use crate::snapshot_util::SnapshottedData;
use crate::JsRuntime;
use crate::OpState;
use anyhow::Error;
use core::panic;
use futures::future::FutureExt;
use futures::stream::FuturesUnordered;
use futures::stream::Stream;
@ -18,6 +19,7 @@ use futures::stream::TryStreamExt;
use log::debug;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::HashSet;
@ -25,6 +27,7 @@ use std::collections::VecDeque;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
@ -192,14 +195,155 @@ impl std::fmt::Display for ModuleType {
// that happened; not only first and final target. It would simplify a lot
// of things throughout the codebase otherwise we may end up requesting
// intermediate redirects from file loader.
#[derive(Debug, Clone, Eq, PartialEq)]
// NOTE: This should _not_ be made #[derive(Clone)] unless we take some precautions to avoid excessive string copying.
#[derive(Debug)]
pub struct ModuleSource {
pub code: Box<[u8]>,
pub code: ModuleCode,
pub module_type: ModuleType,
pub module_url_specified: String,
pub module_url_found: String,
}
/// Module code can be sourced from strings or bytes that are either owned or borrowed. This enumeration allows us
/// to perform a minimal amount of cloning and format-shifting of the underlying data.
///
/// Note that any [`ModuleCode`] created from a `'static` byte array or string must contain ASCII characters.
///
/// Examples of ways to construct a [`ModuleCode`] object:
///
/// ```rust
/// # use deno_core::ModuleCode;
///
/// let code: ModuleCode = "a string".into();
/// let code: ModuleCode = b"a string".into();
/// ```
#[derive(Debug)]
pub enum ModuleCode {
/// Created from static data -- must be 100% 7-bit ASCII!
Static(&'static [u8]),
/// An owned chunk of data.
Owned(Vec<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(),
}
}
/// 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(),
}
}
}
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(),
}
}
}
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(),
}
}
}
impl From<&'static str> for ModuleCode {
#[inline(always)]
fn from(value: &'static str) -> Self {
assert!(value.is_ascii());
ModuleCode::Static(value.as_bytes())
}
}
impl From<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 {
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 {
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>>;
@ -323,7 +467,7 @@ pub(crate) fn resolve_helper(
/// Function that can be passed to the `ExtModuleLoader` that allows to
/// transpile sources before passing to V8.
pub type ExtModuleLoaderCb =
Box<dyn Fn(&ExtensionFileSource) -> Result<String, Error>>;
Box<dyn Fn(&ExtensionFileSource) -> Result<ModuleCode, Error>>;
pub struct ExtModuleLoader {
module_loader: Rc<dyn ModuleLoader>,
@ -448,7 +592,7 @@ impl ModuleLoader for ExtModuleLoader {
return async move {
let code = result?;
let source = ModuleSource {
code: code.into_bytes().into_boxed_slice(),
code,
module_type: ModuleType::JavaScript,
module_url_specified: specifier.clone(),
module_url_found: specifier.clone(),
@ -529,7 +673,7 @@ impl ModuleLoader for FsModuleLoader {
let code = std::fs::read(path)?;
let module = ModuleSource {
code: code.into_boxed_slice(),
code: code.into(),
module_type,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
@ -1002,6 +1146,32 @@ pub(crate) enum ModuleError {
Other(Error),
}
pub enum ModuleName<'a> {
Static(&'static str),
NotStatic(&'a str),
}
impl<'a> ModuleName<'a> {
pub fn as_ref(&self) -> &'a str {
match self {
ModuleName::Static(s) => s,
ModuleName::NotStatic(s) => s,
}
}
}
impl<'a, S: AsRef<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
@ -1326,16 +1496,54 @@ impl ModuleMap {
}
}
fn new_json_module(
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) => {
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: &str,
source: &[u8],
name: N,
source: &ModuleCode,
) -> Result<ModuleId, ModuleError> {
let name_str = v8::String::new(scope, name).unwrap();
// Manual monomorphization (TODO: replace w/momo)
self.new_json_module_inner(scope, name.into(), source)
}
fn new_json_module_inner(
&mut self,
scope: &mut v8::HandleScope,
name: ModuleName,
source: &ModuleCode,
) -> Result<ModuleId, ModuleError> {
let name_str = Self::string_from_module_name(scope, &name).unwrap();
let source_str = v8::String::new_from_utf8(
scope,
strip_bom(source),
strip_bom(source.as_bytes()),
v8::NewStringType::Normal,
)
.unwrap();
@ -1364,25 +1572,47 @@ impl ModuleMap {
let value_handle = v8::Global::<v8::Value>::new(tc_scope, parsed_json);
self.json_value_store.insert(handle.clone(), value_handle);
let id =
self.create_module_info(name, ModuleType::Json, handle, false, vec![]);
let id = self.create_module_info(
name.as_ref(),
ModuleType::Json,
handle,
false,
vec![],
);
Ok(id)
}
// Create and compile an ES module.
pub(crate) fn new_es_module(
/// Create and compile an ES module. Generic interface that can receive either a `&'static str` or a string with a lifetime. Prefer
/// to pass `&'static str` as this allows us to use v8 external strings.
pub(crate) fn new_es_module<'a, N: Into<ModuleName<'a>>>(
&mut self,
scope: &mut v8::HandleScope,
main: bool,
name: &str,
source: &[u8],
name: N,
source: &ModuleCode,
is_dynamic_import: bool,
) -> Result<ModuleId, ModuleError> {
let name_str = v8::String::new(scope, name).unwrap();
let source_str =
v8::String::new_from_utf8(scope, source, v8::NewStringType::Normal)
.unwrap();
// Manual monomorphization (TODO: replace w/momo)
self.new_es_module_inner(
scope,
main,
name.into(),
source,
is_dynamic_import,
)
}
fn new_es_module_inner(
&mut self,
scope: &mut v8::HandleScope,
main: bool,
name: ModuleName,
source: &ModuleCode,
is_dynamic_import: bool,
) -> Result<ModuleId, ModuleError> {
let name_str = Self::string_from_module_name(scope, &name).unwrap();
let source_str = Self::string_from_code(scope, source).unwrap();
let origin = bindings::module_origin(scope, name_str);
let source = v8::script_compiler::Source::new(source_str, Some(&origin));
@ -1432,7 +1662,7 @@ impl ModuleMap {
self.snapshot_loaded_and_not_snapshotting,
self.loader.clone(),
&import_specifier,
name,
name.as_ref(),
if is_dynamic_import {
ResolutionKind::DynamicImport
} else {
@ -1456,7 +1686,7 @@ impl ModuleMap {
if let Some(main_module) = maybe_main_module {
return Err(ModuleError::Other(generic_error(
format!("Trying to create \"main\" module ({:?}), when one already exists ({:?})",
name,
name.as_ref(),
main_module.name,
))));
}
@ -1464,7 +1694,7 @@ impl ModuleMap {
let handle = v8::Global::<v8::Module>::new(tc_scope, module);
let id = self.create_module_info(
name,
name.as_ref(),
ModuleType::JavaScript,
handle,
main,
@ -1846,7 +2076,7 @@ import "/a.js";
}
match mock_source_code(&inner.url) {
Some(src) => Poll::Ready(Ok(ModuleSource {
code: src.0.as_bytes().to_vec().into_boxed_slice(),
code: src.0.into(),
module_type: ModuleType::JavaScript,
module_url_specified: inner.url.clone(),
module_url_found: src.1.to_owned(),
@ -2043,12 +2273,13 @@ import "/a.js";
scope,
true,
&specifier_a,
br#"
&br#"
import { b } from './b.js'
if (b() != 'b') throw Error();
let control = 42;
Deno.core.ops.op_test(control);
"#,
"#
.into(),
false,
)
.unwrap();
@ -2068,7 +2299,7 @@ import "/a.js";
scope,
false,
"file:///b.js",
b"export function b() { return 'b' }",
&b"export function b() { return 'b' }".into(),
false,
)
.unwrap();
@ -2153,11 +2384,12 @@ import "/a.js";
scope,
true,
&specifier_a,
br#"
&br#"
import jsonData from './b.json' assert {type: "json"};
assert(jsonData.a == "b");
assert(jsonData.c.d == 10);
"#,
"#
.into(),
false,
)
.unwrap();
@ -2175,7 +2407,7 @@ import "/a.js";
.new_json_module(
scope,
"file:///b.json",
b"{\"a\": \"b\", \"c\": {\"d\": 10}}",
&b"{\"a\": \"b\", \"c\": {\"d\": 10}}".into(),
)
.unwrap();
let imports = module_map.get_requested_modules(mod_b).unwrap();
@ -2285,9 +2517,7 @@ import "/a.js";
let info = ModuleSource {
module_url_specified: specifier.to_string(),
module_url_found: specifier.to_string(),
code: b"export function b() { return 'b' }"
.to_vec()
.into_boxed_slice(),
code: b"export function b() { return 'b' }".into(),
module_type: ModuleType::JavaScript,
};
async move { Ok(info) }.boxed()
@ -2427,7 +2657,7 @@ import "/a.js";
let info = ModuleSource {
module_url_specified: specifier.to_string(),
module_url_found: specifier.to_string(),
code: code.as_bytes().to_vec().into_boxed_slice(),
code: code.into(),
module_type: ModuleType::JavaScript,
};
async move { Ok(info) }.boxed()
@ -2703,7 +2933,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
// The behavior should be very similar to /a.js.
let spec = resolve_url("file:///main_with_code.js").unwrap();
let main_id_fut = runtime
.load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned()))
.load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.into()))
.boxed_local();
let main_id = futures::executor::block_on(main_id_fut).unwrap();
@ -2795,17 +3025,13 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
"file:///main_module.js" => Ok(ModuleSource {
module_url_specified: "file:///main_module.js".to_string(),
module_url_found: "file:///main_module.js".to_string(),
code: b"if (!import.meta.main) throw Error();"
.to_vec()
.into_boxed_slice(),
code: b"if (!import.meta.main) throw Error();".into(),
module_type: ModuleType::JavaScript,
}),
"file:///side_module.js" => Ok(ModuleSource {
module_url_specified: "file:///side_module.js".to_string(),
module_url_found: "file:///side_module.js".to_string(),
code: b"if (import.meta.main) throw Error();"
.to_vec()
.into_boxed_slice(),
code: b"if (import.meta.main) throw Error();".into(),
module_type: ModuleType::JavaScript,
}),
_ => unreachable!(),
@ -2866,7 +3092,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
// The behavior should be very similar to /a.js.
let spec = resolve_url("file:///main_with_code.js").unwrap();
let main_id_fut = runtime
.load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned()))
.load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.into()))
.boxed_local();
let main_id = futures::executor::block_on(main_id_fut).unwrap();
@ -2906,7 +3132,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
// The behavior should be very similar to /a.js.
let spec = resolve_url("file:///main_with_code.js").unwrap();
let main_id_fut = runtime
.load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned()))
.load_main_module(&spec, Some(MAIN_WITH_CODE_SRC.into()))
.boxed_local();
let main_id = futures::executor::block_on(main_id_fut).unwrap();
@ -2976,4 +3202,23 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
Some("Cannot load extension module from external code".to_string())
);
}
#[test]
fn code_truncate() {
let mut s = "123456".to_owned();
s.truncate(3);
let mut code: ModuleCode = "123456".into();
code.truncate(3);
assert_eq!(s, code.take_as_string());
let mut code: ModuleCode = "123456".to_owned().into();
code.truncate(3);
assert_eq!(s, code.take_as_string());
let arc_str: Arc<str> = "123456".into();
let mut code: ModuleCode = arc_str.into();
code.truncate(3);
assert_eq!(s, code.take_as_string());
}
}

View File

@ -9,6 +9,7 @@ use crate::extensions::OpEventLoopFn;
use crate::inspector::JsRuntimeInspector;
use crate::module_specifier::ModuleSpecifier;
use crate::modules::ExtModuleLoaderCb;
use crate::modules::ModuleCode;
use crate::modules::ModuleError;
use crate::modules::ModuleId;
use crate::modules::ModuleLoadId;
@ -50,6 +51,8 @@ use std::sync::Mutex;
use std::sync::Once;
use std::task::Context;
use std::task::Poll;
use v8::HandleScope;
use v8::Local;
use v8::OwnedIsolate;
type PendingOpFuture = OpCall<(RealmIdx, PromiseId, OpId, OpResult)>;
@ -748,7 +751,7 @@ impl JsRuntime {
realm.execute_script(
self.v8_isolate(),
file_source.specifier,
&file_source.code.load()?,
file_source.code.load()?,
)?;
}
}
@ -902,7 +905,7 @@ impl JsRuntime {
/// The execution takes place on the current global context, so it is possible
/// to maintain local JS state and invoke this method multiple times.
///
/// `name` can be a filepath or any other string, eg.
/// `name` can be a filepath or any other string, but it is required to be 7-bit ASCII, eg.
///
/// - "/some/file/path.js"
/// - "<anon>"
@ -911,10 +914,10 @@ impl JsRuntime {
/// The same `name` value can be used for multiple executions.
///
/// `Error` can usually be downcast to `JsError`.
pub fn execute_script(
pub fn execute_script<S: Into<ModuleCode>>(
&mut self,
name: &str,
source_code: &str,
name: &'static str,
source_code: S,
) -> Result<v8::Global<v8::Value>, Error> {
self
.global_realm()
@ -2056,21 +2059,15 @@ impl JsRuntime {
pub async fn load_main_module(
&mut self,
specifier: &ModuleSpecifier,
code: Option<String>,
code: Option<ModuleCode>,
) -> Result<ModuleId, Error> {
let module_map_rc = Self::module_map(self.v8_isolate());
if let Some(code) = code {
let scope = &mut self.handle_scope();
// true for main module
module_map_rc
.borrow_mut()
.new_es_module(
scope,
// main module
true,
specifier.as_str(),
code.as_bytes(),
false,
)
.new_es_module(scope, true, specifier, &code, false)
.map_err(|e| match e {
ModuleError::Exception(exception) => {
let exception = v8::Local::new(scope, exception);
@ -2116,21 +2113,15 @@ impl JsRuntime {
pub async fn load_side_module(
&mut self,
specifier: &ModuleSpecifier,
code: Option<String>,
code: Option<ModuleCode>,
) -> Result<ModuleId, Error> {
let module_map_rc = Self::module_map(self.v8_isolate());
if let Some(code) = code {
let scope = &mut self.handle_scope();
// false for side module (not main module)
module_map_rc
.borrow_mut()
.new_es_module(
scope,
// not main module
false,
specifier.as_str(),
code.as_bytes(),
false,
)
.new_es_module(scope, false, specifier, &code, false)
.map_err(|e| match e {
ModuleError::Exception(exception) => {
let exception = v8::Local::new(scope, exception);
@ -2476,6 +2467,21 @@ impl JsRealm {
self.0.open(scope).global(scope)
}
fn string_from_code<'a>(
scope: &mut HandleScope<'a>,
code: &ModuleCode,
) -> Option<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,
)
}
}
/// Executes traditional JavaScript code (traditional = not ES modules) in the
/// realm's context.
///
@ -2488,16 +2494,28 @@ impl JsRealm {
/// The same `name` value can be used for multiple executions.
///
/// `Error` can usually be downcast to `JsError`.
pub fn execute_script(
pub fn execute_script<S: Into<ModuleCode>>(
&self,
isolate: &mut v8::Isolate,
name: &str,
source_code: &str,
name: &'static str,
source_code: S,
) -> Result<v8::Global<v8::Value>, Error> {
// Manual monomorphization (TODO: replace w/momo)
self.execute_script_inner(isolate, name, source_code.into())
}
fn execute_script_inner(
&self,
isolate: &mut v8::Isolate,
name: &'static str,
source_code: ModuleCode,
) -> Result<v8::Global<v8::Value>, Error> {
let scope = &mut self.handle_scope(isolate);
let source = v8::String::new(scope, source_code).unwrap();
let name = v8::String::new(scope, name).unwrap();
let source = Self::string_from_code(scope, &source_code).unwrap();
assert!(name.is_ascii());
let name =
v8::String::new_external_onebyte_static(scope, name.as_bytes()).unwrap();
let origin = bindings::script_origin(scope, name);
let tc_scope = &mut v8::TryCatch::new(scope);
@ -3104,7 +3122,8 @@ pub mod tests {
runtime
.execute_script(
"encode_decode_test.js",
include_str!("encode_decode_test.js"),
// Note: We make this to_owned because it contains non-ASCII chars
include_str!("encode_decode_test.js").to_owned(),
)
.unwrap();
if let Poll::Ready(Err(_)) = runtime.poll_event_loop(cx, false) {
@ -3320,7 +3339,7 @@ pub mod tests {
export const a = "b";
export default 1 + 2;
"#
.to_string();
.into();
let module_id = futures::executor::block_on(
runtime.load_main_module(&specifier, Some(source_code)),
@ -3490,7 +3509,8 @@ pub mod tests {
import {{ f{prev} }} from "file:///{prev}.js";
export function f{i}() {{ return f{prev}() }}
"#
);
)
.into();
let id = if main {
futures::executor::block_on(
@ -3559,8 +3579,7 @@ pub mod tests {
});
let specifier = crate::resolve_url("file:///0.js").unwrap();
let source_code =
r#"export function f0() { return "hello world" }"#.to_string();
let source_code = r#"export function f0() { return "hello world" }"#.into();
let id = futures::executor::block_on(
runtime.load_side_module(&specifier, Some(source_code)),
)
@ -3620,7 +3639,7 @@ pub mod tests {
return mod.f400() + " " + Deno.core.ops.op_test();
})();"#
.to_string();
let val = runtime3.execute_script(".", &source_code).unwrap();
let val = runtime3.execute_script(".", source_code).unwrap();
let val = futures::executor::block_on(runtime3.resolve_value(val)).unwrap();
{
let scope = &mut runtime3.handle_scope();
@ -4163,7 +4182,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
) -> Pin<Box<ModuleSourceFuture>> {
async move {
Ok(ModuleSource {
code: b"console.log('hello world');".to_vec().into_boxed_slice(),
code: b"console.log('hello world');".into(),
module_url_specified: "file:///main.js".to_string(),
module_url_found: "file:///main.js".to_string(),
module_type: ModuleType::JavaScript,
@ -4180,7 +4199,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
});
let specifier = crate::resolve_url("file:///main.js").unwrap();
let source_code = "Deno.core.print('hello\\n')".to_string();
let source_code = "Deno.core.print('hello\\n')".into();
let module_id = futures::executor::block_on(
runtime.load_main_module(&specifier, Some(source_code)),
@ -4273,7 +4292,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
.execute_script(
runtime.v8_isolate(),
"",
&format!(
format!(
r#"
globalThis.rejectValue = undefined;
@ -4282,7 +4301,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
}});
Deno.core.opAsync("op_void_async").then(() => Promise.reject({number}));
"#
),
)
)
.unwrap();
}
@ -4344,7 +4363,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
async move {
Ok(ModuleSource {
code: source.as_bytes().to_vec().into_boxed_slice(),
code: source.into(),
module_url_specified: "file:///main.js".to_string(),
module_url_found: "file:///main.js".to_string(),
module_type: ModuleType::JavaScript,
@ -4858,7 +4877,7 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
async move {
Ok(ModuleSource {
code: source.as_bytes().to_vec().into_boxed_slice(),
code: source.into(),
module_url_specified: "file:///main.js".to_string(),
module_url_found: "file:///main.js".to_string(),
module_type: ModuleType::JavaScript,

View File

@ -390,7 +390,7 @@ pub fn initialize_runtime(
} else {
"undefined".to_string()
};
let source_code = &format!(
let source_code = format!(
r#"(function loadBuiltinNodeModules(nodeGlobalThisName, usesLocalNodeModulesDir, argv0) {{
Deno[Deno.internal].node.initialize(
nodeGlobalThisName,
@ -403,7 +403,7 @@ pub fn initialize_runtime(
argv0
);
js_runtime.execute_script(&located_script_name!(), source_code)?;
js_runtime.execute_script(located_script_name!(), source_code)?;
Ok(())
}
@ -417,7 +417,7 @@ pub fn load_cjs_module(
text.replace('\\', r"\\").replace('\'', r"\'")
}
let source_code = &format!(
let source_code = format!(
r#"(function loadCjsModule(moduleName, isMain, inspectBrk) {{
Deno[Deno.internal].node.loadCjsModule(moduleName, isMain, inspectBrk);
}})('{module}', {main}, {inspect_brk});"#,
@ -426,6 +426,6 @@ pub fn load_cjs_module(
inspect_brk = inspect_brk,
);
js_runtime.execute_script(&located_script_name!(), source_code)?;
js_runtime.execute_script(located_script_name!(), source_code)?;
Ok(())
}

View File

@ -17,11 +17,12 @@ mod startup_snapshot {
use deno_core::snapshot_util::*;
use deno_core::Extension;
use deno_core::ExtensionFileSource;
use deno_core::ModuleCode;
use std::path::Path;
fn transpile_ts_for_snapshotting(
file_source: &ExtensionFileSource,
) -> Result<String, AnyError> {
) -> Result<ModuleCode, AnyError> {
let media_type = MediaType::from_path(Path::new(&file_source.specifier));
let should_transpile = match media_type {
@ -41,7 +42,7 @@ mod startup_snapshot {
let parsed = deno_ast::parse_module(ParseParams {
specifier: file_source.specifier.to_string(),
text_info: SourceTextInfo::from_string(code),
text_info: SourceTextInfo::from_string(code.take_as_string()),
media_type,
capture_tokens: false,
scope_analysis: false,
@ -53,7 +54,7 @@ mod startup_snapshot {
..Default::default()
})?;
Ok(transpiled_source.text)
Ok(transpiled_source.text.into())
}
#[derive(Clone)]

View File

@ -25,6 +25,7 @@ use deno_core::CompiledWasmModuleStore;
use deno_core::Extension;
use deno_core::GetErrorClassFn;
use deno_core::JsRuntime;
use deno_core::ModuleCode;
use deno_core::ModuleId;
use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier;
@ -575,16 +576,16 @@ impl WebWorker {
"#;
let poll_for_messages_fn = self
.js_runtime
.execute_script(&located_script_name!(), script)
.execute_script(located_script_name!(), script)
.expect("Failed to execute worker bootstrap script");
self.poll_for_messages_fn = Some(poll_for_messages_fn);
}
/// See [JsRuntime::execute_script](deno_core::JsRuntime::execute_script)
pub fn execute_script(
pub fn execute_script<S: Into<ModuleCode>>(
&mut self,
name: &str,
source_code: &str,
name: &'static str,
source_code: S,
) -> Result<(), AnyError> {
self.js_runtime.execute_script(name, source_code)?;
Ok(())
@ -744,7 +745,7 @@ fn print_worker_error(
pub fn run_web_worker(
worker: WebWorker,
specifier: ModuleSpecifier,
maybe_source_code: Option<String>,
mut maybe_source_code: Option<String>,
preload_module_cb: Arc<ops::worker_host::WorkerEventCb>,
pre_execute_module_cb: Arc<ops::worker_host::WorkerEventCb>,
format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
@ -772,8 +773,8 @@ pub fn run_web_worker(
};
// Execute provided source code immediately
let result = if let Some(source_code) = maybe_source_code {
let r = worker.execute_script(&located_script_name!(), &source_code);
let result = if let Some(source_code) = maybe_source_code.take() {
let r = worker.execute_script(located_script_name!(), source_code);
worker.start_polling_for_messages();
r
} else {

View File

@ -21,6 +21,7 @@ use deno_core::FsModuleLoader;
use deno_core::GetErrorClassFn;
use deno_core::JsRuntime;
use deno_core::LocalInspectorSession;
use deno_core::ModuleCode;
use deno_core::ModuleId;
use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier;
@ -364,10 +365,10 @@ impl MainWorker {
}
/// See [JsRuntime::execute_script](deno_core::JsRuntime::execute_script)
pub fn execute_script(
pub fn execute_script<S: Into<ModuleCode>>(
&mut self,
script_name: &str,
source_code: &str,
script_name: &'static str,
source_code: S,
) -> Result<v8::Global<v8::Value>, AnyError> {
self.js_runtime.execute_script(script_name, source_code)
}
@ -502,7 +503,7 @@ impl MainWorker {
/// Does not poll event loop, and thus not await any of the "load" event handlers.
pub fn dispatch_load_event(
&mut self,
script_name: &str,
script_name: &'static str,
) -> Result<(), AnyError> {
self.execute_script(
script_name,
@ -519,7 +520,7 @@ impl MainWorker {
/// Does not poll event loop, and thus not await any of the "unload" event handlers.
pub fn dispatch_unload_event(
&mut self,
script_name: &str,
script_name: &'static str,
) -> Result<(), AnyError> {
self.execute_script(
script_name,
@ -536,7 +537,7 @@ impl MainWorker {
/// running.
pub fn dispatch_beforeunload_event(
&mut self,
script_name: &str,
script_name: &'static str,
) -> Result<bool, AnyError> {
let value = self.js_runtime.execute_script(
script_name,