Skip to content

Commit

Permalink
support computed class members
Browse files Browse the repository at this point in the history
  • Loading branch information
y21 committed Mar 31, 2024
1 parent 4a52a3d commit 8ba2f19
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 93 deletions.
35 changes: 14 additions & 21 deletions crates/dash_compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ use dash_middle::parser::expr::{
PropertyAccessExpr, Seq, UnaryExpr,
};
use dash_middle::parser::statement::{
Asyncness, BlockStatement, Class, ClassMemberKind, DoWhileLoop, ExportKind, ForInLoop, ForLoop, ForOfLoop, FuncId,
FunctionDeclaration, FunctionKind, IfStatement, ImportKind, Loop, Parameter, ReturnStatement, SpecifierKind,
Statement, StatementKind, SwitchCase, SwitchStatement, TryCatch, VariableBinding, VariableDeclaration,
VariableDeclarationKind, VariableDeclarationName, VariableDeclarations, WhileLoop,
Asyncness, BlockStatement, Class, ClassMemberKey, ClassMemberValue, DoWhileLoop, ExportKind, ForInLoop, ForLoop,
ForOfLoop, FuncId, FunctionDeclaration, FunctionKind, IfStatement, ImportKind, Loop, Parameter, ReturnStatement,
SpecifierKind, Statement, StatementKind, SwitchCase, SwitchStatement, TryCatch, VariableBinding,
VariableDeclaration, VariableDeclarationKind, VariableDeclarationName, VariableDeclarations, WhileLoop,
};
use dash_middle::sourcemap::Span;
use dash_middle::visitor::Visitor;
Expand Down Expand Up @@ -2096,15 +2096,7 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
}
None => None,
};
let constructor = class.members.iter().find_map(|member| {
if let ClassMemberKind::Method(method) = &member.kind {
if method.name == Some(sym::constructor) {
return Some(method.clone());
}
}

None
});
let constructor = class.constructor();

