diff --git a/crates/oxc_resolver/src/cache.rs b/crates/oxc_resolver/src/cache.rs index 99352defe..472c14aeb 100644 --- a/crates/oxc_resolver/src/cache.rs +++ b/crates/oxc_resolver/src/cache.rs @@ -1,22 +1,25 @@ use std::{ - hash::BuildHasherDefault, + borrow::Borrow, + convert::AsRef, + hash::{BuildHasherDefault, Hash, Hasher}, + ops::Deref, path::{Path, PathBuf}, sync::{Arc, OnceLock}, }; -use dashmap::DashMap; +use dashmap::DashSet; use rustc_hash::FxHasher; use crate::{package_json::PackageJson, FileMetadata, FileSystem, ResolveError}; pub struct Cache { pub(crate) fs: Fs, - cache: DashMap, Arc, BuildHasherDefault>, + cache: DashSet>, } impl Default for Cache { fn default() -> Self { - Self { fs: Fs::default(), cache: DashMap::default() } + Self { fs: Fs::default(), cache: DashSet::default() } } } @@ -28,36 +31,74 @@ impl Cache { /// # Panics /// /// * Path is file but does not have a parent - pub fn dirname(&self, cache_value: &Arc) -> Arc { - Arc::clone(if cache_value.is_file(&self.fs) { + pub fn dirname<'a>(&self, cache_value: &'a CacheValue) -> &'a CacheValue { + if cache_value.is_file(&self.fs) { cache_value.parent.as_ref().unwrap() } else { cache_value - }) + } } - pub fn value(&self, path: &Path) -> Arc { + pub fn value(&self, path: &Path) -> CacheValue { if let Some(cache_entry) = self.cache.get(path) { - return Arc::clone(cache_entry.value()); + return cache_entry.key().clone(); } let parent = path.parent().map(|p| self.value(p)); - let data = Arc::new(CacheValue::new(path.to_path_buf().into_boxed_path(), parent)); - self.cache.insert(path.to_path_buf().into_boxed_path(), Arc::clone(&data)); + let data = + CacheValue(Arc::new(CacheValueImpl::new(path.to_path_buf().into_boxed_path(), parent))); + self.cache.insert(data.clone()); data } } -#[derive(Debug, Clone)] -pub struct CacheValue { +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct CacheValue(Arc); + +impl Deref for CacheValue { + type Target = CacheValueImpl; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl AsRef for CacheValue { + fn as_ref(&self) -> &CacheValueImpl { + self.0.as_ref() + } +} + +impl Borrow for CacheValue { + fn borrow(&self) -> &Path { + &self.path + } +} + +#[derive(Debug)] +pub struct CacheValueImpl { path: Box, - parent: Option>, + parent: Option, meta: OnceLock>, symlink: OnceLock>, package_json: OnceLock, ResolveError>>>, } -impl CacheValue { - fn new(path: Box, parent: Option>) -> Self { +impl Hash for CacheValueImpl { + fn hash(&self, h: &mut H) { + self.path.hash(h); + } +} + +impl PartialEq for CacheValueImpl { + fn eq(&self, other: &Self) -> bool { + self.path == other.path + } +} + +impl Eq for CacheValueImpl {} + +impl CacheValueImpl { + fn new(path: Box, parent: Option) -> Self { Self { path, parent, diff --git a/crates/oxc_resolver/src/lib.rs b/crates/oxc_resolver/src/lib.rs index cb0afffb6..56d53eee5 100644 --- a/crates/oxc_resolver/src/lib.rs +++ b/crates/oxc_resolver/src/lib.rs @@ -21,7 +21,6 @@ use std::{ borrow::Cow, ffi::OsStr, path::{Path, PathBuf}, - sync::Arc, }; use crate::{ @@ -38,7 +37,7 @@ pub use crate::{ resolution::Resolution, }; -type ResolveState = Result>, ResolveError>; +type ResolveState = Result, ResolveError>; /// Resolver with the current operating system as the file system pub type Resolver = ResolverGeneric; @@ -108,9 +107,9 @@ impl ResolverGeneric { /// Y: path fn require( &self, - cache_value: &Arc, + cache_value: &CacheValue, request: &Request, - ) -> Result, ResolveError> { + ) -> Result { match request.path { // 1. If X is a core module, // a. return the core module @@ -139,9 +138,9 @@ impl ResolverGeneric { // 3. If X begins with './' or '/' or '../' fn require_relative( &self, - cache_value: &Arc, + cache_value: &CacheValue, request: &str, - ) -> Result, ResolveError> { + ) -> Result { if let Some(path) = self.load_package_self(cache_value, request)? { return Ok(path); } @@ -163,16 +162,16 @@ impl ResolverGeneric { fn require_path( &self, - cache_value: &Arc, + cache_value: &CacheValue, request: &str, - ) -> Result, ResolveError> { + ) -> Result { let dirname = self.cache.dirname(cache_value); // 5. LOAD_PACKAGE_SELF(X, dirname(Y)) - if let Some(path) = self.load_package_self(&dirname, request)? { + if let Some(path) = self.load_package_self(dirname, request)? { return Ok(path); } // 6. LOAD_NODE_MODULES(X, dirname(Y)) - if let Some(path) = self.load_node_modules(&dirname, request)? { + if let Some(path) = self.load_node_modules(dirname, request)? { return Ok(path); } let cache_value = self.cache.value(&cache_value.path().join(request)); @@ -183,7 +182,7 @@ impl ResolverGeneric { Err(ResolveError::NotFound(cache_value.to_path_buf().into_boxed_path())) } - fn load_as_file(&self, cache_value: &Arc) -> ResolveState { + fn load_as_file(&self, cache_value: &CacheValue) -> ResolveState { // enhanced-resolve feature: extension_alias if let Some(path) = self.load_extension_alias(cache_value)? { return Ok(Some(path)); @@ -202,11 +201,7 @@ impl ResolverGeneric { Ok(None) } - fn load_extensions( - &self, - cache_value: &Arc, - extensions: &[String], - ) -> ResolveState { + fn load_extensions(&self, cache_value: &CacheValue, extensions: &[String]) -> ResolveState { let mut path_with_extension = cache_value.path().to_path_buf(); for extension in extensions { path_with_extension.set_extension(extension); @@ -218,11 +213,11 @@ impl ResolverGeneric { Ok(None) } - fn load_symlink(&self, cache_value: &Arc) -> Option { + fn load_symlink(&self, cache_value: &CacheValue) -> Option { if self.options.symlinks { cache_value.symlink(&self.cache.fs) } else { None } } - fn load_index(&self, cache_value: &Arc) -> ResolveState { + fn load_index(&self, cache_value: &CacheValue) -> ResolveState { for main_file in &self.options.main_files { let main_path = cache_value.path().join(main_file); let cache_value = self.cache.value(&main_path); @@ -241,7 +236,7 @@ impl ResolverGeneric { Ok(None) } - fn load_alias_or_file(&self, cache_value: &Arc) -> ResolveState { + fn load_alias_or_file(&self, cache_value: &CacheValue) -> ResolveState { if let Some(package_json) = cache_value.find_package_json(&self.cache.fs)? { let path = cache_value.path(); if let Some(path) = self.load_browser_field(path, None, &package_json)? { @@ -249,12 +244,12 @@ impl ResolverGeneric { } } if cache_value.is_file(&self.cache.fs) { - return Ok(Some(Arc::clone(cache_value))); + return Ok(Some(cache_value.clone())); } Ok(None) } - fn load_as_directory(&self, cache_value: &Arc) -> ResolveState { + fn load_as_directory(&self, cache_value: &CacheValue) -> ResolveState { // TODO: Only package.json is supported, so warn about having other values // Checking for empty files is needed for omitting checks on package.json // 1. If X/package.json is a file, @@ -288,7 +283,7 @@ impl ResolverGeneric { self.load_index(cache_value) } - fn load_node_modules(&self, cache_value: &Arc, request: &str) -> ResolveState { + fn load_node_modules(&self, cache_value: &CacheValue, request: &str) -> ResolveState { // 1. let DIRS = NODE_MODULES_PATHS(START) let dirs = self.node_module_paths(cache_value.path()); // 2. for each DIR in DIRS: @@ -338,7 +333,7 @@ impl ResolverGeneric { /// # Panics /// /// * Parent of package.json is None - fn load_package_self(&self, cache_value: &Arc, request: &str) -> ResolveState { + fn load_package_self(&self, cache_value: &CacheValue, request: &str) -> ResolveState { if let Some(package_json) = cache_value.find_package_json(&self.cache.fs)? { if let Some(path) = self.load_browser_field(cache_value.path(), Some(request), &package_json)? @@ -365,12 +360,7 @@ impl ResolverGeneric { Ok(None) } - fn load_alias( - &self, - cache_value: &Arc, - request: &str, - alias: &Alias, - ) -> ResolveState { + fn load_alias(&self, cache_value: &CacheValue, request: &str, alias: &Alias) -> ResolveState { for (alias, requests) in alias { let exact_match = alias.strip_prefix(request).is_some_and(|c| c == "$"); if request.starts_with(alias) || exact_match { @@ -411,7 +401,7 @@ impl ResolverGeneric { /// # Errors /// /// * [ResolveError::ExtensionAlias]: When all of the aliased extensions are not found - fn load_extension_alias(&self, cache_value: &Arc) -> ResolveState { + fn load_extension_alias(&self, cache_value: &CacheValue) -> ResolveState { let Some(path_extension) = cache_value.path().extension() else { return Ok(None) }; let Some((_, extensions)) = self.options.extension_alias.iter().find(|(ext, _)| OsStr::new(ext) == path_extension) @@ -426,9 +416,9 @@ impl ResolverGeneric { fn load_roots( &self, - cache_value: &Arc, + cache_value: &CacheValue, request: &str, - ) -> Result, ResolveError> { + ) -> Result { debug_assert!(request.starts_with('/')); if self.options.roots.is_empty() { let cache_value = self.cache.value(Path::new("/"));