Skip to content

Commit

Permalink
support class expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
y21 committed May 5, 2024
1 parent 5d7c32b commit 5b4613a
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 21 deletions.
12 changes: 6 additions & 6 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ dash_node_impl = { path = "../crates/dash_node_impl", optional = true, features
"fetch",
] }
dash_rt_modules = { path = "../crates/dash_rt_modules", features = [
# "http",
# "fs",
# "fetch",
# "modules",
# "timers",
# "dll",
"http",
"fs",
"fetch",
"modules",
"timers",
"dll",
] }
tracing-subscriber = "0.3.15"
tracing = "0.1.36"
Expand Down
26 changes: 17 additions & 9 deletions crates/dash_compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
ExprKind::Postfix(e) => self.visit_postfix_expr(span, e),
ExprKind::Prefix(e) => self.visit_prefix_expr(span, e),
ExprKind::Function(e) => self.visit_function_expr(span, e),
ExprKind::Class(e) => self.visit_class_expr(span, e),
ExprKind::Array(e) => self.visit_array_literal(span, e),
ExprKind::Object(e) => self.visit_object_literal(span, e),
ExprKind::Compiled(mut buf) => {
Expand Down Expand Up @@ -2061,10 +2062,10 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
Ok(())
}

fn visit_class_declaration(&mut self, span: Span, class: Class) -> Result<(), Error> {
fn visit_class_expr(&mut self, span: Span, class: Class) -> Result<(), Error> {
let mut ib = InstructionBuilder::new(self);

let load_super_class = match &class.extends {
let load_super_class = match class.extends.as_deref() {
Some(expr) => {
let extend_id = ib
.current_scope_mut()
Expand Down Expand Up @@ -2124,17 +2125,17 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
constructor_initializers: Some(fields.clone().filter(|member| !member.static_).cloned().collect()),
};

ib.visit_assignment_expression(
span,
AssignmentExpr::new_local_place(
ib.visit_expression_statement(Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::Assignment(AssignmentExpr::new_local_place(
binding_id,
Expr {
span,
kind: ExprKind::Function(desugared_class),
},
TokenType::Assignment,
),
)?;
)),
})?;
let load_class_binding = Expr {
span: Span::COMPILER_GENERATED,
kind: ExprKind::Compiled(compile_local_load(binding_id, false)),
Expand Down Expand Up @@ -2172,8 +2173,6 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
ib.accept_expr(load_class_binding.clone())?;
ib.build_object_member_like_instruction(span, static_fields, Instruction::AssignProperties)?;

ib.build_pop();

if let Some(super_id) = load_super_class {
// Add the superclass' prototype to our prototype chain
// Class.prototype.__proto__ = Superclass.prototype
Expand Down Expand Up @@ -2229,6 +2228,15 @@ impl<'interner> Visitor<Result<(), Error>> for FunctionCompiler<'interner> {
})?;
}

// Load it one last time since the `class` expression ultimately should evaluate to that class
ib.accept_expr(load_class_binding)?;

Ok(())
}

fn visit_class_declaration(&mut self, span: Span, class: Class) -> Result<(), Error> {
self.visit_class_expr(span, class)?;
InstructionBuilder::new(self).build_pop();
Ok(())
}

Expand Down
4 changes: 3 additions & 1 deletion crates/dash_middle/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::interner::{sym, Symbol};
use crate::lexer::token::TokenType;
use crate::sourcemap::Span;

use super::statement::{fmt_list, FunctionDeclaration};
use super::statement::{fmt_list, Class, FunctionDeclaration};

/// The sequence operator (`expr, expr`)
pub type Seq = (Box<Expr>, Box<Expr>);
Expand Down Expand Up @@ -47,6 +47,8 @@ pub enum ExprKind {
///
/// This includes both normal functions and arrow functions
Function(FunctionDeclaration),
/// A `class {}` expression
Class(Class),
/// An array literal expression
Array(ArrayLiteral),
/// An object literal expression
Expand Down
2 changes: 1 addition & 1 deletion crates/dash_middle/src/parser/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ pub struct Class {
/// Class expressions don't necessarily need to have a name
pub name: Option<Symbol>,
/// The superclass of this class, if present
pub extends: Option<Expr>,
pub extends: Option<Box<Expr>>,
/// Members of this class
pub members: Vec<ClassMember>,
}
Expand Down
3 changes: 3 additions & 0 deletions crates/dash_middle/src/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ pub trait Visitor<V> {
/// Visits a function expression
fn visit_function_expr(&mut self, span: Span, f: FunctionDeclaration) -> V;

fn visit_class_expr(&mut self, span: Span, c: Class) -> V;

/// Visits an array literal
fn visit_array_literal(&mut self, span: Span, a: ArrayLiteral) -> V;

Expand Down Expand Up @@ -181,6 +183,7 @@ where
ExprKind::Postfix(e) => this.visit_postfix_expr(span, e),
ExprKind::Prefix(e) => this.visit_prefix_expr(span, e),
ExprKind::Function(e) => this.visit_function_expr(span, e),
ExprKind::Class(e) => this.visit_class_expr(span, e),
ExprKind::Array(e) => this.visit_array_literal(span, e),
ExprKind::Object(e) => this.visit_object_literal(span, e),
ExprKind::Compiled(..) => on_empty(this),
Expand Down
3 changes: 2 additions & 1 deletion crates/dash_optimizer/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ 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);
self.visit_maybe_expr(extends.as_deref_mut(), func_id);
for member in members {
match &mut member.value {
ClassMemberValue::Method(method)
Expand Down Expand Up @@ -230,6 +230,7 @@ impl<'b, 'interner> ConstFunctionEvalCtx<'b, 'interner> {
ExprKind::Prefix(..) => self.visit_prefix_expression(expression, func_id),
ExprKind::Postfix(..) => self.visit_postfix_expression(expression, func_id),
ExprKind::Function(expr) => self.visit_function_expression(expr, func_id),
ExprKind::Class(class) => self.visit_class_statement(class, func_id),
ExprKind::Array(..) => self.visit_array_expression(expression, func_id),
ExprKind::Object(..) => self.visit_object_expression(expression, func_id),
ExprKind::Compiled(..) => {}
Expand Down
8 changes: 7 additions & 1 deletion crates/dash_optimizer/src/type_infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl TypeInferCtx {
}: &Class,
func_id: FuncId,
) {
self.visit_maybe_expr(extends.as_ref(), func_id);
self.visit_maybe_expr(extends.as_deref(), func_id);
for member in members {
match &member.value {
ClassMemberValue::Method(method)
Expand Down Expand Up @@ -258,6 +258,7 @@ impl TypeInferCtx {
ExprKind::Prefix((tt, expr)) => self.visit_prefix_expression(expr, *tt, func_id),
ExprKind::Postfix((tt, expr)) => self.visit_postfix_expression(expr, *tt, func_id),
ExprKind::Function(expr) => self.visit_function_expression(expr, func_id),
ExprKind::Class(class) => self.visit_class_expression(class, func_id),
ExprKind::Array(expr) => self.visit_array_expression(expr, func_id),
ExprKind::Object(expr) => self.visit_object_expression(expr, func_id),
ExprKind::Compiled(..) => None,
Expand Down Expand Up @@ -290,6 +291,11 @@ impl TypeInferCtx {
}
}

fn visit_class_expression(&mut self, class: &Class, func_id: FuncId) -> Option<CompileValueType> {
self.visit_class_statement(class, func_id);
None
}

pub fn visit_grouping_expression(
&mut self,
GroupingExpr(expression): &GroupingExpr,
Expand Down
8 changes: 8 additions & 0 deletions crates/dash_parser/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,14 @@ impl<'a, 'interner> Parser<'a, 'interner> {
span,
kind: ExprKind::function(f),
})?,
TokenType::Class => {
let class = self.parse_class()?;
let rbrace = self.previous().unwrap().span;
Expr {
span: current.span.to(rbrace),
kind: ExprKind::Class(class),
}
}
TokenType::RegexLiteral { literal, flags } => {
// Trim / prefix and suffix
let full = self.interner.resolve(literal);
Expand Down
4 changes: 2 additions & 2 deletions crates/dash_parser/src/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ impl<'a, 'interner> Parser<'a, 'interner> {
})
}

fn parse_class(&mut self) -> Option<Class> {
pub fn parse_class(&mut self) -> Option<Class> {
let name = self.expect_identifier(false);

let extends = if self.expect_token_type_and_skip(&[TokenType::Extends], false) {
Some(self.parse_expression()?)
Some(Box::new(self.parse_expression()?))
} else {
None
};
Expand Down
4 changes: 4 additions & 0 deletions crates/dash_vm/src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,10 @@ simple_test!(
assert(new C8().get() === 1);
assert(new C8().set() === 2);
assert(new C8().a === 3);
assert(new (class { constructor() { return [42] } })()[0] == 42);
let v = class V {};
assert(v === V);
"#,
Value::undefined()
);

0 comments on commit 5b4613a

Please sign in to comment.