forked from oxc-project/oxc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(semantic): add ClassTable (oxc-project#1793)
- Loading branch information
Showing
12 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use oxc_ast::{ | ||
ast::{ClassBody, ClassElement, MethodDefinition, PrivateIdentifier, PropertyDefinition}, | ||
AstKind, | ||
}; | ||
use oxc_span::GetSpan; | ||
use oxc_syntax::class::ClassId; | ||
|
||
use crate::{AstNodeId, AstNodes}; | ||
|
||
use super::{ | ||
table::{Method, PrivateIdentifierReference, Property}, | ||
ClassTable, | ||
}; | ||
|
||
#[derive(Debug, Default)] | ||
pub struct ClassTableBuilder { | ||
pub current_class_id: Option<ClassId>, | ||
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.0); | ||
} | ||
ClassElement::MethodDefinition(definition) => { | ||
self.declare_class_method(definition.0); | ||
} | ||
_ => {} | ||
} | ||
} | ||
} | ||
|
||
pub fn declare_class_property(&mut self, property: &PropertyDefinition) { | ||
let is_private = property.key.is_private_identifier(); | ||
let name = | ||
if is_private { property.key.private_name() } else { property.key.static_name() }; | ||
|
||
if let Some(name) = name { | ||
if let Some(class_id) = self.current_class_id { | ||
self.classes | ||
.add_property(class_id, Property::new(name, property.key.span(), is_private)); | ||
} | ||
} | ||
} | ||
|
||
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 property_id = self.classes.get_property_id(class_id, &ident.name); | ||
let method_id = if property_id.is_some() { | ||
None | ||
} else { | ||
self.classes.get_method_id(class_id, &ident.name) | ||
}; | ||
let reference = PrivateIdentifierReference::new( | ||
current_node_id, | ||
ident.name.clone(), | ||
ident.span, | ||
property_id, | ||
method_id, | ||
); | ||
self.classes.add_private_identifier_reference(class_id, reference); | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn declare_class_method(&mut self, method: &MethodDefinition) { | ||
if method.kind.is_constructor() { | ||
return; | ||
} | ||
let is_private = method.key.is_private_identifier(); | ||
let name = if is_private { method.key.private_name() } else { method.key.static_name() }; | ||
|
||
if let Some(name) = name { | ||
if let Some(class_id) = self.current_class_id { | ||
self.classes.add_method( | ||
class_id, | ||
Method::new(name, method.key.span(), is_private, method.kind), | ||
); | ||
} | ||
} | ||
} | ||
|
||
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()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
mod builder; | ||
mod table; | ||
|
||
pub use builder::ClassTableBuilder; | ||
pub use table::ClassTable; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
use oxc_ast::ast::MethodDefinitionKind; | ||
use oxc_index::IndexVec; | ||
use oxc_span::{Atom, Span}; | ||
use oxc_syntax::class::{ClassId, MethodId, PropertyId}; | ||
use rustc_hash::FxHashMap; | ||
|
||
use crate::node::AstNodeId; | ||
|
||
#[derive(Debug)] | ||
pub struct Property { | ||
pub name: Atom, | ||
pub span: Span, | ||
pub is_private: bool, | ||
} | ||
|
||
impl Property { | ||
pub fn new(name: Atom, span: Span, is_private: bool) -> Self { | ||
Self { name, span, is_private } | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Method { | ||
pub name: Atom, | ||
pub span: Span, | ||
pub is_private: bool, | ||
pub kind: MethodDefinitionKind, | ||
} | ||
|
||
impl Method { | ||
pub fn new(name: Atom, span: Span, is_private: bool, kind: MethodDefinitionKind) -> Self { | ||
Self { name, span, is_private, kind } | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct PrivateIdentifierReference { | ||
pub id: AstNodeId, | ||
pub name: Atom, | ||
pub span: Span, | ||
pub property_id: Option<PropertyId>, | ||
pub method_id: Option<MethodId>, | ||
} | ||
|
||
impl PrivateIdentifierReference { | ||
pub fn new( | ||
id: AstNodeId, | ||
name: Atom, | ||
span: Span, | ||
property_id: Option<PropertyId>, | ||
method_id: Option<MethodId>, | ||
) -> Self { | ||
Self { id, name, span, property_id, method_id } | ||
} | ||
} | ||
|
||
/// Class Table | ||
/// | ||
/// `SoA` (Struct of Arrays) for memory efficiency. | ||
#[derive(Debug, Default)] | ||
pub struct ClassTable { | ||
pub parent_ids: FxHashMap<ClassId, ClassId>, | ||
pub declarations: IndexVec<ClassId, AstNodeId>, | ||
// PropertyDefinition | ||
pub properties: IndexVec<ClassId, IndexVec<PropertyId, Property>>, | ||
// MethodDefinition | ||
pub methods: IndexVec<ClassId, IndexVec<MethodId, Method>>, | ||
// PrivateIdentifier reference | ||
pub private_identifiers: IndexVec<ClassId, Vec<PrivateIdentifierReference>>, | ||
} | ||
|
||
impl ClassTable { | ||
pub fn ancestors(&self, class_id: ClassId) -> impl Iterator<Item = ClassId> + '_ { | ||
std::iter::successors(Some(class_id), |class_id| self.parent_ids.get(class_id).copied()) | ||
} | ||
|
||
pub fn iter_enumerated(&self) -> impl Iterator<Item = (ClassId, &AstNodeId)> + '_ { | ||
self.declarations.iter_enumerated() | ||
} | ||
|
||
pub fn get_property_id(&self, class_id: ClassId, name: &Atom) -> Option<PropertyId> { | ||
self.properties[class_id].iter_enumerated().find_map(|(property_id, property)| { | ||
if property.name == *name { | ||
Some(property_id) | ||
} else { | ||
None | ||
} | ||
}) | ||
} | ||
|
||
pub fn get_method_id(&self, class_id: ClassId, name: &Atom) -> Option<MethodId> { | ||
self.methods[class_id].iter_enumerated().find_map(|(method_id, method)| { | ||
if method.name == *name { | ||
Some(method_id) | ||
} else { | ||
None | ||
} | ||
}) | ||
} | ||
|
||
pub fn has_private_definition(&self, class_id: ClassId, name: &Atom) -> bool { | ||
self.properties[class_id].iter().any(|p| p.is_private && p.name == *name) | ||
|| self.methods[class_id].iter().any(|m| m.is_private && m.name == *name) | ||
} | ||
|
||
pub fn declare_class(&mut self, parent_id: Option<ClassId>, ast_node_id: AstNodeId) -> ClassId { | ||
let class_id = self.declarations.push(ast_node_id); | ||
if let Some(parent_id) = parent_id { | ||
self.parent_ids.insert(class_id, parent_id); | ||
}; | ||
self.properties.push(IndexVec::default()); | ||
self.methods.push(IndexVec::default()); | ||
self.private_identifiers.push(Vec::new()); | ||
class_id | ||
} | ||
|
||
pub fn add_property(&mut self, class_id: ClassId, property: Property) { | ||
self.properties[class_id].push(property); | ||
} | ||
|
||
pub fn add_method(&mut self, class_id: ClassId, method: Method) { | ||
self.methods[class_id].push(method); | ||
} | ||
|
||
pub fn add_private_identifier_reference( | ||
&mut self, | ||
class_id: ClassId, | ||
private_identifier_reference: PrivateIdentifierReference, | ||
) { | ||
self.private_identifiers[class_id].push(private_identifier_reference); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.