| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:kernel/ast.dart'; |
| |
| /// Simple unreachable code elimination: removes asserts and if statements |
| /// with constant conditions. Does a very limited constant folding of |
| /// logical expressions. |
| Component transformComponent(Component component, bool enableAsserts) { |
| new SimpleUnreachableCodeElimination(enableAsserts).visitComponent(component); |
| return component; |
| } |
| |
| class SimpleUnreachableCodeElimination extends Transformer { |
| final bool enableAsserts; |
| |
| SimpleUnreachableCodeElimination(this.enableAsserts); |
| |
| bool _isBoolConstant(Expression node) => |
| node is BoolLiteral || |
| (node is ConstantExpression && node.constant is BoolConstant); |
| |
| bool _getBoolConstantValue(Expression node) { |
| if (node is BoolLiteral) { |
| return node.value; |
| } |
| if (node is ConstantExpression) { |
| final constant = node.constant; |
| if (constant is BoolConstant) { |
| return constant.value; |
| } |
| } |
| throw 'Expected bool constant: $node'; |
| } |
| |
| Expression _createBoolLiteral(bool value, int fileOffset) => |
| new BoolLiteral(value)..fileOffset = fileOffset; |
| |
| Statement _makeEmptyBlockIfNull(Statement node) => |
| node == null ? Block(<Statement>[]) : node; |
| |
| @override |
| TreeNode visitIfStatement(IfStatement node) { |
| node.transformChildren(this); |
| final condition = node.condition; |
| if (_isBoolConstant(condition)) { |
| final value = _getBoolConstantValue(condition); |
| return value ? node.then : node.otherwise; |
| } |
| node.then = _makeEmptyBlockIfNull(node.then); |
| return node; |
| } |
| |
| @override |
| visitConditionalExpression(ConditionalExpression node) { |
| node.transformChildren(this); |
| final condition = node.condition; |
| if (_isBoolConstant(condition)) { |
| final value = _getBoolConstantValue(condition); |
| return value ? node.then : node.otherwise; |
| } |
| return node; |
| } |
| |
| @override |
| TreeNode visitNot(Not node) { |
| node.transformChildren(this); |
| final operand = node.operand; |
| if (_isBoolConstant(operand)) { |
| return _createBoolLiteral( |
| !_getBoolConstantValue(operand), node.fileOffset); |
| } |
| return node; |
| } |
| |
| @override |
| TreeNode visitLogicalExpression(LogicalExpression node) { |
| node.transformChildren(this); |
| final left = node.left; |
| final right = node.right; |
| final operator = node.operator; |
| if (_isBoolConstant(left)) { |
| final leftValue = _getBoolConstantValue(left); |
| if (_isBoolConstant(right)) { |
| final rightValue = _getBoolConstantValue(right); |
| if (operator == '||') { |
| return _createBoolLiteral(leftValue || rightValue, node.fileOffset); |
| } else if (operator == '&&') { |
| return _createBoolLiteral(leftValue && rightValue, node.fileOffset); |
| } else { |
| throw 'Unexpected LogicalExpression operator ${operator}: $node'; |
| } |
| } else { |
| if (leftValue && operator == '||') { |
| return _createBoolLiteral(true, node.fileOffset); |
| } else if (!leftValue && operator == '&&') { |
| return _createBoolLiteral(false, node.fileOffset); |
| } |
| } |
| } |
| return node; |
| } |
| |
| @override |
| visitStaticGet(StaticGet node) { |
| node.transformChildren(this); |
| final target = node.target; |
| if (target is Field && target.isConst) { |
| throw 'StaticGet from const field $target should be evaluated by front-end: $node'; |
| } |
| return node; |
| } |
| |
| @override |
| TreeNode visitAssertStatement(AssertStatement node) { |
| if (!enableAsserts) { |
| return null; |
| } |
| return super.visitAssertStatement(node); |
| } |
| |
| @override |
| TreeNode visitAssertBlock(AssertBlock node) { |
| if (!enableAsserts) { |
| return null; |
| } |
| return super.visitAssertBlock(node); |
| } |
| |
| @override |
| TreeNode visitAssertInitializer(AssertInitializer node) { |
| if (!enableAsserts) { |
| return null; |
| } |
| return super.visitAssertInitializer(node); |
| } |
| |
| @override |
| TreeNode visitTryFinally(TryFinally node) { |
| node.transformChildren(this); |
| final fin = node.finalizer; |
| if (fin == null || (fin is Block && fin.statements.isEmpty)) { |
| return node.body; |
| } |
| return node; |
| } |
| |
| bool _isRethrow(Statement body) { |
| if (body is ExpressionStatement && body.expression is Rethrow) { |
| return true; |
| } else if (body is Block && body.statements.length == 1) { |
| return _isRethrow(body.statements.single); |
| } |
| return false; |
| } |
| |
| @override |
| TreeNode visitTryCatch(TryCatch node) { |
| node.transformChildren(this); |
| // Can replace try/catch with its body if all catches are just rethow. |
| for (Catch catchClause in node.catches) { |
| if (!_isRethrow(catchClause.body)) { |
| return node; |
| } |
| } |
| return node.body; |
| } |
| |
| // Make sure we're not generating `null` bodies. |
| // Try/catch, try/finally and switch/case statements |
| // always have a Block in a body, so there is no |
| // need to guard against null. |
| |
| @override |
| TreeNode visitWhileStatement(WhileStatement node) { |
| node.transformChildren(this); |
| node.body = _makeEmptyBlockIfNull(node.body); |
| return node; |
| } |
| |
| @override |
| TreeNode visitDoStatement(DoStatement node) { |
| node.transformChildren(this); |
| node.body = _makeEmptyBlockIfNull(node.body); |
| return node; |
| } |
| |
| @override |
| TreeNode visitForStatement(ForStatement node) { |
| node.transformChildren(this); |
| node.body = _makeEmptyBlockIfNull(node.body); |
| return node; |
| } |
| |
| @override |
| TreeNode visitForInStatement(ForInStatement node) { |
| node.transformChildren(this); |
| node.body = _makeEmptyBlockIfNull(node.body); |
| return node; |
| } |
| } |