summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-08-26 11:13:39 -0400
committerGitHub <noreply@github.com>2024-08-26 11:13:39 -0400
commitd8dfe6dc97942a2e67c85cb799c08d0c95ed8aee (patch)
treec953729c352e034a2f5f79e2759f756e17edb3c3
parente53678fd5841fe7b94f8f9e16d6521201a3d2993 (diff)
perf(ext/node): reduce some allocations in require (#25197)
-rw-r--r--ext/node/ops/require.rs93
-rw-r--r--ext/node_resolver/analyze.rs12
2 files changed, 52 insertions, 53 deletions
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index d074234c3..def1d9758 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -10,6 +10,7 @@ use deno_core::JsRuntimeInspector;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_fs::FileSystemRc;
+use deno_package_json::PackageJsonRc;
use node_resolver::NodeModuleKind;
use node_resolver::NodeResolutionMode;
use node_resolver::REQUIRE_CONDITIONS;
@@ -22,7 +23,6 @@ use crate::NodePermissions;
use crate::NodeRequireResolverRc;
use crate::NodeResolverRc;
use crate::NpmResolverRc;
-use crate::PackageJson;
fn ensure_read_permission<P>(
state: &mut OpState,
@@ -70,7 +70,7 @@ pub fn op_require_init_paths() -> Vec<String> {
// let mut paths = paths
// .into_iter()
- // .map(|p| p.to_string_lossy().to_string())
+ // .map(|p| p.to_string_lossy().into_owned())
// .collect();
// if !node_path.is_empty() {
@@ -98,15 +98,13 @@ where
{
let fs = state.borrow::<FileSystemRc>();
// Guarantee that "from" is absolute.
- let from_url = if from.starts_with("file:///") {
- Url::parse(&from)?
+ let from = if from.starts_with("file:///") {
+ url_to_file_path(&Url::parse(&from)?)?
} else {
- deno_core::resolve_path(
- &from,
- &(fs.cwd().map_err(AnyError::from)).context("Unable to get CWD")?,
- )?
+ let current_dir =
+ &(fs.cwd().map_err(AnyError::from)).context("Unable to get CWD")?;
+ deno_core::normalize_path(current_dir.join(from))
};
- let from = url_to_file_path(&from_url)?;
ensure_read_permission::<P>(state, &from)?;
@@ -117,7 +115,7 @@ where
let bytes = from_str.as_bytes();
if bytes[from_str.len() - 1] == b'\\' && bytes[from_str.len() - 2] == b':'
{
- let p = from_str.to_owned() + "node_modules";
+ let p = format!("{}node_modules", from_str);
return Ok(vec![p]);
}
}
@@ -129,12 +127,12 @@ where
}
}
- let mut paths = vec![];
+ let mut paths = Vec::with_capacity(from.components().count());
let mut current_path = from.as_path();
let mut maybe_parent = Some(current_path);
while let Some(parent) = maybe_parent {
if !parent.ends_with("node_modules") {
- paths.push(parent.join("node_modules").to_string_lossy().to_string());
+ paths.push(parent.join("node_modules").to_string_lossy().into_owned());
}
current_path = parent;
maybe_parent = current_path.parent();
@@ -158,7 +156,7 @@ pub fn op_require_proxy_path(#[string] filename: String) -> String {
if trailing_slash {
let p = PathBuf::from(filename);
- p.join("noop.js").to_string_lossy().to_string()
+ p.join("noop.js").to_string_lossy().into_owned()
} else {
filename
}
@@ -200,7 +198,7 @@ pub fn op_require_resolve_deno_dir(
}),
)
.ok()
- .map(|p| p.to_string_lossy().to_string())
+ .map(|p| p.to_string_lossy().into_owned())
}
#[op2(fast)]
@@ -251,7 +249,7 @@ pub fn op_require_resolve_lookup_paths(
// }
let p = PathBuf::from(parent_filename);
- Some(vec![p.parent().unwrap().to_string_lossy().to_string()])
+ Some(vec![p.parent().unwrap().to_string_lossy().into_owned()])
}
#[op2(fast)]
@@ -295,24 +293,23 @@ where
let fs = state.borrow::<FileSystemRc>();
let canonicalized_path =
deno_core::strip_unc_prefix(fs.realpath_sync(&path)?);
- Ok(canonicalized_path.to_string_lossy().to_string())
+ Ok(canonicalized_path.to_string_lossy().into_owned())
}
-fn path_resolve(parts: Vec<String>) -> String {
- assert!(!parts.is_empty());
- let mut p = PathBuf::from(&parts[0]);
- if parts.len() > 1 {
- for part in &parts[1..] {
- p = p.join(part);
- }
+fn path_resolve<'a>(mut parts: impl Iterator<Item = &'a str>) -> PathBuf {
+ let mut p = PathBuf::from(parts.next().unwrap());
+ for part in parts {
+ p = p.join(part);
}
- normalize_path(p).to_string_lossy().to_string()
+ normalize_path(p)
}
#[op2]
#[string]
pub fn op_require_path_resolve(#[serde] parts: Vec<String>) -> String {
- path_resolve(parts)
+ path_resolve(parts.iter().map(|s| s.as_str()))
+ .to_string_lossy()
+ .into_owned()
}
#[op2]
@@ -322,7 +319,7 @@ pub fn op_require_path_dirname(
) -> Result<String, AnyError> {
let p = PathBuf::from(request);
if let Some(parent) = p.parent() {
- Ok(parent.to_string_lossy().to_string())
+ Ok(parent.to_string_lossy().into_owned())
} else {
Err(generic_error("Path doesn't have a parent"))
}
@@ -335,7 +332,7 @@ pub fn op_require_path_basename(
) -> Result<String, AnyError> {
let p = PathBuf::from(request);
if let Some(path) = p.file_name() {
- Ok(path.to_string_lossy().to_string())
+ Ok(path.to_string_lossy().into_owned())
} else {
Err(generic_error("Path doesn't have a file name"))
}
@@ -365,7 +362,7 @@ where
let fs = state.borrow::<FileSystemRc>();
if let Ok(cwd) = fs.cwd() {
ensure_read_permission::<P>(state, &cwd)?;
- return Ok(Some(cwd.to_string_lossy().to_string()));
+ return Ok(Some(cwd.to_string_lossy().into_owned()));
}
}
}
@@ -388,9 +385,7 @@ where
let node_resolver = state.borrow::<NodeResolverRc>();
let pkg = node_resolver
- .get_closest_package_json(
- &Url::from_file_path(parent_path.unwrap()).unwrap(),
- )
+ .get_closest_package_json_from_path(&PathBuf::from(parent_path.unwrap()))
.ok()
.flatten();
if pkg.is_none() {
@@ -457,7 +452,7 @@ where
pub fn op_require_as_file_path(#[string] file_or_url: String) -> String {
if let Ok(url) = Url::parse(&file_or_url) {
if let Ok(p) = url.to_file_path() {
- return p.to_string_lossy().to_string();
+ return p.to_string_lossy().into_owned();
}
}
@@ -469,7 +464,7 @@ pub fn op_require_as_file_path(#[string] file_or_url: String) -> String {
pub fn op_require_resolve_exports<P>(
state: &mut OpState,
uses_local_node_modules_dir: bool,
- #[string] modules_path: String,
+ #[string] modules_path_str: String,
#[string] _request: String,
#[string] name: String,
#[string] expansion: String,
@@ -482,22 +477,22 @@ where
let npm_resolver = state.borrow::<NpmResolverRc>();
let node_resolver = state.borrow::<NodeResolverRc>();
- let pkg_path = if npm_resolver
- .in_npm_package_at_file_path(&PathBuf::from(&modules_path))
+ let modules_path = PathBuf::from(&modules_path_str);
+ let pkg_path = if npm_resolver.in_npm_package_at_file_path(&modules_path)
&& !uses_local_node_modules_dir
{
modules_path
} else {
- let original = modules_path.clone();
- let mod_dir = path_resolve(vec![modules_path, name]);
- if fs.is_dir_sync(Path::new(&mod_dir)) {
+ let mod_dir =
+ path_resolve([modules_path_str.as_str(), name.as_str()].into_iter());
+ if fs.is_dir_sync(&mod_dir) {
mod_dir
} else {
- original
+ modules_path
}
};
- let Some(pkg) = node_resolver
- .load_package_json(&PathBuf::from(&pkg_path).join("package.json"))?
+ let Some(pkg) =
+ node_resolver.load_package_json(&pkg_path.join("package.json"))?
else {
return Ok(None);
};
@@ -527,18 +522,15 @@ where
pub fn op_require_read_closest_package_json<P>(
state: &mut OpState,
#[string] filename: String,
-) -> Result<Option<PackageJson>, AnyError>
+) -> Result<Option<PackageJsonRc>, AnyError>
where
P: NodePermissions + 'static,
{
- ensure_read_permission::<P>(
- state,
- PathBuf::from(&filename).parent().unwrap(),
- )?;
+ let filename = PathBuf::from(filename);
+ ensure_read_permission::<P>(state, filename.parent().unwrap())?;
let node_resolver = state.borrow::<NodeResolverRc>().clone();
node_resolver
- .get_closest_package_json(&Url::from_file_path(filename).unwrap())
- .map(|maybe_pkg| maybe_pkg.map(|pkg| (*pkg).clone()))
+ .get_closest_package_json_from_path(&filename)
.map_err(AnyError::from)
}
@@ -547,7 +539,7 @@ where
pub fn op_require_read_package_scope<P>(
state: &mut OpState,
#[string] package_json_path: String,
-) -> Option<PackageJson>
+) -> Option<PackageJsonRc>
where
P: NodePermissions + 'static,
{
@@ -561,7 +553,6 @@ where
.load_package_json(&package_json_path)
.ok()
.flatten()
- .map(|pkg| (*pkg).clone())
}
#[op2]
@@ -610,7 +601,7 @@ pub fn op_require_break_on_next_statement(state: &mut OpState) {
fn url_to_file_path_string(url: &Url) -> Result<String, AnyError> {
let file_path = url_to_file_path(url)?;
- Ok(file_path.to_string_lossy().to_string())
+ Ok(file_path.to_string_lossy().into_owned())
}
fn url_to_file_path(url: &Url) -> Result<PathBuf, AnyError> {
diff --git a/ext/node_resolver/analyze.rs b/ext/node_resolver/analyze.rs
index 8d6a73424..8231cdc8a 100644
--- a/ext/node_resolver/analyze.rs
+++ b/ext/node_resolver/analyze.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::borrow::Cow;
use std::collections::BTreeSet;
use std::collections::HashSet;
use std::path::Path;
@@ -583,9 +584,16 @@ fn not_found(path: &str, referrer: &Path) -> AnyError {
std::io::Error::new(std::io::ErrorKind::NotFound, msg).into()
}
-fn escape_for_double_quote_string(text: &str) -> String {
- text.replace('\\', "\\\\").replace('"', "\\\"")
+fn escape_for_double_quote_string(text: &str) -> Cow<str> {
+ // this should be rare, so doing a scan first before allocating is ok
+ if text.chars().any(|c| matches!(c, '"' | '\\')) {
+ // don't bother making this more complex for perf because it's rare
+ Cow::Owned(text.replace('\\', "\\\\").replace('"', "\\\""))
+ } else {
+ Cow::Borrowed(text)
+ }
}
+
#[cfg(test)]
mod tests {
use super::*;