Skip to content

Commit

Permalink
feat(semantic): add ClassTable (oxc-project#1793)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing authored Dec 25, 2023
1 parent bd11b02 commit ca04312
Show file tree
Hide file tree
Showing 12 changed files with 392 additions and 0 deletions.
6 changes: 6 additions & 0 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1890,6 +1890,12 @@ pub enum MethodDefinitionKind {
Set,
}

impl MethodDefinitionKind {
pub fn is_constructor(&self) -> bool {
matches!(self, Self::Constructor)
}
}

#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct PrivateIdentifier {
Expand Down
20 changes: 20 additions & 0 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc_hash::FxHashMap;
use crate::{
binder::Binder,
checker::{EarlyErrorJavaScript, EarlyErrorTypeScript},
class::ClassTableBuilder,
diagnostics::Redeclaration,
jsdoc::JSDocBuilder,
module_record::ModuleRecordBuilder,
Expand Down Expand Up @@ -84,6 +85,7 @@ pub struct SemanticBuilder<'a> {
check_syntax_error: bool,

redeclare_variables: RedeclareVariables,
class_table_builder: ClassTableBuilder,
}

pub struct SemanticBuilderReturn<'a> {
Expand Down Expand Up @@ -116,6 +118,7 @@ impl<'a> SemanticBuilder<'a> {
jsdoc: JSDocBuilder::new(source_text, &trivias),
check_syntax_error: false,
redeclare_variables: RedeclareVariables { variables: vec![] },
class_table_builder: ClassTableBuilder::new(),
}
}

Expand Down Expand Up @@ -169,6 +172,7 @@ impl<'a> SemanticBuilder<'a> {
nodes: self.nodes,
scopes: self.scope,
symbols: self.symbols,
classes: self.class_table_builder.build(),
module_record: Arc::clone(&self.module_record),
jsdoc: self.jsdoc.build(),
unused_labels: self.unused_labels.labels,
Expand All @@ -185,6 +189,7 @@ impl<'a> SemanticBuilder<'a> {
nodes: self.nodes,
scopes: self.scope,
symbols: self.symbols,
classes: self.class_table_builder.build(),
module_record: Arc::new(ModuleRecord::default()),
jsdoc: self.jsdoc.build(),
unused_labels: self.unused_labels.labels,
Expand Down Expand Up @@ -421,6 +426,20 @@ impl<'a> SemanticBuilder<'a> {
class.bind(self);
self.make_all_namespaces_valuelike();
}
AstKind::ClassBody(body) => {
self.class_table_builder.declare_class_body(
body,
self.current_node_id,
&self.nodes,
);
}
AstKind::PrivateIdentifier(ident) => {
self.class_table_builder.add_private_identifier_reference(
ident,
self.current_node_id,
&self.nodes,
);
}
AstKind::FormalParameters(params) => {
params.bind(self);
}
Expand Down Expand Up @@ -496,6 +515,7 @@ impl<'a> SemanticBuilder<'a> {
match kind {
AstKind::Class(_) => {
self.current_node_flags -= NodeFlags::Class;
self.class_table_builder.pop_class();
}
AstKind::ModuleDeclaration(decl) => {
self.current_symbol_flags -= Self::symbol_flag_from_module_declaration(decl);
Expand Down
117 changes: 117 additions & 0 deletions crates/oxc_semantic/src/class/builder.rs
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(&current_class_id).copied());
}
}
5 changes: 5 additions & 0 deletions crates/oxc_semantic/src/class/mod.rs
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;
132 changes: 132 additions & 0 deletions crates/oxc_semantic/src/class/table.rs
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);
}
}
8 changes: 8 additions & 0 deletions crates/oxc_semantic/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod binder;
mod builder;
mod checker;
mod class;
mod diagnostics;
mod jsdoc;
mod module_record;
Expand All @@ -12,6 +13,7 @@ mod symbol;
use std::{rc::Rc, sync::Arc};

pub use builder::{SemanticBuilder, SemanticBuilderReturn};
use class::ClassTable;
pub use jsdoc::{JSDoc, JSDocComment, JSDocTag};
use oxc_ast::{ast::IdentifierReference, AstKind, TriviasMap};
use oxc_span::SourceType;
Expand Down Expand Up @@ -40,6 +42,8 @@ pub struct Semantic<'a> {

symbols: SymbolTable,

classes: ClassTable,

trivias: Rc<TriviasMap>,

module_record: Arc<ModuleRecord>,
Expand Down Expand Up @@ -72,6 +76,10 @@ impl<'a> Semantic<'a> {
&self.scopes
}

pub fn classes(&self) -> &ClassTable {
&self.classes
}

pub fn scopes_mut(&mut self) -> &mut ScopeTree {
&mut self.scopes
}
Expand Down
Loading

0 comments on commit ca04312

Please sign in to comment.