| ... | @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; | ... | @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; |
| 2 | use std::vec; | 2 | use std::vec; |
| 3 | | 3 | |
| 4 | use ast::{BinaryOperator, BlockOrIf, BlockWithLoc, Expr, Statement}; | 4 | use ast::{BinaryOperator, BlockOrIf, BlockWithLoc, Expr, Statement}; |
| | 5 | use has_side_effects::HasSideEffects; |
| 5 | use tokenizer::Loc; | 6 | use tokenizer::Loc; |
| 6 | | 7 | |
| 7 | use crate::*; | 8 | use crate::*; |
| ... | @@ -156,9 +157,7 @@ impl HoistContext { | ... | @@ -156,9 +157,7 @@ impl HoistContext { |
| 156 | pub fn process_statement(&mut self, statement: Statement) -> Option<()> { | 157 | pub fn process_statement(&mut self, statement: Statement) -> Option<()> { |
| 157 | if statement.is_valid_js_expression() { | 158 | if statement.is_valid_js_expression() { |
| 158 | let result = convert_valid_statement(self, statement); | 159 | let result = convert_valid_statement(self, statement); |
| 159 | if !result.is_literal() { | 160 | self.add_statement(result); |
| 160 | self.add_statement(result); | | |
| 161 | } | | |
| 162 | return Some(()); | 161 | return Some(()); |
| 163 | } | 162 | } |
| 164 | | 163 | |
| ... | @@ -189,13 +188,11 @@ impl HoistContext { | ... | @@ -189,13 +188,11 @@ impl HoistContext { |
| 189 | Statement::Expr(expr) => { | 188 | Statement::Expr(expr) => { |
| 190 | if let Some(expr) = self.hoist_expression_contents(expr) { | 189 | if let Some(expr) = self.hoist_expression_contents(expr) { |
| 191 | let loc = expr.loc.clone(); | 190 | let loc = expr.loc.clone(); |
| 192 | if !expr.is_literal() { | 191 | self.add_statement(JsStatement::new( |
| 193 | self.add_statement(JsStatement::new( | 192 | JsStatementData::Expression(expr), |
| 194 | JsStatementData::Expression(expr), | 193 | loc, |
| 195 | loc, | 194 | None, |
| 196 | None, | 195 | )); |
| 197 | )) | | |
| 198 | } | | |
| 199 | Some(()) | 196 | Some(()) |
| 200 | } else { | 197 | } else { |
| 201 | None | 198 | None |
| ... | @@ -209,9 +206,9 @@ impl HoistContext { | ... | @@ -209,9 +206,9 @@ impl HoistContext { |
| 209 | /// and returns an identifier expression for it. | 206 | /// and returns an identifier expression for it. |
| 210 | pub fn hoist_expression_to_temporary(&mut self, expr: Expr) -> Option<JsExpr> { | 207 | pub fn hoist_expression_to_temporary(&mut self, expr: Expr) -> Option<JsExpr> { |
| 211 | // TODO | 208 | // TODO |
| 212 | // if !expr.has_side_effects() { | 209 | if !expr.has_side_effects() { |
| 213 | // return Some(self.hoist_expression_contents(expr)?); | 210 | return Some(self.hoist_expression_contents(expr)?); |
| 214 | // } | 211 | } |
| 215 | | 212 | |
| 216 | let init = self.hoist_expression_contents(expr)?; | 213 | let init = self.hoist_expression_contents(expr)?; |
| 217 | | 214 | |
| ... | @@ -334,7 +331,7 @@ impl HoistContext { | ... | @@ -334,7 +331,7 @@ impl HoistContext { |
| 334 | let mut did_assign = false; | 331 | let mut did_assign = false; |
| 335 | | 332 | |
| 336 | let js_if = | 333 | let js_if = |
| 337 | self.hoist_js_if(data.yes, data.no, condition, &mut did_assign, &temporary)?; | 334 | self.hoist_js_if(data.yes, data.no, condition, &mut did_assign, &temporary); |
| 338 | | 335 | |
| 339 | self.add_statement(JsStatement::new( | 336 | self.add_statement(JsStatement::new( |
| 340 | JsStatementData::If(js_if), | 337 | JsStatementData::If(js_if), |
| ... | @@ -396,7 +393,7 @@ impl HoistContext { | ... | @@ -396,7 +393,7 @@ impl HoistContext { |
| 396 | b: BlockWithLoc, | 393 | b: BlockWithLoc, |
| 397 | temporary: &String, | 394 | temporary: &String, |
| 398 | did_assign: &mut bool, | 395 | did_assign: &mut bool, |
| 399 | ) -> Option<JsBlock> { | 396 | ) -> JsBlock { |
| 400 | let loc = b.loc.clone(); | 397 | let loc = b.loc.clone(); |
| 401 | | 398 | |
| 402 | // A non-block will become a block with a single expression: | 399 | // A non-block will become a block with a single expression: |
| ... | @@ -405,21 +402,22 @@ impl HoistContext { | ... | @@ -405,21 +402,22 @@ impl HoistContext { |
| 405 | // We will also apply this transformation to blocks that are valid | 402 | // We will also apply this transformation to blocks that are valid |
| 406 | // expressions, because that is more concise. | 403 | // expressions, because that is more concise. |
| 407 | if b.is_valid_js_expression() { | 404 | if b.is_valid_js_expression() { |
| 408 | return Some(JsBlock::new( | 405 | return JsBlock::new( |
| 409 | vec![new_js_assignment_statement( | 406 | vec![new_js_assignment_statement( |
| 410 | convert_valid_block(self, b), | 407 | convert_valid_block(self, b), |
| 411 | temporary.clone(), | 408 | temporary.clone(), |
| 412 | loc.clone(), | 409 | loc.clone(), |
| 413 | )], | 410 | )], |
| 414 | loc, | 411 | loc, |
| 415 | )); | 412 | ); |
| 416 | } | 413 | } |
| 417 | | 414 | |
| 418 | let mut child = self.new_child(); | 415 | let mut child = self.new_child(); |
| 419 | | 416 | |
| 420 | for statement in b.block.statements { | 417 | for statement in b.block.statements { |
| 421 | if let None = child.process_statement(statement) { | 418 | if let None = child.process_statement(statement) { |
| 422 | return None; | 419 | tree_shake_statement_list(&mut child.statements); |
| | 420 | return JsBlock::new(child.statements, loc); |
| 423 | }; | 421 | }; |
| 424 | } | 422 | } |
| 425 | | 423 | |
| ... | @@ -449,7 +447,9 @@ impl HoistContext { | ... | @@ -449,7 +447,9 @@ impl HoistContext { |
| 449 | _ => {} | 447 | _ => {} |
| 450 | } | 448 | } |
| 451 | | 449 | |
| 452 | Some(JsBlock::new(child.statements, loc)) | 450 | tree_shake_statement_list(&mut child.statements); |
| | 451 | |
| | 452 | JsBlock::new(child.statements, loc) |
| 453 | } | 453 | } |
| 454 | | 454 | |
| 455 | pub fn hoist_block_to_arrow_fn_body(&mut self, b: BlockWithLoc) -> JsArrowBody { | 455 | pub fn hoist_block_to_arrow_fn_body(&mut self, b: BlockWithLoc) -> JsArrowBody { |
| ... | @@ -494,6 +494,8 @@ impl HoistContext { | ... | @@ -494,6 +494,8 @@ impl HoistContext { |
| 494 | _ => {} | 494 | _ => {} |
| 495 | } | 495 | } |
| 496 | | 496 | |
| | 497 | tree_shake_statement_list(&mut child.statements); |
| | 498 | |
| 497 | JsArrowBody::Block(JsBlock::new(child.statements, loc)) | 499 | JsArrowBody::Block(JsBlock::new(child.statements, loc)) |
| 498 | } | 500 | } |
| 499 | | 501 | |
| ... | @@ -513,16 +515,9 @@ impl HoistContext { | ... | @@ -513,16 +515,9 @@ impl HoistContext { |
| 513 | // to assign to the temporary variable. | 515 | // to assign to the temporary variable. |
| 514 | match self.last_statement().map(|x| &x.data) { | 516 | match self.last_statement().map(|x| &x.data) { |
| 515 | Some(JsStatementData::Expression(_)) if b.block.has_implicit_return => { | 517 | Some(JsStatementData::Expression(_)) if b.block.has_implicit_return => { |
| 516 | let last = self.statements.pop().unwrap(); | 518 | match self.statements.pop().unwrap().data { |
| 517 | match last { | 519 | JsStatementData::Expression(expr) => expr, |
| 518 | JsStatement { | 520 | _ => unreachable!(), |
| 519 | data: JsStatementData::Expression(expr), | | |
| 520 | .. | | |
| 521 | } => expr, | | |
| 522 | _ => { | | |
| 523 | self.statements.push(last); | | |
| 524 | JsExpr::new(JsExprData::Null, loc.clone(), None) | | |
| 525 | } | | |
| 526 | } | 521 | } |
| 527 | } | 522 | } |
| 528 | _ => JsExpr::new(JsExprData::Null, loc.clone(), None), | 523 | _ => JsExpr::new(JsExprData::Null, loc.clone(), None), |
| ... | @@ -536,15 +531,15 @@ impl HoistContext { | ... | @@ -536,15 +531,15 @@ impl HoistContext { |
| 536 | condition: JsExpr, | 531 | condition: JsExpr, |
| 537 | did_assign: &mut bool, | 532 | did_assign: &mut bool, |
| 538 | temporary: &TempId, | 533 | temporary: &TempId, |
| 539 | ) -> Option<JsIf> { | 534 | ) -> JsIf { |
| 540 | let yes = self.hoist_block_to_js_block(yes, &temporary.get(&self), did_assign)?; | 535 | let yes = self.hoist_block_to_js_block(yes, &temporary.get(&self), did_assign); |
| 541 | | 536 | |
| 542 | let no = match no { | 537 | let no = match no { |
| 543 | Some(BlockOrIf::Block(no)) => Some(JsElse::Block(self.hoist_block_to_js_block( | 538 | Some(BlockOrIf::Block(no)) => Some(JsElse::Block(self.hoist_block_to_js_block( |
| 544 | no, | 539 | no, |
| 545 | &temporary.get(&self), | 540 | &temporary.get(&self), |
| 546 | did_assign, | 541 | did_assign, |
| 547 | )?)), | 542 | ))), |
| 548 | Some(BlockOrIf::If(else_if)) => { | 543 | Some(BlockOrIf::If(else_if)) => { |
| 549 | // If the condition of this block has a side effect, we need to | 544 | // If the condition of this block has a side effect, we need to |
| 550 | // emit a block so that we can assign to the temporary variable. | 545 | // emit a block so that we can assign to the temporary variable. |
| ... | @@ -558,11 +553,12 @@ impl HoistContext { | ... | @@ -558,11 +553,12 @@ impl HoistContext { |
| 558 | condition, | 553 | condition, |
| 559 | did_assign, | 554 | did_assign, |
| 560 | temporary, | 555 | temporary, |
| 561 | )?))) | 556 | )))) |
| 562 | } else { | 557 | } else { |
| 563 | let mut temp_block = self.new_child(); | 558 | let mut temp_block = self.new_child(); |
| 564 | let condition = | 559 | let condition = temp_block |
| 565 | temp_block.hoist_expression_to_temporary(*else_if.condition.clone())?; | 560 | .hoist_expression_to_temporary(*else_if.condition.clone()) |
| | 561 | .unwrap(); |
| 566 | | 562 | |
| 567 | temp_block.add_statement(JsStatement::new( | 563 | temp_block.add_statement(JsStatement::new( |
| 568 | JsStatementData::If(self.hoist_js_if( | 564 | JsStatementData::If(self.hoist_js_if( |
| ... | @@ -571,11 +567,13 @@ impl HoistContext { | ... | @@ -571,11 +567,13 @@ impl HoistContext { |
| 571 | condition, | 567 | condition, |
| 572 | did_assign, | 568 | did_assign, |
| 573 | temporary, | 569 | temporary, |
| 574 | )?), | 570 | )), |
| 575 | Loc::EMPTY, | 571 | Loc::EMPTY, |
| 576 | None, | 572 | None, |
| 577 | )); | 573 | )); |
| 578 | | 574 | |
| | 575 | tree_shake_statement_list(&mut temp_block.statements); |
| | 576 | |
| 579 | Some(JsElse::Block(JsBlock::new( | 577 | Some(JsElse::Block(JsBlock::new( |
| 580 | temp_block.statements, | 578 | temp_block.statements, |
| 581 | Loc::EMPTY, | 579 | Loc::EMPTY, |
| ... | @@ -585,7 +583,7 @@ impl HoistContext { | ... | @@ -585,7 +583,7 @@ impl HoistContext { |
| 585 | None => None, | 583 | None => None, |
| 586 | }; | 584 | }; |
| 587 | | 585 | |
| 588 | Some(JsIf { condition, yes, no }) | 586 | JsIf { condition, yes, no } |
| 589 | } | 587 | } |
| 590 | } | 588 | } |
| 591 | | 589 | |
| ... | @@ -612,3 +610,29 @@ pub fn new_js_assignment_expr(expr: JsExpr, target: String, loc: Loc) -> JsExpr | ... | @@ -612,3 +610,29 @@ pub fn new_js_assignment_expr(expr: JsExpr, target: String, loc: Loc) -> JsExpr |
| 612 | None, | 610 | None, |
| 613 | ) | 611 | ) |
| 614 | } | 612 | } |
| | 613 | |
| | 614 | /// Remove |
| | 615 | /// - Discarded literals |
| | 616 | pub fn tree_shake_statement_list(list: &mut Vec<JsStatement>) { |
| | 617 | let mut set_i = 0; |
| | 618 | for i in 0..list.len() { |
| | 619 | let stmt = list.get(i).unwrap(); |
| | 620 | let should_keep = match &stmt.data { |
| | 621 | JsStatementData::Expression(expr) => !expr.is_literal(), |
| | 622 | JsStatementData::Return(_) => true, |
| | 623 | JsStatementData::Export(_) => true, |
| | 624 | JsStatementData::Declaration(_) => true, |
| | 625 | JsStatementData::Function(_) => true, |
| | 626 | JsStatementData::If(_) => true, |
| | 627 | // |
| | 628 | }; |
| | 629 | |
| | 630 | if should_keep { |
| | 631 | if set_i != i { |
| | 632 | list.swap(i, set_i) |
| | 633 | } |
| | 634 | set_i += 1; |
| | 635 | } |
| | 636 | } |
| | 637 | list.truncate(set_i) |
| | 638 | } |