summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/examples/eval_js_value.rs2
-rw-r--r--core/examples/ts_module_loader.rs2
-rw-r--r--core/extensions.rs10
-rw-r--r--core/lib.rs3
-rw-r--r--core/modules.rs331
-rw-r--r--core/runtime.rs99
6 files changed, 356 insertions, 91 deletions
diff --git a/core/examples/eval_js_value.rs b/core/examples/eval_js_value.rs
index 6990abb85..e5b823a09 100644
--- a/core/examples/eval_js_value.rs
+++ b/core/examples/eval_js_value.rs
@@ -26,7 +26,7 @@ fn main() {
fn eval(
context: &mut JsRuntime,
- code: &str,
+ code: &'static str,
) -> Result<serde_json::Value, String> {
let res = context.execute_script("<anon>", code);
match res {
diff --git a/core/examples/ts_module_loader.rs b/core/examples/ts_module_loader.rs
index c7097fc91..4a38073ab 100644
--- a/core/examples/ts_module_loader.rs
+++ b/core/examples/ts_module_loader.rs
@@ -82,7 +82,7 @@ impl ModuleLoader for TypescriptModuleLoader {
code
};
let module = ModuleSource {
- code: code.into_bytes().into_boxed_slice(),
+ code: code.into(),
module_type,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
diff --git a/core/extensions.rs b/core/extensions.rs
index 2a578429b..94c4a2a79 100644
--- a/core/extensions.rs
+++ b/core/extensions.rs
@@ -1,4 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use crate::modules::ModuleCode;
use crate::OpState;
use anyhow::Context as _;
use anyhow::Error;
@@ -23,13 +24,12 @@ pub enum ExtensionFileSourceCode {
}
impl ExtensionFileSourceCode {
- pub fn load(&self) -> Result<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())
}
}
}
diff --git a/core/lib.rs b/core/lib.rs
index 7751be6e8..ba0a026bc 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -77,6 +77,7 @@ pub use crate::module_specifier::ModuleSpecifier;
pub use crate::modules::ExtModuleLoader;
pub use crate::modules::ExtModuleLoaderCb;
pub use crate::modules::FsModuleLoader;
+pub use crate::modules::ModuleCode;
pub use crate::modules::ModuleId;
pub use crate::modules::ModuleLoader;
pub use crate::modules::ModuleSource;
@@ -144,7 +145,7 @@ pub mod _ops {
#[macro_export]
macro_rules! located_script_name {
() => {
- format!("[ext:{}:{}:{}]", std::file!(), std::line!(), std::column!());
+ concat!("[ext:{}:{}:{}]", std::file!(), std::line!(), std::column!());
};
}
diff --git a/core/modules.rs b/core/modules.rs
index 2d80071aa..78efdedfd 100644
--- a/core/modules.rs
+++ b/core/modules.rs
@@ -10,6 +10,7 @@ use crate::snapshot_util::SnapshottedData;
use crate::JsRuntime;
use crate::OpState;
use anyhow::Error;
+use core::panic;
use futures::future::FutureExt;
use futures::stream::FuturesUnordered;
use futures::stream::Stream;
@@ -18,6 +19,7 @@ use futures::stream::TryStreamExt;
use log::debug;
use serde::Deserialize;
use serde::Serialize;
+use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::HashSet;
@@ -25,6 +27,7 @@ use std::collections::VecDeque;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
+use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
@@ -192,14 +195,155 @@ impl std::fmt::Display for ModuleType {
// that happened; not only first and final target. It would simplify a lot
// of things throughout the codebase otherwise we may end up requesting
// intermediate redirects from file loader.
-#[derive(Debug, Clone, Eq, PartialEq)]
+// NOTE: This should _not_ be made #[derive(Clone)] unless we take some precautions to avoid excessive string copying.
+#[derive(Debug)]
pub struct ModuleSource {
- pub code: Box<[u8]>,
+ pub code: ModuleCode,
pub module_type: ModuleType,
pub module_url_specified: String,
pub module_url_found: String,
}
+/// Module code can be sourced from strings or bytes that are either owned or borrowed. This enumeration allows us
+/// to perform a minimal amount of cloning and format-shifting of the underlying data.
+///
+/// Note that any [`ModuleCode`] created from a `'static` byte array or string must contain ASCII characters.
+///
+/// Examples of ways to construct a [`ModuleCode`] object:
+///
+/// ```rust
+/// # use deno_core::ModuleCode;
+///
+/// let code: ModuleCode = "a string".into();
+/// let code: ModuleCode = b"a string".into();
+/// ```
+#[derive(Debug)]
+pub enum ModuleCode {
+ /// Created from static data -- must be 100% 7-bit ASCII!
+ Static(&'static [u8]),
+
+ /// An owned chunk of data.
+ Owned(Vec<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());
+ }
}
diff --git a/core/runtime.rs b/core/runtime.rs
index 7b6dfba92..f9f2e5523 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -9,6 +9,7 @@ use crate::extensions::OpEventLoopFn;
use crate::inspector::JsRuntimeInspector;
use crate::module_specifier::ModuleSpecifier;
use crate::modules::ExtModuleLoaderCb;
+use crate::modules::ModuleCode;
use crate::modules::ModuleError;
use crate::modules::ModuleId;
use crate::modules::ModuleLoadId;
@@ -50,6 +51,8 @@ use std::sync::Mutex;
use std::sync::Once;
use std::task::Context;
use std::task::Poll;
+use v8::HandleScope;
+use v8::Local;
use v8::OwnedIsolate;
type PendingOpFuture = OpCall<(RealmIdx, PromiseId, OpId, OpResult)>;
@@ -748,7 +751,7 @@ impl JsRuntime {
realm.execute_script(
self.v8_isolate(),
file_source.specifier,
- &file_source.code.load()?,
+ file_source.code.load()?,
)?;
}
}
@@ -902,7 +905,7 @@ impl JsRuntime {
/// The execution takes place on the current global context, so it is possible
/// to maintain local JS state and invoke this method multiple times.
///
- /// `name` can be a filepath or any other string, eg.
+ /// `name` can be a filepath or any other string, but it is required to be 7-bit ASCII, eg.
///
/// - "/some/file/path.js"
/// - "<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,