| Index: pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| diff --git a/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| index aba10f4fc9967a4bee301471a4a6d7d3d50a6999..189171739d1a263dcc06ce072051579a34aa3157 100644
|
| --- a/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| +++ b/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| @@ -8,8 +8,6 @@ import '../tree_ir_nodes.dart';
|
| import 'optimization.dart' show Pass;
|
| import '../../constants/values.dart' as values;
|
|
|
| -// TODO(asgerf): Update this class to use JS semantics for && and ||.
|
| -
|
| /// Rewrites logical expressions to be more compact in the Tree IR.
|
| ///
|
| /// In this class an expression is said to occur in "boolean context" if
|
| @@ -49,16 +47,10 @@ import '../../constants/values.dart' as values;
|
| ///
|
| /// x ? true : false ==> !!x
|
| ///
|
| -/// If an operand is known to be a boolean, we can introduce a logical operator:
|
| -///
|
| -/// x ? y : false ==> x && y (if y is known to be a boolean)
|
| +/// If the possible falsy values of the condition are known, we can sometimes
|
| +/// introduce a logical operator:
|
| ///
|
| -/// The following sequence of rewrites demonstrates the merit of these rules:
|
| -///
|
| -/// x ? (y ? true : false) : false
|
| -/// x ? !!y : false (double negation introduced by [toBoolean])
|
| -/// x && !!y (!!y validated by [isBooleanValued])
|
| -/// x && y (double negation removed by [putInBooleanContext])
|
| +/// !x ? y : false ==> !x && y
|
| ///
|
| class LogicalRewriter extends RecursiveTransformer
|
| implements Pass {
|
| @@ -94,7 +86,7 @@ class LogicalRewriter extends RecursiveTransformer
|
|
|
| Statement visitIf(If node) {
|
| // If one of the branches is empty (i.e. just a fallthrough), then that
|
| - // branch should preferrably be the 'else' so we won't have to print it.
|
| + // branch should preferably be the 'else' so we won't have to print it.
|
| // In other words, we wish to perform this rewrite:
|
| // if (E) {} else {S}
|
| // ==>
|
| @@ -143,6 +135,31 @@ class LogicalRewriter extends RecursiveTransformer
|
| return toBoolean(makeCondition(node.operand, false, liftNots: false));
|
| }
|
|
|
| + /// True if the only possible falsy return value of [condition] is [value].
|
| + ///
|
| + /// If [value] is `null` or a truthy value, false is returned. This is to make
|
| + /// pattern matching more convenient.
|
| + bool matchesFalsyValue(Expression condition, values.ConstantValue value) {
|
| + if (value == null) return false;
|
| + // TODO(asgerf): Here we could really use some more type information,
|
| + // this is just the best we can do at the moment.
|
| + return isBooleanValued(condition) && value.isFalse;
|
| + }
|
| +
|
| + /// True if the only possible truthy return value of [condition] is [value].
|
| + ///
|
| + /// If [value] is `null` or a falsy value, false is returned. This is to make
|
| + /// pattern matching more convenient.
|
| + bool matchesTruthyValue(Expression condition, values.ConstantValue value) {
|
| + if (value == null) return false;
|
| + // TODO(asgerf): Again, more type information could really beef this up.
|
| + return isBooleanValued(condition) && value.isTrue;
|
| + }
|
| +
|
| + values.ConstantValue getConstant(Expression exp) {
|
| + return exp is Constant ? exp.value : null;
|
| + }
|
| +
|
| Expression visitConditional(Conditional node) {
|
| // node.condition will be visited after the then and else parts, because its
|
| // polarity depends on what rewrite we use.
|
| @@ -161,29 +178,32 @@ class LogicalRewriter extends RecursiveTransformer
|
| return toBoolean(makeCondition(node.condition, false, liftNots: false));
|
| }
|
|
|
| - // x ? y : false ==> x && y (if y is known to be a boolean)
|
| - if (isBooleanValued(node.thenExpression) && isFalse(node.elseExpression)) {
|
| + // x ? y : false ==> x && y (if x is truthy or false)
|
| + // x ? y : null ==> x && y (if x is truthy or null)
|
| + // x ? y : 0 ==> x && y (if x is truthy or zero) (and so on...)
|
| + if (matchesFalsyValue(node.condition, getConstant(node.elseExpression))) {
|
| return new LogicalOperator.and(
|
| - makeCondition(node.condition, true, liftNots:false),
|
| - putInBooleanContext(node.thenExpression));
|
| + visitExpression(node.condition),
|
| + node.thenExpression);
|
| }
|
| - // x ? y : true ==> !x || y (if y is known to be a boolean)
|
| - if (isBooleanValued(node.thenExpression) && isTrue(node.elseExpression)) {
|
| + // x ? true : y ==> x || y (if x is falsy or true)
|
| + // x ? 1 : y ==> x || y (if x is falsy or one) (and so on...)
|
| + if (matchesTruthyValue(node.condition, getConstant(node.thenExpression))) {
|
| return new LogicalOperator.or(
|
| - makeCondition(node.condition, false, liftNots: false),
|
| - putInBooleanContext(node.thenExpression));
|
| + visitExpression(node.condition),
|
| + node.elseExpression);
|
| }
|
| - // x ? true : y ==> x || y (if y if known to be boolean)
|
| - if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) {
|
| + // x ? y : true ==> !x || y
|
| + if (isTrue(node.elseExpression)) {
|
| return new LogicalOperator.or(
|
| - makeCondition(node.condition, true, liftNots: false),
|
| - putInBooleanContext(node.elseExpression));
|
| + toBoolean(makeCondition(node.condition, false, liftNots: false)),
|
| + node.thenExpression);
|
| }
|
| - // x ? false : y ==> !x && y (if y is known to be a boolean)
|
| - if (isBooleanValued(node.elseExpression) && isFalse(node.thenExpression)) {
|
| + // x ? false : y ==> !x && y
|
| + if (isFalse(node.thenExpression)) {
|
| return new LogicalOperator.and(
|
| - makeCondition(node.condition, false, liftNots: false),
|
| - putInBooleanContext(node.elseExpression));
|
| + toBoolean(makeCondition(node.condition, false, liftNots: false)),
|
| + node.elseExpression);
|
| }
|
|
|
| node.condition = makeCondition(node.condition, true);
|
| @@ -200,8 +220,8 @@ class LogicalRewriter extends RecursiveTransformer
|
| }
|
|
|
| Expression visitLogicalOperator(LogicalOperator node) {
|
| - node.left = makeCondition(node.left, true);
|
| - node.right = makeCondition(node.right, true);
|
| + node.left = visitExpression(node.left);
|
| + node.right = visitExpression(node.right);
|
| return node;
|
| }
|
|
|
| @@ -253,16 +273,6 @@ class LogicalRewriter extends RecursiveTransformer
|
| }
|
| }
|
|
|
| - /// Rewrite an expression that was originally processed in a non-boolean
|
| - /// context.
|
| - Expression putInBooleanContext(Expression e) {
|
| - if (e is Not && e.operand is Not) {
|
| - return (e.operand as Not).operand;
|
| - } else {
|
| - return e;
|
| - }
|
| - }
|
| -
|
| /// Forces a boolean conversion of the given expression.
|
| Expression toBoolean(Expression e) {
|
| if (isBooleanValued(e))
|
| @@ -275,7 +285,7 @@ class LogicalRewriter extends RecursiveTransformer
|
| /// context where its result is immediately subject to boolean conversion.
|
| /// If [polarity] if false, the negated condition will be created instead.
|
| /// If [liftNots] is true (default) then Not expressions will be lifted toward
|
| - /// the root the condition so they can be eliminated by the caller.
|
| + /// the root of the condition so they can be eliminated by the caller.
|
| Expression makeCondition(Expression e, bool polarity, {bool liftNots:true}) {
|
| if (e is Not) {
|
| // !!E ==> E
|
|
|