mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
## Why
Due to the usage of `&'alloc mut T` in `oxc_allocator::Box`, and
`bumpalo::collections::Vec` in `oxc_allocator::Vec`, ast types are
currently invariant over their allocator lifetime `'a`. This prevents
`ouroboros` from generating `borrow_*` on ast type fields, leading to
the unfriendly `with_*` api:
c250b288ef/crates/oxc_parser/examples/multi-thread.rs (L82-L84)
## How
- For `oxc_allocator::Vec`, switch to `allocator_api2::vec::Vec`, which
has a covariant relationship with the allocator lifetime.
- For `oxc_allocator::Box`, use `std::ptr::NonNull` which is
specifically designed to be covariant. I don't use
`allocator_api2::boxed::Box` because it holds the allocator for
dropping, so the size is bigger.
## Downside
Now that `oxc_allocator::Box` uses the unsafe `NonNull`. It has to be a
private field to be safe. This make it impossible to do `Box(....)`
pattern matching.
162 lines
5.3 KiB
Rust
162 lines
5.3 KiB
Rust
use oxc_ast::{
|
|
ast::{
|
|
AccessorProperty, ClassBody, ClassElement, MethodDefinition, MethodDefinitionKind,
|
|
PrivateIdentifier, PropertyDefinition,
|
|
},
|
|
AstKind,
|
|
};
|
|
use oxc_span::{Atom, GetSpan};
|
|
use oxc_syntax::class::{ClassId, ElementKind};
|
|
|
|
use crate::{AstNodeId, AstNodes};
|
|
|
|
use super::{
|
|
table::{Element, PrivateIdentifierReference},
|
|
ClassTable,
|
|
};
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct ClassTableBuilder {
|
|
pub current_class_id: Option<ClassId>,
|
|
pub classes: ClassTable,
|
|
}
|
|
|
|
impl ClassTableBuilder {
|
|
pub fn new() -> Self {
|
|
Self { current_class_id: None, classes: ClassTable::default() }
|
|
}
|
|
|
|
pub fn build(self) -> ClassTable {
|
|
self.classes
|
|
}
|
|
|
|
pub fn declare_class_body(
|
|
&mut self,
|
|
class: &ClassBody,
|
|
current_node_id: AstNodeId,
|
|
nodes: &AstNodes,
|
|
) {
|
|
let parent_id = nodes.parent_id(current_node_id).unwrap_or_else(|| unreachable!());
|
|
self.current_class_id = Some(self.classes.declare_class(self.current_class_id, parent_id));
|
|
|
|
for element in &class.body {
|
|
match element {
|
|
ClassElement::PropertyDefinition(definition) => {
|
|
self.declare_class_property(definition);
|
|
}
|
|
ClassElement::MethodDefinition(definition) => {
|
|
self.declare_class_method(definition);
|
|
}
|
|
ClassElement::AccessorProperty(definition) => {
|
|
self.declare_class_accessor(definition);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn declare_class_accessor(&mut self, property: &AccessorProperty) {
|
|
let is_private = property.key.is_private_identifier();
|
|
let name = property.key.name();
|
|
|
|
if let Some(name) = name {
|
|
if let Some(class_id) = self.current_class_id {
|
|
self.classes.add_element(
|
|
class_id,
|
|
Element::new(
|
|
name,
|
|
property.key.span(),
|
|
property.r#static,
|
|
is_private,
|
|
ElementKind::Accessor,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn declare_class_property(&mut self, property: &PropertyDefinition) {
|
|
let is_private = property.key.is_private_identifier();
|
|
let name = property.key.name();
|
|
|
|
if let Some(name) = name {
|
|
if let Some(class_id) = self.current_class_id {
|
|
self.classes.add_element(
|
|
class_id,
|
|
Element::new(
|
|
name,
|
|
property.key.span(),
|
|
property.r#static,
|
|
is_private,
|
|
ElementKind::Property,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn add_private_identifier_reference(
|
|
&mut self,
|
|
ident: &PrivateIdentifier,
|
|
current_node_id: AstNodeId,
|
|
nodes: &AstNodes,
|
|
) {
|
|
let parent_kind = nodes.parent_kind(current_node_id);
|
|
if let Some(parent_kind) = parent_kind {
|
|
if matches!(parent_kind, AstKind::PrivateInExpression(_) | AstKind::MemberExpression(_))
|
|
{
|
|
if let Some(class_id) = self.current_class_id {
|
|
let element_ids = self.classes.get_element_ids(class_id, &ident.name);
|
|
|
|
let reference = PrivateIdentifierReference::new(
|
|
current_node_id,
|
|
ident.name.to_compact_str(),
|
|
ident.span,
|
|
element_ids,
|
|
);
|
|
self.classes.add_private_identifier_reference(class_id, reference);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn declare_class_method(&mut self, method: &MethodDefinition) {
|
|
if method.kind.is_constructor() || method.value.is_typescript_syntax() {
|
|
return;
|
|
}
|
|
let is_private = method.key.is_private_identifier();
|
|
let name = if is_private {
|
|
method.key.private_name().map(Atom::to_compact_str)
|
|
} else {
|
|
method.key.static_name()
|
|
};
|
|
|
|
if let Some(name) = name {
|
|
if let Some(class_id) = self.current_class_id {
|
|
self.classes.add_element(
|
|
class_id,
|
|
Element::new(
|
|
name,
|
|
method.key.span(),
|
|
method.r#static,
|
|
is_private,
|
|
match method.kind {
|
|
MethodDefinitionKind::Method => ElementKind::Method,
|
|
MethodDefinitionKind::Get => ElementKind::Method | ElementKind::Getter,
|
|
MethodDefinitionKind::Set => ElementKind::Method | ElementKind::Setter,
|
|
MethodDefinitionKind::Constructor => {
|
|
// Skip constructor
|
|
unreachable!()
|
|
}
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
pub fn pop_class(&mut self) {
|
|
self.current_class_id = self
|
|
.current_class_id
|
|
.and_then(|current_class_id| self.classes.parent_ids.get(¤t_class_id).copied());
|
|
}
|
|
}
|