@samitouri / QOS-React-2 / commits / 8176f25719

[rust] ForStatement support

Implements support for `ForStatement` from swc -> estree -> hir, flushing out more of the HIR representation and porting pieces from HIRBuilder as necessary.

Joe Savona committed Jul 8, 2023 at 22:59 UTC 8176f25719da6f99be829f4e1400a10f34c6bff9
9 files changed +393 -76
compiler/forget/crates/build-hir/src/build.rs
+105 -13
@@ -1,14 +1,15 @@
1 use bumpalo::collections::{CollectIn, String};
2 use estree::{
3 - AssignmentTarget, ExpressionLike, FunctionDeclaration, IfStatement, Literal, LiteralValue,
4 - Pattern, Statement, VariableDeclarationKind,
3 + AssignmentTarget, BinaryExpression, ExpressionLike, ForInit, ForStatement, FunctionDeclaration,
4 + IfStatement, Literal, LiteralValue, Pattern, Statement, VariableDeclarationKind,
5 };
6 use hir::{
7 - ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionKind,
8 - InstructionValue, LValue, LoadGlobal, LoadLocal, Place, PrimitiveValue, TerminalValue,
7 + ArrayElement, BlockKind, BranchTerminal, Environment, ForTerminal, Function, GotoKind,
8 + Identifier, InstructionKind, InstructionValue, LValue, LoadGlobal, LoadLocal, Place,
9 + PrimitiveValue, TerminalValue,
10 };
11
11 -use crate::builder::{Binding, Builder};
12 +use crate::builder::{Binding, Builder, LoopScope};
13
14 /// Converts a React function in ESTree format into HIR. Returns the HIR
15 /// if it was constructed sucessfully, otherwise a list of diagnostics
@@ -35,7 +36,7 @@ pub fn build<'a>(
36 }),
37 );
38 builder.terminate(
38 - TerminalValue::ReturnTerminal(hir::ReturnTerminal {
39 + TerminalValue::Return(hir::ReturnTerminal {
40 value: implicit_return_value,
41 }),
42 hir::BlockKind::Block,
@@ -55,7 +56,7 @@ fn lower_statement<'a>(
56 env: &'a Environment<'a>,
57 builder: &mut Builder<'a>,
58 stmt: Statement,
58 - _label: Option<String<'a>>,
59 + label: Option<String<'a>>,
60 ) -> Result<(), Diagnostic> {
61 match stmt {
62 Statement::BlockStatement(stmt) => {
@@ -66,7 +67,7 @@ fn lower_statement<'a>(
67 Statement::BreakStatement(stmt) => {
68 let block = builder.resolve_break(stmt.label.as_ref())?;
69 builder.terminate(
69 - TerminalValue::GotoTerminal(hir::GotoTerminal {
70 + TerminalValue::Goto(hir::GotoTerminal {
71 block,
72 kind: GotoKind::Break,
73 }),
@@ -76,7 +77,7 @@ fn lower_statement<'a>(
77 Statement::ContinueStatement(stmt) => {
78 let block = builder.resolve_continue(stmt.label.as_ref())?;
79 builder.terminate(
79 - TerminalValue::GotoTerminal(hir::GotoTerminal {
80 + TerminalValue::Goto(hir::GotoTerminal {
81 block,
82 kind: GotoKind::Continue,
83 }),
@@ -95,7 +96,7 @@ fn lower_statement<'a>(
96 ),
97 };
98 builder.terminate(
98 - TerminalValue::ReturnTerminal(hir::ReturnTerminal { value }),
99 + TerminalValue::Return(hir::ReturnTerminal { value }),
100 BlockKind::Block,
101 );
102 }
@@ -158,7 +159,7 @@ fn lower_statement<'a>(
159
160 let consequent_block = builder.enter(BlockKind::Block, |builder| {
161 lower_statement(env, builder, consequent, None).unwrap();
161 - TerminalValue::GotoTerminal(hir::GotoTerminal {
162 + TerminalValue::Goto(hir::GotoTerminal {
163 block: fallthrough_block.id,
164 kind: GotoKind::Break,
165 })
@@ -168,14 +169,14 @@ fn lower_statement<'a>(
169 if let Some(alternate) = alternate {
170 lower_statement(env, builder, alternate, None).unwrap();
171 }
171 - TerminalValue::GotoTerminal(hir::GotoTerminal {
172 + TerminalValue::Goto(hir::GotoTerminal {
173 block: fallthrough_block.id,
174 kind: GotoKind::Break,
175 })
176 });
177
178 let test = lower_expression_to_temporary(env, builder, test);
178 - let terminal = TerminalValue::IfTerminal(hir::IfTerminal {
179 + let terminal = TerminalValue::If(hir::IfTerminal {
180 test,
181 consequent: consequent_block,
182 alternate: alternate_block,
@@ -183,6 +184,80 @@ fn lower_statement<'a>(
184 });
185 builder.terminate_with_fallthrough(terminal, fallthrough_block);
186 }
187 + Statement::ForStatement(stmt) => {
188 + let ForStatement {
189 + init,
190 + test,
191 + update,
192 + body,
193 + ..
194 + } = *stmt;
195 +
196 + // Block for the loop's test condition
197 + let test_block = builder.reserve(BlockKind::Loop);
198 +
199 + // Block for code following the loop
200 + let fallthrough_block = builder.reserve(BlockKind::Block);
201 +
202 + let init_block = builder.enter(BlockKind::Loop, |builder| {
203 + if let Some(ForInit::VariableDeclaration(decl)) = init {
204 + lower_statement(env, builder, Statement::VariableDeclaration(decl), None)
205 + .unwrap();
206 + TerminalValue::Goto(hir::GotoTerminal {
207 + block: test_block.id,
208 + kind: GotoKind::Break,
209 + })
210 + } else {
211 + panic!("Expected for statement to have a variable declaration initializer")
212 + }
213 + });
214 +
215 + let update_block = update.map(|update| {
216 + builder.enter(BlockKind::Loop, |builder| {
217 + lower_expression_to_temporary(env, builder, update);
218 + TerminalValue::Goto(hir::GotoTerminal {
219 + block: test_block.id,
220 + kind: GotoKind::Break,
221 + })
222 + })
223 + });
224 +
225 + let body_block = builder.enter(BlockKind::Block, |builder| {
226 + let loop_ = LoopScope {
227 + label,
228 + continue_block: update_block.unwrap_or(test_block.id),
229 + break_block: fallthrough_block.id,
230 + };
231 + builder.enter_loop(loop_, |builder| {
232 + lower_statement(env, builder, body, None).unwrap();
233 + TerminalValue::Goto(hir::GotoTerminal {
234 + block: update_block.unwrap_or(test_block.id),
235 + kind: GotoKind::Continue,
236 + })
237 + })
238 + });
239 +
240 + let terminal = TerminalValue::For(ForTerminal {
241 + body: body_block,
242 + init: init_block,
243 + test: test_block.id,
244 + fallthrough: fallthrough_block.id,
245 + update: update_block,
246 + });
247 + builder.terminate_with_fallthrough(terminal, test_block);
248 +
249 + if let Some(test) = test {
250 + let test_value = lower_expression_to_temporary(env, builder, test);
251 + let terminal = TerminalValue::Branch(BranchTerminal {
252 + test: test_value,
253 + consequent: body_block,
254 + alternate: fallthrough_block.id,
255 + });
256 + builder.terminate_with_fallthrough(terminal, fallthrough_block);
257 + } else {
258 + panic!("Expected for statement to have a tesst block");
259 + }
260 + }
261 _ => todo!("Lower {stmt:#?}"),
262 }
263 Ok(())
@@ -240,6 +315,7 @@ fn lower_expression<'a>(
315 .collect_in(env.allocator);
316 InstructionValue::Array(hir::Array { elements })
317 }
318 +
319 ExpressionLike::AssignmentExpression(expr) => match expr.operator {
320 estree::AssignmentOperator::Equals => {
321 let right = lower_expression_to_temporary(env, builder, expr.right);
@@ -248,6 +324,22 @@ fn lower_expression<'a>(
324 _ => todo!("lower assignment expr {:#?}", expr),
325 },
326
327 + ExpressionLike::BinaryExpression(expr) => {
328 + let BinaryExpression {
329 + left,
330 + operator,
331 + right,
332 + ..
333 + } = *expr;
334 + let left = lower_expression_to_temporary(env, builder, left);
335 + let right = lower_expression_to_temporary(env, builder, right);
336 + InstructionValue::Binary(hir::Binary {
337 + left,
338 + operator,
339 + right,
340 + })
341 + }
342 +
343 // Cases that cannot appear in expression position but which are included in ExpressionLike
344 // to make serialization easier
345 ExpressionLike::SpreadElement(_) => {
compiler/forget/crates/build-hir/src/builder.rs
+113 -20
@@ -1,4 +1,4 @@
1 -use bumpalo::collections::Vec;
1 +use bumpalo::collections::{String, Vec};
2 use std::{cell::RefCell, collections::HashSet, rc::Rc};
3
4 use hir::{
@@ -27,6 +27,8 @@ pub(crate) struct Builder<'a> {
27 wip: WipBlock<'a>,
28
29 id_gen: InstructionIdGenerator,
30 +
31 + scopes: Vec<'a, ControlFlowScope<'a>>,
32 }
33
34 pub(crate) struct WipBlock<'a> {
@@ -35,6 +37,48 @@ pub(crate) struct WipBlock<'a> {
37 pub instructions: Vec<'a, Instruction<'a>>,
38 }
39
40 +pub(crate) enum Binding<'a> {
41 + Local(Identifier<'a>),
42 + Module(Identifier<'a>),
43 + Global,
44 +}
45 +
46 +#[derive(Clone, PartialEq, Eq, Debug)]
47 +enum ControlFlowScope<'a> {
48 + Loop(LoopScope<'a>),
49 + // Switch(SwitchScope<'a>),
50 + Label(LabelScope<'a>),
51 +}
52 +
53 +#[derive(Clone, PartialEq, Eq, Debug)]
54 +pub(crate) struct LoopScope<'a> {
55 + pub label: Option<String<'a>>,
56 + pub continue_block: BlockId,
57 + pub break_block: BlockId,
58 +}
59 +
60 +#[derive(Clone, PartialEq, Eq, Debug)]
61 +pub(crate) struct LabelScope<'a> {
62 + pub label: String<'a>,
63 + pub block: BlockId,
64 +}
65 +
66 +impl<'a> ControlFlowScope<'a> {
67 + fn label(&self) -> Option<&String<'a>> {
68 + match self {
69 + Self::Loop(scope) => scope.label.as_ref(),
70 + Self::Label(scope) => Some(&scope.label),
71 + }
72 + }
73 +
74 + fn break_block(&self) -> BlockId {
75 + match self {
76 + Self::Loop(scope) => scope.break_block,
77 + Self::Label(scope) => scope.block,
78 + }
79 + }
80 +}
81 +
82 impl<'a> Builder<'a> {
83 pub(crate) fn new(environment: &'a Environment<'a>) -> Self {
84 let entry = environment.next_block_id();
@@ -49,6 +93,7 @@ impl<'a> Builder<'a> {
93 entry,
94 wip: current,
95 id_gen: InstructionIdGenerator::new(),
96 + scopes: Vec::new_in(&environment.allocator),
97 }
98 }
99
@@ -155,6 +200,17 @@ impl<'a> Builder<'a> {
200 );
201 }
202
203 + pub(crate) fn enter_loop<F>(&mut self, scope: LoopScope<'a>, f: F) -> TerminalValue<'a>
204 + where
205 + F: FnOnce(&mut Self) -> TerminalValue<'a>,
206 + {
207 + self.scopes.push(ControlFlowScope::Loop(scope.clone()));
208 + let terminal = f(self);
209 + let last = self.scopes.pop().unwrap();
210 + assert_eq!(last, ControlFlowScope::Loop(scope));
211 + terminal
212 + }
213 +
214 /// Returns a new temporary identifier
215 pub(crate) fn make_temporary(&self) -> hir::Identifier<'a> {
216 hir::Identifier {
@@ -173,9 +229,21 @@ impl<'a> Builder<'a> {
229 /// provided but cannot be resolved.
230 pub(crate) fn resolve_break(
231 &self,
176 - _label: Option<&estree::Identifier>,
232 + label: Option<&estree::Identifier>,
233 ) -> Result<BlockId, Diagnostic> {
178 - todo!()
234 + for scope in self.scopes.iter().rev() {
235 + match (label, scope.label()) {
236 + // If this is an unlabeled break, return the most recent break target
237 + (None, _) => return Ok(scope.break_block()),
238 + // If the break is labeled and matches the current scope, return its break target
239 + (Some(label), Some(scope_label)) if &label.name == scope_label => {
240 + return Ok(scope.break_block());
241 + }
242 + // Otherwise keep searching
243 + _ => continue,
244 + }
245 + }
246 + Err(())
247 }
248
249 /// Resolves the target for the given continue label (if present), or returns the default
@@ -183,9 +251,36 @@ impl<'a> Builder<'a> {
251 /// provided but cannot be resolved.
252 pub(crate) fn resolve_continue(
253 &self,
186 - _label: Option<&estree::Identifier>,
254 + label: Option<&estree::Identifier>,
255 ) -> Result<BlockId, Diagnostic> {
188 - todo!()
256 + for scope in self.scopes.iter().rev() {
257 + match scope {
258 + ControlFlowScope::Loop(scope) => {
259 + match (label, &scope.label) {
260 + // If this is an unlabeled continue, return the first matching loop
261 + (None, _) => return Ok(scope.continue_block),
262 + // If the continue is labeled and matches the current scope, return its continue target
263 + (Some(label), Some(scope_label))
264 + if label.name.as_str() == scope_label.as_str() =>
265 + {
266 + return Ok(scope.continue_block);
267 + }
268 + // Otherwise keep searching
269 + _ => continue,
270 + }
271 + }
272 + _ => {
273 + match (label, scope.label()) {
274 + (Some(label), Some(scope_label)) if label.name.as_str() == scope_label => {
275 + // Error, the continue referred to a label that is not a loop
276 + return Err(());
277 + }
278 + _ => continue,
279 + }
280 + }
281 + }
282 + }
283 + Err(())
284 }
285
286 pub(crate) fn resolve_binding(
@@ -206,12 +301,6 @@ impl<'a> Builder<'a> {
301 }
302 }
303
209 -pub(crate) enum Binding<'a> {
210 - Local(Identifier<'a>),
211 - Module(Identifier<'a>),
212 - Global,
213 -}
214 -
304 /// Modifies the HIR to put the blocks in reverse postorder, with predecessors before
305 /// successors (except for the case of loops)
306 fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) {
@@ -230,20 +319,24 @@ fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) {
319 let block = hir.block(block_id);
320 let terminal = &block.terminal;
321 match &terminal.value {
233 - TerminalValue::IfTerminal(terminal) => {
322 + TerminalValue::Branch(terminal) => {
323 + visit(terminal.alternate, hir, visited, postorder);
324 + visit(terminal.consequent, hir, visited, postorder);
325 + }
326 + TerminalValue::If(terminal) => {
327 visit(terminal.alternate, hir, visited, postorder);
328 visit(terminal.consequent, hir, visited, postorder);
329 }
237 - TerminalValue::ForTerminal(terminal) => {
330 + TerminalValue::For(terminal) => {
331 visit(terminal.init, hir, visited, postorder);
332 }
240 - TerminalValue::DoWhileTerminal(terminal) => {
333 + TerminalValue::DoWhile(terminal) => {
334 visit(terminal.body, hir, visited, postorder);
335 }
243 - TerminalValue::GotoTerminal(terminal) => {
336 + TerminalValue::Goto(terminal) => {
337 visit(terminal.block, hir, visited, postorder);
338 }
246 - TerminalValue::ReturnTerminal(..) => { /* no-op */ }
339 + TerminalValue::Return(..) => { /* no-op */ }
340 }
341 postorder.push(block_id);
342 }
@@ -263,7 +356,7 @@ fn remove_unreachable_for_updates<'a>(hir: &mut HIR<'a>) {
356 let block_ids: HashSet<BlockId> = hir.blocks.keys().cloned().collect();
357
358 for block in hir.blocks.values_mut() {
266 - if let TerminalValue::ForTerminal(terminal) = &mut block.terminal.value {
359 + if let TerminalValue::For(terminal) = &mut block.terminal.value {
360 if let Some(update) = terminal.update {
361 if !block_ids.contains(&update) {
362 terminal.update = None;
@@ -297,9 +390,9 @@ fn remove_unreachable_do_while_statements<'a>(hir: &mut HIR<'a>) {
390 let block_ids: HashSet<BlockId> = hir.blocks.keys().cloned().collect();
391
392 for block in hir.blocks.values_mut() {
300 - if let TerminalValue::DoWhileTerminal(terminal) = &mut block.terminal.value {
393 + if let TerminalValue::DoWhile(terminal) = &mut block.terminal.value {
394 if !block_ids.contains(&terminal.test) {
302 - block.terminal.value = TerminalValue::GotoTerminal(hir::GotoTerminal {
395 + block.terminal.value = TerminalValue::Goto(hir::GotoTerminal {
396 block: terminal.body,
397 kind: GotoKind::Break,
398 });
@@ -353,7 +446,7 @@ fn mark_predecessors<'a>(hir: &mut HIR<'a>) {
446
447 fn invariant<F>(cond: bool, f: F) -> Result<(), Diagnostic>
448 where
356 - F: FnOnce() -> String,
449 + F: FnOnce() -> std::string::String,
450 {
451 if !cond {
452 let msg = f();
compiler/forget/crates/estree-swc/src/lib.rs
+34 -13
@@ -6,8 +6,8 @@ use swc_core::common::errors::Handler;
6 use swc_core::common::source_map::Pos;
7 use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS};
8 use swc_core::ecma::ast::{
9 - AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, MemberExpr, ModuleItem, Pat,
10 - PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr,
9 + AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, MemberExpr, MemberProp,
10 + ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr,
11 };
12 use swc_core::ecma::parser::Syntax;
13 use swc_core::ecma::transforms::base::resolver;
@@ -366,6 +366,9 @@ fn convert_expression(cx: &Context, expr: &Expr) -> estree::ExpressionLike {
366 range: convert_span(&expr.span),
367 }))
368 }
369 + Expr::Member(expr) => {
370 + estree::ExpressionLike::MemberExpression(Box::new(convert_member_expression(cx, expr)))
371 + }
372 _ => todo!("translate expression {:#?}", expr),
373 }
374 }
@@ -376,22 +379,40 @@ fn convert_assignment_target(cx: &Context, target: &PatOrExpr) -> estree::Assign
379 estree::AssignmentTarget::Pattern(Box::new(convert_pattern(cx, target)))
380 }
381 PatOrExpr::Expr(target) => {
379 - if let Expr::Member(target) = target.as_ref() {
380 - estree::AssignmentTarget::MemberExpression(Box::new(convert_member_expression(
381 - cx, target,
382 - )))
383 - } else {
384 - panic!(
385 - "Invalid input, expected either a pattern or member expression, got {:#?}",
386 - target
387 - )
382 + match target.as_ref() {
383 + Expr::Member(target) => estree::AssignmentTarget::MemberExpression(Box::new(
384 + convert_member_expression(cx, target),
385 + )),
386 + Expr::Ident(target) => estree::AssignmentTarget::Pattern(Box::new(
387 + estree::Pattern::Identifier(Box::new(convert_identifier(cx, target))),
388 + )),
389 + _ => {
390 + panic!("Expected assignment target to be member expression or identifier, got {:#?}", target)
391 + }
392 }
393 }
394 }
395 }
396
393 -fn convert_member_expression(_cx: &Context, _expr: &MemberExpr) -> estree::MemberExpression {
394 - todo!("convert member expression")
397 +fn convert_member_expression(cx: &Context, expr: &MemberExpr) -> estree::MemberExpression {
398 + let (is_computed, property) = match &expr.prop {
399 + MemberProp::Ident(prop) => (
400 + false,
401 + estree::ExpressionLike::Identifier(Box::new(convert_identifier(cx, prop))),
402 + ),
403 + MemberProp::Computed(prop) => (true, convert_expression(cx, &prop.expr)),
404 + _ => {
405 + panic!("PrivateName member expression properties are not supported")
406 + }
407 + };
408 + estree::MemberExpression {
409 + object: convert_expression(cx, &expr.obj),
410 + property,
411 + is_computed,
412 + is_optional: false,
413 + loc: None,
414 + range: convert_span(&expr.span),
415 + }
416 }
417
418 fn convert_unary_operator(op: UnaryOp) -> estree::UnaryOperator {
compiler/forget/crates/estree/src/lib.rs
+12 -1
@@ -1,6 +1,6 @@
1 use serde::{Deserialize, Serialize};
2 use static_assertions::assert_eq_size;
3 -use std::num::NonZeroU32;
3 +use std::{fmt::Display, num::NonZeroU32};
4
5 #[derive(Serialize, Deserialize, Debug)]
6 pub struct SourceLocation {
@@ -689,6 +689,17 @@ pub enum BinaryOperator {
689 Instanceof,
690 }
691
692 +impl Display for BinaryOperator {
693 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
694 + let name = match self {
695 + Self::Plus => "+",
696 + Self::LessThan => "<",
697 + _ => todo!("display for operator: {:#?}", self),
698 + };
699 + f.write_str(name)
700 + }
701 +}
702 +
703 #[derive(Serialize, Deserialize, Debug)]
704 pub struct CallExpression {
705 #[serde(rename = "optional")]
compiler/forget/crates/fixtures/tests/fixtures/for-statement.js new
+7
@@ -0,0 +1,7 @@
1 +function foo() {
2 + let x = 0;
3 + for (let i = 0; i < 10; i = i + 1) {
4 + x = x + i;
5 + }
6 + return x;
7 +}
compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@for-statement.js.snap new
+46
@@ -0,0 +1,46 @@
1 +---
2 +source: crates/fixtures/tests/fixtures_test.rs
3 +expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
4 +input_file: crates/fixtures/tests/fixtures/for-statement.js
5 +---
6 +Input:
7 +function foo() {
8 + let x = 0;
9 + for (let i = 0; i < 10; i = i + 1) {
10 + x = x + i;
11 + }
12 + return x;
13 +}
14 +
15 +
16 +Output:
17 +entry bb0
18 +bb0
19 + [0] unknown $0 = 0
20 + [1] unknown $2 = StoreLocal Let unknown x$1 = unknown $0
21 + [2] For init=bb3 test=bb1 update=bb4 body=bb5 fallthrough=bb2
22 +bb3
23 + [3] unknown $3 = 0
24 + [4] unknown $5 = StoreLocal Let unknown i$4 = unknown $3
25 + [5] Goto bb1
26 +bb1
27 + [6] unknown $14 = LoadLocal unknown i$4
28 + [7] unknown $15 = 10
29 + [8] unknown $16 = Binary unknown $14 < unknown $15
30 + [9] Branch unknown $16 consequent=bb5 alternate=bb2
31 +bb5
32 + [10] unknown $10 = LoadLocal unknown x$1
33 + [11] unknown $11 = LoadLocal unknown i$4
34 + [12] unknown $12 = Binary unknown $10 + unknown $11
35 + [13] unknown $13 = StoreLocal Reassign unknown x$1 = unknown $12
36 + [14] Goto bb4
37 +bb4
38 + [15] unknown $6 = LoadLocal unknown i$4
39 + [16] unknown $7 = 1
40 + [17] unknown $8 = Binary unknown $6 + unknown $7
41 + [18] unknown $9 = StoreLocal Reassign unknown i$4 = unknown $8
42 + [19] Goto bb1
43 +bb2
44 + [20] unknown $17 = LoadLocal unknown x$1
45 + [21] Return unknown $17
46 +
compiler/forget/crates/hir/src/instruction.rs
+9 -1
@@ -1,6 +1,7 @@
1 use std::{cell::RefCell, fmt::Display, rc::Rc};
2
3 use bumpalo::collections::{String, Vec};
4 +use estree::BinaryOperator;
5
6 use crate::{IdentifierId, InstructionId, ScopeId, Type};
7
@@ -15,7 +16,7 @@ pub struct Instruction<'a> {
16 pub enum InstructionValue<'a> {
17 Array(Array<'a>),
18 // Await(Await<'a>),
18 - // Binary(Binary<'a>),
19 + Binary(Binary<'a>),
20 // Call(Call<'a>),
21 // ComputedDelete(ComputedDelete<'a>),
22 // ComputedLoad(ComputedLoad<'a>),
@@ -59,6 +60,13 @@ pub enum ArrayElement<'a> {
60 Spread(Place<'a>),
61 }
62
63 +#[derive(Debug)]
64 +pub struct Binary<'a> {
65 + pub left: Place<'a>,
66 + pub operator: BinaryOperator,
67 + pub right: Place<'a>,
68 +}
69 +
70 #[derive(Debug)]
71 pub struct Primitive<'a> {
72 pub value: PrimitiveValue<'a>,
compiler/forget/crates/hir/src/print.rs
+32 -3
@@ -90,6 +90,12 @@ impl<'a> Print for InstructionValue<'a> {
90 write!(out, "DeclareLocal ")?;
91 value.lvalue.print(out)?;
92 }
93 + InstructionValue::Binary(value) => {
94 + write!(out, "Binary ")?;
95 + value.left.print(out)?;
96 + write!(out, " {} ", value.operator)?;
97 + value.right.print(out)?;
98 + }
99 _ => write!(out, "{:?}", self)?,
100 }
101 Ok(())
@@ -146,14 +152,14 @@ impl<'a> Print for Terminal<'a> {
152 impl<'a> Print for TerminalValue<'a> {
153 fn print(&self, out: &mut impl Write) -> Result {
154 match self {
149 - TerminalValue::ReturnTerminal(terminal) => {
155 + TerminalValue::Return(terminal) => {
156 write!(out, "Return ")?;
157 terminal.value.print(out)?;
158 }
153 - TerminalValue::GotoTerminal(terminal) => {
159 + TerminalValue::Goto(terminal) => {
160 write!(out, "Goto {}", terminal.block)?;
161 }
156 - TerminalValue::IfTerminal(terminal) => {
162 + TerminalValue::If(terminal) => {
163 write!(out, "If ")?;
164 terminal.test.print(out)?;
165 write!(
@@ -167,6 +173,29 @@ impl<'a> Print for TerminalValue<'a> {
173 }
174 )?;
175 }
176 + TerminalValue::Branch(terminal) => {
177 + write!(out, "Branch ")?;
178 + terminal.test.print(out)?;
179 + write!(
180 + out,
181 + " consequent={} alternate={}",
182 + terminal.consequent, terminal.alternate,
183 + )?;
184 + }
185 + TerminalValue::For(terminal) => {
186 + write!(
187 + out,
188 + "For init={} test={} update={} body={} fallthrough={}",
189 + terminal.init,
190 + terminal.test,
191 + match terminal.update {
192 + Some(fallthrough) => format!("{fallthrough}"),
193 + None => "<none>".to_string(),
194 + },
195 + terminal.body,
196 + terminal.fallthrough,
197 + )?;
198 + }
199 _ => write!(out, "{:?}", self)?,
200 }
201 Ok(())
compiler/forget/crates/hir/src/terminal.rs
+35 -25
@@ -10,22 +10,22 @@ pub struct Terminal<'a> {
10
11 #[derive(Debug)]
12 pub enum TerminalValue<'a> {
13 - // BranchTerminal(BranchTerminal),
14 - DoWhileTerminal(DoWhileTerminal),
15 - // ForOfTerminal(ForOfTerminal),
16 - ForTerminal(ForTerminal),
17 - GotoTerminal(GotoTerminal),
18 - IfTerminal(IfTerminal<'a>),
19 - // LabelTerminal(LabelTerminal),
20 - // LogicalTerminal(LogicalTerminal),
21 - // OptionalTerminal(OptionalTerminal),
22 - ReturnTerminal(ReturnTerminal<'a>),
23 - // SequenceTerminal(SequenceTerminal),
24 - // SwitchTerminal(SwitchTerminal),
25 - // TernaryTerminal(TernaryTerminal),
26 - // ThrowTerminal(ThrowTerminal),
27 - // UnsupportedTerminal(UnsupportedTerminal),
28 - // WhileTerminal(WhileTerminal),
13 + Branch(BranchTerminal<'a>),
14 + DoWhile(DoWhileTerminal),
15 + // ForOf(ForOfTerminal),
16 + For(ForTerminal),
17 + Goto(GotoTerminal),
18 + If(IfTerminal<'a>),
19 + // Label(LabelTerminal),
20 + // Logical(LogicalTerminal),
21 + // Optional(OptionalTerminal),
22 + Return(ReturnTerminal<'a>),
23 + // Sequence(SequenceTerminal),
24 + // Switch(SwitchTerminal),
25 + // Ternary(TernaryTerminal),
26 + // Throw(ThrowTerminal),
27 + // Unsupported(UnsupportedTerminal),
28 + // While(WhileTerminal),
29 }
30
31 impl<'a> TerminalValue<'a> {
@@ -34,43 +34,53 @@ impl<'a> TerminalValue<'a> {
34 F: Fn(BlockId) -> Option<BlockId>,
35 {
36 match self {
37 - Self::IfTerminal(terminal) => {
37 + Self::If(terminal) => {
38 terminal.fallthrough = match terminal.fallthrough {
39 Some(fallthrough) => f(fallthrough),
40 _ => None,
41 }
42 }
43 - Self::DoWhileTerminal(DoWhileTerminal { fallthrough, .. })
44 - | Self::ForTerminal(ForTerminal { fallthrough, .. }) => {
43 + Self::DoWhile(DoWhileTerminal { fallthrough, .. })
44 + | Self::For(ForTerminal { fallthrough, .. }) => {
45 // statically detect if fallthrough is changed to Option so
46 // that we can update to map the fallthrough w f()
47 let _: BlockId = *fallthrough;
48 }
49 - Self::GotoTerminal(_) | Self::ReturnTerminal(_) => {}
49 + Self::Branch(_) | Self::Goto(_) | Self::Return(_) => {}
50 }
51 }
52
53 pub fn successors(&self) -> Vec<BlockId> {
54 match self {
55 - Self::IfTerminal(terminal) => {
55 + Self::If(terminal) => {
56 vec![terminal.consequent, terminal.alternate]
57 }
58 - Self::ForTerminal(terminal) => {
58 + Self::Branch(terminal) => {
59 + vec![terminal.consequent, terminal.alternate]
60 + }
61 + Self::For(terminal) => {
62 vec![terminal.init]
63 }
61 - Self::DoWhileTerminal(terminal) => {
64 + Self::DoWhile(terminal) => {
65 vec![terminal.body]
66 }
64 - Self::GotoTerminal(terminal) => {
67 + Self::Goto(terminal) => {
68 vec![terminal.block]
69 }
67 - Self::ReturnTerminal(_) => {
70 + Self::Return(_) => {
71 vec![]
72 }
73 }
74 }
75 }
76
77 +#[derive(Debug)]
78 +pub struct BranchTerminal<'a> {
79 + pub test: Place<'a>,
80 + pub consequent: BlockId,
81 + pub alternate: BlockId,
82 +}
83 +
84 #[derive(Debug)]
85 pub struct GotoTerminal {
86 pub block: BlockId,