Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions apps/oxlint/src-js/generated/deserialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ function deserializeIdentifierName(pos) {
__proto__: NodeProto,
type: "Identifier",
decorators: null,
name: deserializeStr(pos + 8),
name: deserializeIdent(pos + 8),
optional: null,
typeAnnotation: null,
start,
Expand All @@ -223,7 +223,7 @@ function deserializeIdentifierReference(pos) {
__proto__: NodeProto,
type: "Identifier",
decorators: null,
name: deserializeStr(pos + 8),
name: deserializeIdent(pos + 8),
optional: null,
typeAnnotation: null,
start,
Expand All @@ -245,7 +245,7 @@ function deserializeBindingIdentifier(pos) {
__proto__: NodeProto,
type: "Identifier",
decorators: null,
name: deserializeStr(pos + 8),
name: deserializeIdent(pos + 8),
optional: null,
typeAnnotation: null,
start,
Expand All @@ -267,7 +267,7 @@ function deserializeLabelIdentifier(pos) {
__proto__: NodeProto,
type: "Identifier",
decorators: null,
name: deserializeStr(pos + 8),
name: deserializeIdent(pos + 8),
optional: null,
typeAnnotation: null,
start,
Expand Down Expand Up @@ -2649,7 +2649,7 @@ function deserializePrivateIdentifier(pos) {
return {
__proto__: NodeProto,
type: "PrivateIdentifier",
name: deserializeStr(pos + 8),
name: deserializeIdent(pos + 8),
start,
end,
range: [start, end],
Expand Down Expand Up @@ -3685,7 +3685,7 @@ function deserializeJSXIdentifier(pos) {
return {
__proto__: NodeProto,
type: "JSXIdentifier",
name: deserializeStr(pos + 8),
name: deserializeIdent(pos + 8),
start,
end,
range: [start, end],
Expand Down
22 changes: 15 additions & 7 deletions crates/oxc_allocator/src/clone_in.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use std::{alloc::Layout, cell::Cell, hash::Hash, ptr::NonNull, slice};
use std::{
alloc::Layout,
cell::Cell,
hash::{BuildHasher, Hash},
ptr::NonNull,
slice,
};

use crate::{Allocator, Box, HashMap, Vec};
use crate::{Allocator, Box, HashMapImpl, Vec};

/// A trait to explicitly clone an object into an arena allocator.
///
Expand Down Expand Up @@ -207,13 +213,14 @@ where
}
}

impl<'new_alloc, K, V, CK, CV> CloneIn<'new_alloc> for HashMap<'_, K, V>
impl<'new_alloc, K, V, CK, CV, H> CloneIn<'new_alloc> for HashMapImpl<'_, K, V, H>
where
K: CloneIn<'new_alloc, Cloned = CK>,
V: CloneIn<'new_alloc, Cloned = CV>,
CK: Hash + Eq,
H: BuildHasher + Default,
{
type Cloned = HashMap<'new_alloc, CK, CV>;
type Cloned = HashMapImpl<'new_alloc, CK, CV, H>;

fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
// Keys in original hash map are guaranteed to be unique.
Expand All @@ -223,15 +230,15 @@ where
// `hashbrown::HashMap` also has a faster cloning method in its `Clone` implementation,
// but those APIs are not exposed, and `Clone` doesn't support custom allocators.
// So sadly this is a lot slower than it could be, especially for `Copy` types.
let mut cloned = HashMap::with_capacity_in(self.len(), allocator);
let mut cloned = HashMapImpl::with_capacity_in(self.len(), allocator);
for (key, value) in self {
cloned.insert(key.clone_in(allocator), value.clone_in(allocator));
}
cloned
}

fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
let mut cloned = HashMap::with_capacity_in(self.len(), allocator);
let mut cloned = HashMapImpl::with_capacity_in(self.len(), allocator);
for (key, value) in self {
cloned.insert(
key.clone_in_with_semantic_ids(allocator),
Expand Down Expand Up @@ -281,7 +288,8 @@ impl_clone_in! {

#[cfg(test)]
mod test {
use super::{Allocator, CloneIn, HashMap, Vec};
use super::{Allocator, CloneIn, Vec};
use crate::HashMap;

#[test]
fn clone_in_boxed_slice() {
Expand Down
61 changes: 41 additions & 20 deletions crates/oxc_allocator/src/hash_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
#![expect(clippy::inline_always)]

use std::{
hash::Hash,
hash::{BuildHasher, Hash},
mem::ManuallyDrop,
ops::{Deref, DerefMut},
};

use bumpalo::Bump;
use rustc_hash::FxBuildHasher;

// Re-export additional types from `hashbrown`
pub use hashbrown::{
Expand All @@ -27,7 +26,7 @@ pub use hashbrown::{

use crate::Allocator;

type FxHashMap<'alloc, K, V> = hashbrown::HashMap<K, V, FxBuildHasher, &'alloc Bump>;
type InternalHashMap<'alloc, K, V, H> = hashbrown::HashMap<K, V, H, &'alloc Bump>;

/// A hash map without `Drop`, that uses [`FxHasher`] to hash keys, and stores data in arena allocator.
///
Expand All @@ -49,7 +48,26 @@ type FxHashMap<'alloc, K, V> = hashbrown::HashMap<K, V, FxBuildHasher, &'alloc B
///
/// [`FxHasher`]: rustc_hash::FxHasher
#[derive(Debug)]
pub struct HashMap<'alloc, K, V>(pub(crate) ManuallyDrop<FxHashMap<'alloc, K, V>>);
pub struct HashMapImpl<'alloc, K, V, H: BuildHasher>(
pub(crate) ManuallyDrop<InternalHashMap<'alloc, K, V, H>>,
);

// Workaround for `FxBuildHasher` not implementing `Debug`.
#[derive(Default, Debug)]
#[expect(missing_docs)] // TODO
pub struct FxBuildHasher;

impl BuildHasher for FxBuildHasher {
type Hasher = rustc_hash::FxHasher;

#[inline(always)]
fn build_hasher(&self) -> Self::Hasher {
rustc_hash::FxHasher::default()
}
}

#[expect(missing_docs)] // TODO
pub type HashMap<'alloc, K, V> = HashMapImpl<'alloc, K, V, FxBuildHasher>;

/// SAFETY: Even though `Bump` is not `Sync`, we can make `HashMap<K, V>` `Sync` if both `K` and `V`
/// are `Sync` because:
Expand Down Expand Up @@ -85,12 +103,12 @@ pub struct HashMap<'alloc, K, V>(pub(crate) ManuallyDrop<FxHashMap<'alloc, K, V>
///
/// TODO: Fix these holes.
/// TODO: Remove any other methods that currently allow performing allocations with only a `&self` reference.
unsafe impl<K: Sync, V: Sync> Sync for HashMap<'_, K, V> {}
unsafe impl<K: Sync, V: Sync, H: BuildHasher> Sync for HashMapImpl<'_, K, V, H> {}

// TODO: `IntoIter`, `Drain`, and other consuming iterators provided by `hashbrown` are `Drop`.
// Wrap them in `ManuallyDrop` to prevent that.

impl<'alloc, K, V> HashMap<'alloc, K, V> {
impl<'alloc, K, V, H: BuildHasher + Default> HashMapImpl<'alloc, K, V, H> {
/// Const assertions that `K` and `V` are not `Drop`.
/// Must be referenced in all methods which create a `HashMap`.
const ASSERT_K_AND_V_ARE_NOT_DROP: () = {
Expand All @@ -112,7 +130,7 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> {
pub fn new_in(allocator: &'alloc Allocator) -> Self {
const { Self::ASSERT_K_AND_V_ARE_NOT_DROP };

let inner = FxHashMap::with_hasher_in(FxBuildHasher, allocator.bump());
let inner = InternalHashMap::with_hasher_in(H::default(), allocator.bump());
Self(ManuallyDrop::new(inner))
}

Expand All @@ -125,7 +143,7 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> {
const { Self::ASSERT_K_AND_V_ARE_NOT_DROP };

let inner =
FxHashMap::with_capacity_and_hasher_in(capacity, FxBuildHasher, allocator.bump());
InternalHashMap::with_capacity_and_hasher_in(capacity, H::default(), allocator.bump());
Self(ManuallyDrop::new(inner))
}

Expand Down Expand Up @@ -153,7 +171,8 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> {
// * Positive: Avoids potential large over-allocation for iterators where upper bound may be a large over-estimate
// e.g. filter iterators.
let capacity = iter.size_hint().0;
let map = FxHashMap::with_capacity_and_hasher_in(capacity, FxBuildHasher, allocator.bump());
let map =
InternalHashMap::with_capacity_and_hasher_in(capacity, H::default(), allocator.bump());
// Wrap in `ManuallyDrop` *before* calling `for_each`, so compiler doesn't insert unnecessary code
// to drop the `FxHashMap` in case of a panic in iterator's `next` method
let mut map = ManuallyDrop::new(map);
Expand Down Expand Up @@ -202,23 +221,23 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> {
}

// Provide access to all `hashbrown::HashMap`'s methods via deref
impl<'alloc, K, V> Deref for HashMap<'alloc, K, V> {
type Target = FxHashMap<'alloc, K, V>;
impl<'alloc, K, V, H: BuildHasher> Deref for HashMapImpl<'alloc, K, V, H> {
type Target = InternalHashMap<'alloc, K, V, H>;

#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<'alloc, K, V> DerefMut for HashMap<'alloc, K, V> {
impl<'alloc, K, V, H: BuildHasher> DerefMut for HashMapImpl<'alloc, K, V, H> {
#[inline]
fn deref_mut(&mut self) -> &mut FxHashMap<'alloc, K, V> {
fn deref_mut(&mut self) -> &mut InternalHashMap<'alloc, K, V, H> {
&mut self.0
}
}

impl<'alloc, K, V> IntoIterator for HashMap<'alloc, K, V> {
impl<'alloc, K, V, H: BuildHasher> IntoIterator for HashMapImpl<'alloc, K, V, H> {
type IntoIter = IntoIter<K, V, &'alloc Bump>;
type Item = (K, V);

Expand All @@ -235,8 +254,8 @@ impl<'alloc, K, V> IntoIterator for HashMap<'alloc, K, V> {
}
}

impl<'alloc, 'i, K, V> IntoIterator for &'i HashMap<'alloc, K, V> {
type IntoIter = <&'i FxHashMap<'alloc, K, V> as IntoIterator>::IntoIter;
impl<'alloc, 'i, K, V, H: BuildHasher> IntoIterator for &'i HashMapImpl<'alloc, K, V, H> {
type IntoIter = <&'i InternalHashMap<'alloc, K, V, H> as IntoIterator>::IntoIter;
type Item = (&'i K, &'i V);

/// Creates an iterator over the entries of a `HashMap` in arbitrary order.
Expand All @@ -250,8 +269,8 @@ impl<'alloc, 'i, K, V> IntoIterator for &'i HashMap<'alloc, K, V> {
}
}

impl<'alloc, 'i, K, V> IntoIterator for &'i mut HashMap<'alloc, K, V> {
type IntoIter = <&'i mut FxHashMap<'alloc, K, V> as IntoIterator>::IntoIter;
impl<'alloc, 'i, K, V, H: BuildHasher> IntoIterator for &'i mut HashMapImpl<'alloc, K, V, H> {
type IntoIter = <&'i mut InternalHashMap<'alloc, K, V, H> as IntoIterator>::IntoIter;
type Item = (&'i K, &'i mut V);

/// Creates an iterator over the entries of a `HashMap` in arbitrary order
Expand All @@ -266,21 +285,23 @@ impl<'alloc, 'i, K, V> IntoIterator for &'i mut HashMap<'alloc, K, V> {
}
}

impl<K, V> PartialEq for HashMap<'_, K, V>
impl<K, V, H> PartialEq for HashMapImpl<'_, K, V, H>
where
K: Eq + Hash,
V: PartialEq,
H: BuildHasher,
{
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}

impl<K, V> Eq for HashMap<'_, K, V>
impl<K, V, H> Eq for HashMapImpl<'_, K, V, H>
where
K: Eq + Hash,
V: Eq,
H: BuildHasher,
{
}

Expand Down
3 changes: 1 addition & 2 deletions crates/oxc_allocator/src/hash_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ use std::{
};

use bumpalo::Bump;
use rustc_hash::FxBuildHasher;

// Re-export additional types from `hashbrown`
pub use hashbrown::hash_set::{
Difference, Drain, Entry, ExtractIf, Intersection, IntoIter, Iter, SymmetricDifference, Union,
};

use crate::{Allocator, HashMap};
use crate::{Allocator, HashMap, hash_map::FxBuildHasher};

type FxHashSet<'alloc, T> = hashbrown::HashSet<T, FxBuildHasher, &'alloc Bump>;

Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_allocator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub use bitset::BitSet;
pub use boxed::Box;
pub use clone_in::CloneIn;
pub use convert::{FromIn, IntoIn};
pub use hash_map::HashMap;
pub use hash_map::{FxBuildHasher, HashMap, HashMapImpl};
pub use hash_set::HashSet;
#[cfg(feature = "pool")]
pub use pool::*;
Expand Down
13 changes: 7 additions & 6 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::cell::Cell;
use oxc_allocator::{Box, CloneIn, Dummy, GetAddress, TakeIn, UnstableAddress, Vec};
use oxc_ast_macros::ast;
use oxc_estree::ESTree;
use oxc_span::{Atom, ContentEq, GetSpan, GetSpanMut, SourceType, Span};
use oxc_span::{Atom, ContentEq, GetSpan, GetSpanMut, Ident, SourceType, Span};
use oxc_syntax::{
operator::{
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator,
Expand Down Expand Up @@ -234,7 +234,7 @@ pub use match_expression;
pub struct IdentifierName<'a> {
pub span: Span,
#[estree(json_safe)]
pub name: Atom<'a>,
pub name: Ident<'a>,
}

/// `x` inside `func` in `const x = 0; function func() { console.log(x); }`
Expand All @@ -253,8 +253,9 @@ pub struct IdentifierName<'a> {
pub struct IdentifierReference<'a> {
pub span: Span,
/// The name of the identifier being referenced.
// pub name: Atom<'a>,
#[estree(json_safe)]
pub name: Atom<'a>,
pub name: Ident<'a>,
/// Reference ID
///
/// Identifies what identifier this refers to, and how it is used. This is
Expand Down Expand Up @@ -283,7 +284,7 @@ pub struct BindingIdentifier<'a> {
pub span: Span,
/// The identifier name being bound.
#[estree(json_safe)]
pub name: Atom<'a>,
pub name: Ident<'a>,
/// Unique identifier for this binding.
///
/// This gets initialized during [`semantic analysis`] in the bind step. If
Expand All @@ -309,7 +310,7 @@ pub struct BindingIdentifier<'a> {
pub struct LabelIdentifier<'a> {
pub span: Span,
#[estree(json_safe)]
pub name: Atom<'a>,
pub name: Ident<'a>,
}

/// `this` in `return this.prop;`
Expand Down Expand Up @@ -2285,7 +2286,7 @@ pub enum MethodDefinitionKind {
#[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, ContentEq, ESTree, UnstableAddress)]
pub struct PrivateIdentifier<'a> {
pub span: Span,
pub name: Atom<'a>,
pub name: Ident<'a>,
}

/// Class Static Block
Expand Down
Loading
Loading