let binding_id = match class.name {
Some(name) => ib
Expand Down Expand Up @@ -2141,7 +2133,7 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
AssignmentExpr::new_local_place(
binding_id,
Expr {
span: Span::COMPILER_GENERATED, // TODO: we might have to use a real span here?
span: Span::COMPILER_GENERATED,
kind: ExprKind::Function(desugared_class),
},
TokenType::Assignment,
Expand All @@ -2166,21 +2158,22 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
};

for member in class.members {
if let ClassMemberKind::Method(method) = member.kind {
let name = method.name.expect("Class method did not have a name");

if let ClassMemberValue::Method(method) = member.value {
// Either Class.name or Class.prototype.name
let assign_target = Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::property_access(
false,
matches!(member.key, ClassMemberKey::Computed(_)),
match member.static_ {
true => load_class_binding.clone(),
false => class_prototype.clone(),
},
Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::identifier(name),
match member.key {
ClassMemberKey::Computed(ref expr) => expr.clone(),
ClassMemberKey::Named(name) => Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::identifier(name),
},
},
),
};
Expand Down
27 changes: 16 additions & 11 deletions crates/dash_compiler/src/transformations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use dash_middle::interner::sym;
use dash_middle::lexer::token::TokenType;
use dash_middle::parser::expr::{AssignmentExpr, AssignmentTarget, Expr, ExprKind, PropertyAccessExpr};
use dash_middle::parser::statement::{
BlockStatement, Class, ClassMemberKind, ClassProperty, Loop, ReturnStatement, Statement, StatementKind,
BlockStatement, Class, ClassMemberKey, ClassMemberValue, Loop, ReturnStatement, Statement, StatementKind,
};
use dash_middle::sourcemap::Span;

Expand Down Expand Up @@ -36,11 +36,7 @@ pub fn ast_insert_implicit_return(ast: &mut Vec<Statement>) {
pub fn insert_initializer_in_constructor(class: &Class, statements: &mut Vec<Statement>) {
let mut prestatements = Vec::new();
for member in &class.members {
if let ClassMemberKind::Property(ClassProperty {
name,
value: Some(value),
}) = &member.kind
{
if let ClassMemberValue::Field(field) = &member.value {
prestatements.push(Statement {
span: Span::COMPILER_GENERATED,
kind: StatementKind::Expression(Expr {
Expand All @@ -49,10 +45,13 @@ pub fn insert_initializer_in_constructor(class: &Class, statements: &mut Vec<Sta
left: AssignmentTarget::Expr(Box::new(Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::PropertyAccess(PropertyAccessExpr {
computed: false,
property: Box::new(Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::identifier(*name),
computed: matches!(member.key, ClassMemberKey::Computed(_)),
property: Box::new(match member.key {
ClassMemberKey::Computed(ref expr) => expr.clone(),
ClassMemberKey::Named(name) => Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::identifier(name),
},
}),
target: Box::new(Expr {
span: Span::COMPILER_GENERATED,
Expand All @@ -61,7 +60,13 @@ pub fn insert_initializer_in_constructor(class: &Class, statements: &mut Vec<Sta
}),
})),
operator: TokenType::Assignment,
right: Box::new(value.clone()),
right: Box::new(match field {
Some(field) => field.clone(),
None => Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::undefined_literal(),
},
}),
}),
}),
});
Expand Down
75 changes: 32 additions & 43 deletions crates/dash_middle/src/parser/statement.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::fmt;
use std::fmt::{self, Write};

use derive_more::Display;
#[cfg(feature = "serde")]
Expand Down Expand Up @@ -830,21 +830,30 @@ impl fmt::Display for Class {
}

impl Class {
/// Returns a reference to the constructor, if present
pub fn constructor(&self) -> Option<&FunctionDeclaration> {
self.members.iter().find_map(|cm| cm.as_constructor())
/// Returns the constructor, if present
pub fn constructor(&self) -> Option<FunctionDeclaration> {
self.members.iter().find_map(|cm| cm.as_constructor()).cloned()
}
}

#[derive(Debug, Clone)]
pub enum ClassMemberKey {
/// [Key] = Value
Computed(Expr),
/// Key = Value
Named(Symbol),
}

/// A JavaScript class member
#[derive(Debug, Clone)]
pub struct ClassMember {
/// Whether this class member is declared as static
pub static_: bool,
/// Whether this class member is declared as private
pub private: bool,
pub key: ClassMemberKey,
/// The type of class member
pub kind: ClassMemberKind,
pub value: ClassMemberValue,
}

impl fmt::Display for ClassMember {
Expand All @@ -857,7 +866,16 @@ impl fmt::Display for ClassMember {
write!(f, "private ")?;
}

write!(f, "{}", self.kind)
match &self.key {
ClassMemberKey::Computed(c) => write!(f, "[{c}]")?,
ClassMemberKey::Named(n) => write!(f, "{n}")?,
}

match &self.value {
ClassMemberValue::Method(method) => write!(f, "{method}"),
ClassMemberValue::Field(Some(field)) => write!(f, "= {field};"),
ClassMemberValue::Field(None) => f.write_char(';'),
}
}
}

Expand All @@ -869,50 +887,21 @@ impl ClassMember {
return None;
}

match &self.kind {
ClassMemberKind::Method(m) if m.name == Some(sym::constructor) => Some(m),
match &self.value {
ClassMemberValue::Method(m) if m.name == Some(sym::constructor) => Some(m),
_ => None,
}
}

/// Returns the identifier of this class member
pub fn name(&self) -> Symbol {
match &self.kind {
ClassMemberKind::Property(p) => p.name,
// Methods *always* have names, so unwrapping is OK here
ClassMemberKind::Method(m) => m.name.unwrap(),
}
}
}

/// The type of class member
#[derive(Debug, Clone, Display)]
pub enum ClassMemberKind {
/// The value of a class member
#[derive(Debug, Clone)]
pub enum ClassMemberValue {
/// A class method
Method(FunctionDeclaration),
/// A class property
Property(ClassProperty),
}

/// A class property
#[derive(Debug, Clone)]
pub struct ClassProperty {
/// The name of this property
pub name: Symbol,
/// The default value of this property, set when its constructor is called
pub value: Option<Expr>,
}

impl fmt::Display for ClassProperty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;

if let Some(value) = &self.value {
write!(f, " = {value}")?;
}

write!(f, ";")
}
/// A class field.
/// The value can be `None` for `class V { Key; }`
Field(Option<Expr>),
}

/// A function parameter
Expand Down
12 changes: 6 additions & 6 deletions crates/dash_optimizer/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use dash_middle::parser::expr::{
UnaryExpr,
};
use dash_middle::parser::statement::{
BlockStatement, Class, ClassMemberKind, ClassProperty, DoWhileLoop, ExportKind, ForInLoop, ForLoop, ForOfLoop,
FuncId, FunctionDeclaration, IfStatement, ImportKind, Loop, Parameter, ReturnStatement, SpecifierKind, Statement,
BlockStatement, Class, ClassMemberValue, DoWhileLoop, ExportKind, ForInLoop, ForLoop, ForOfLoop, FuncId,
FunctionDeclaration, IfStatement, ImportKind, Loop, Parameter, ReturnStatement, SpecifierKind, Statement,
StatementKind, SwitchCase, SwitchStatement, TryCatch, VariableBinding, VariableDeclaration,
VariableDeclarationKind, VariableDeclarations, WhileLoop,
};
Expand Down Expand Up @@ -116,12 +116,12 @@ impl<'b, 'interner> ConstFunctionEvalCtx<'b, 'interner> {
pub fn visit_class_statement(&mut self, Class { extends, members, .. }: &mut Class, func_id: FuncId) {
self.visit_maybe_expr(extends.as_mut(), func_id);
for member in members {
match &mut member.kind {
ClassMemberKind::Method(method) => {
match &mut member.value {
ClassMemberValue::Method(method) => {
self.visit_function_expression(method, func_id);
}
ClassMemberKind::Property(ClassProperty { value, .. }) => {
self.visit_maybe_expr(value.as_mut(), func_id);
ClassMemberValue::Field(field) => {
self.visit_maybe_expr(field.as_mut(), func_id);
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions crates/dash_optimizer/src/type_infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use dash_middle::parser::expr::{
UnaryExpr,
};
use dash_middle::parser::statement::{
BlockStatement, Class, ClassMemberKind, ClassProperty, DoWhileLoop, ExportKind, ForInLoop, ForLoop, ForOfLoop,
FuncId, FunctionDeclaration, IfStatement, ImportKind, Loop, Parameter, ReturnStatement, SpecifierKind, Statement,
BlockStatement, Class, ClassMemberValue, DoWhileLoop, ExportKind, ForInLoop, ForLoop, ForOfLoop, FuncId,
FunctionDeclaration, IfStatement, ImportKind, Loop, Parameter, ReturnStatement, SpecifierKind, Statement,
StatementKind, SwitchCase, SwitchStatement, TryCatch, VariableBinding, VariableDeclaration,
VariableDeclarationKind, VariableDeclarationName, VariableDeclarations, WhileLoop,
};
Expand Down Expand Up @@ -127,11 +127,9 @@ impl TypeInferCtx {
) {
self.visit_maybe_expr(extends.as_ref(), func_id);
for member in members {
match &member.kind {
ClassMemberKind::Method(method) => drop(self.visit_function_expression(method, func_id)),
ClassMemberKind::Property(ClassProperty { value, .. }) => {
drop(self.visit_maybe_expr(value.as_ref(), func_id))
}
match &member.value {
ClassMemberValue::Method(method) => drop(self.visit_function_expression(method, func_id)),
ClassMemberValue::Field(field) => drop(self.visit_maybe_expr(field.as_ref(), func_id)),
}
}

Expand Down
22 changes: 17 additions & 5 deletions crates/dash_parser/src/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use dash_middle::lexer::token::{TokenType, VARIABLE_TYPES};
use dash_middle::parser::error::Error;
use dash_middle::parser::expr::{Expr, ExprKind};
use dash_middle::parser::statement::{
Asyncness, BlockStatement, Catch, Class, ClassMember, ClassMemberKind, ClassProperty, DoWhileLoop, ExportKind,
Asyncness, BlockStatement, Catch, Class, ClassMember, ClassMemberKey, ClassMemberValue, DoWhileLoop, ExportKind,
ForInLoop, ForLoop, ForOfLoop, FunctionDeclaration, FunctionKind, IfStatement, ImportKind, Loop, Parameter,
ReturnStatement, SpecifierKind, Statement, StatementKind, SwitchCase, SwitchStatement, TryCatch, VariableBinding,
VariableDeclaration, VariableDeclarationKind, VariableDeclarationName, VariableDeclarations, WhileLoop,
Expand Down Expand Up @@ -79,7 +79,13 @@ impl<'a, 'interner> Parser<'a, 'interner> {
let is_static = self.expect_token_type_and_skip(&[TokenType::Static], false);
let is_private = self.expect_token_type_and_skip(&[TokenType::Hash], false);

let name = self.expect_identifier_or_reserved_kw(true)?;
let key = if self.expect_token_type_and_skip(&[TokenType::LeftSquareBrace], false) {
let expr = self.parse_expression()?;
self.expect_token_type_and_skip(&[TokenType::RightSquareBrace], true);
ClassMemberKey::Computed(expr)
} else {
ClassMemberKey::Named(self.expect_identifier_or_reserved_kw(true)?)
};

let is_method = self.expect_token_type_and_skip(&[TokenType::LeftParen], false);

Expand All @@ -98,7 +104,11 @@ impl<'a, 'interner> Parser<'a, 'interner> {

let func_id = self.function_counter.advance();
let func = FunctionDeclaration::new(
Some(name),
match key {
ClassMemberKey::Named(name) => Some(name),
// TODO: not correct, `class V { ['a']() {} }` should have its name set to 'a'
ClassMemberKey::Computed(_) => None,
},
func_id,
arguments,
vec![body],
Expand All @@ -109,7 +119,8 @@ impl<'a, 'interner> Parser<'a, 'interner> {
members.push(ClassMember {
private: is_private,
static_: is_static,
kind: ClassMemberKind::Method(func),
key,
value: ClassMemberValue::Method(func),
});
} else {
let kind = self.next()?.ty;
Expand All @@ -130,7 +141,8 @@ impl<'a, 'interner> Parser<'a, 'interner> {
members.push(ClassMember {
private: is_private,
static_: is_static,
kind: ClassMemberKind::Property(ClassProperty { name, value }),
key,
value: ClassMemberValue::Field(value),
});
};
}
Expand Down

0 comments on commit 8ba2f19

Please sign in to comment.