Revert "Flatten constant operations to implement JS semantics by default."

This reverts commit 8172270c954513813eef066b4615d0f46b389e6e.

Reason for revert: <INSERT REASONING HERE>

Original change's description:
> Flatten constant operations to implement JS semantics by default.
> 
> Change-Id: Id93e9a1ce77d8035928ba0b9c338a1030d0e13bd
> Reviewed-on: https://dart-review.googlesource.com/c/93960
> Commit-Queue: Mayank Patke <fishythefish@google.com>
> Reviewed-by: Sigmund Cherem <sigmund@google.com>
> Reviewed-by: Johnni Winther <johnniwinther@google.com>

TBR=johnniwinther@google.com,sigmund@google.com,fishythefish@google.com

Change-Id: I228c171ba25d24ab672add95e92e3f5d0656ddc4
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/94100
Reviewed-by: Mayank Patke <fishythefish@google.com>
Commit-Queue: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/constants/constant_system.dart b/pkg/compiler/lib/src/constants/constant_system.dart
index 61c1eab..5e2f563 100644
--- a/pkg/compiler/lib/src/constants/constant_system.dart
+++ b/pkg/compiler/lib/src/constants/constant_system.dart
@@ -14,32 +14,38 @@
 
 final _BITS32 = new BigInt.from(0xFFFFFFFF);
 
-const add = const AddOperation();
-const bitAnd = const BitAndOperation();
+const add = const JavaScriptAddOperation();
+const bitAnd = const JavaScriptBinaryBitOperation(const BitAndOperation());
 const bitNot = const BitNotOperation();
-const bitOr = const BitOrOperation();
-const bitXor = const BitXorOperation();
+const bitOr = const JavaScriptBinaryBitOperation(const BitOrOperation());
+const bitXor = const JavaScriptBinaryBitOperation(const BitXorOperation());
 const booleanAnd = const BooleanAndOperation();
 const booleanOr = const BooleanOrOperation();
-const divide = const DivideOperation();
+const divide =
+    const JavaScriptBinaryArithmeticOperation(const DivideOperation());
 const equal = const EqualsOperation();
 const greaterEqual = const GreaterEqualOperation();
 const greater = const GreaterOperation();
-const identity = const IdentityOperation();
+const identity = const JavaScriptIdentityOperation();
 const ifNull = const IfNullOperation();
 const lessEqual = const LessEqualOperation();
 const less = const LessOperation();
-const modulo = const ModuloOperation();
-const multiply = const MultiplyOperation();
-const negate = const NegateOperation();
+const modulo =
+    const JavaScriptBinaryArithmeticOperation(const ModuloOperation());
+const multiply =
+    const JavaScriptBinaryArithmeticOperation(const MultiplyOperation());
+const negate = const JavaScriptNegateOperation();
 const not = const NotOperation();
-const remainder = const RemainderOperation();
-const shiftLeft = const ShiftLeftOperation();
-const shiftRight = const ShiftRightOperation();
-const subtract = const SubtractOperation();
-const truncatingDivide = const TruncatingDivideOperation();
-const codeUnitAt = const CodeUnitAtOperation();
-const round = const RoundOperation();
+const remainder = const JavaScriptRemainderOperation();
+const shiftLeft =
+    const JavaScriptBinaryBitOperation(const ShiftLeftOperation());
+const shiftRight = const JavaScriptShiftRightOperation();
+const subtract =
+    const JavaScriptBinaryArithmeticOperation(const SubtractOperation());
+const truncatingDivide = const JavaScriptBinaryArithmeticOperation(
+    const TruncatingDivideOperation());
+const codeUnitAt = const CodeUnitAtRuntimeOperation();
+const round = const JavaScriptRoundOperation();
 const abs = const UnfoldedUnaryOperation('abs');
 
 /// Returns true if [value] will turn into NaN or infinity
@@ -259,12 +265,9 @@
 }
 
 class BitNotOperation implements UnaryOperation {
-  @override
   final String name = '~';
-
   const BitNotOperation();
 
-  @override
   ConstantValue fold(ConstantValue constant) {
     if (isInt(constant)) {
       // In JavaScript we don't check for -0 and treat it as if it was zero.
@@ -280,42 +283,42 @@
 }
 
 class NegateOperation implements UnaryOperation {
-  @override
   final String name = 'negate';
-
   const NegateOperation();
-
-  @override
   ConstantValue fold(ConstantValue constant) {
-    ConstantValue _fold(ConstantValue constant) {
-      if (constant.isInt) {
-        IntConstantValue intConstant = constant;
-        return createInt(-intConstant.intValue);
-      }
-      if (constant.isDouble) {
-        DoubleConstantValue doubleConstant = constant;
-        return createDouble(-doubleConstant.doubleValue);
-      }
-      return null;
+    if (constant.isInt) {
+      IntConstantValue intConstant = constant;
+      return createInt(-intConstant.intValue);
     }
+    if (constant.isDouble) {
+      DoubleConstantValue doubleConstant = constant;
+      return createDouble(-doubleConstant.doubleValue);
+    }
+    return null;
+  }
+}
 
+class JavaScriptNegateOperation implements UnaryOperation {
+  final NegateOperation dartNegateOperation = const NegateOperation();
+
+  const JavaScriptNegateOperation();
+
+  String get name => dartNegateOperation.name;
+
+  ConstantValue fold(ConstantValue constant) {
     if (constant.isInt) {
       IntConstantValue intConstant = constant;
       if (intConstant.intValue == BigInt.zero) {
         return createDouble(-0.0);
       }
     }
-    return _fold(constant);
+    return dartNegateOperation.fold(constant);
   }
 }
 
 class NotOperation implements UnaryOperation {
-  @override
   final String name = '!';
-
   const NotOperation();
-
-  @override
   ConstantValue fold(ConstantValue constant) {
     if (constant.isBool) {
       BoolConstantValue boolConstant = constant;
@@ -328,20 +331,30 @@
 /// Operations that only work if both arguments are integers.
 abstract class BinaryBitOperation implements BinaryOperation {
   const BinaryBitOperation();
-
-  @override
   ConstantValue fold(ConstantValue left, ConstantValue right) {
-    ConstantValue _fold(ConstantValue left, ConstantValue right) {
-      if (left.isInt && right.isInt) {
-        IntConstantValue leftInt = left;
-        IntConstantValue rightInt = right;
-        BigInt resultValue = foldInts(leftInt.intValue, rightInt.intValue);
-        if (resultValue == null) return null;
-        return createInt(resultValue);
-      }
-      return null;
+    if (left.isInt && right.isInt) {
+      IntConstantValue leftInt = left;
+      IntConstantValue rightInt = right;
+      BigInt resultValue = foldInts(leftInt.intValue, rightInt.intValue);
+      if (resultValue == null) return null;
+      return createInt(resultValue);
     }
+    return null;
+  }
 
+  BigInt foldInts(BigInt left, BigInt right);
+}
+
+/// In JavaScript we truncate the result to an unsigned 32 bit integer. Also, -0
+/// is treated as if it was the integer 0.
+class JavaScriptBinaryBitOperation implements BinaryOperation {
+  final BinaryBitOperation dartBitOperation;
+
+  const JavaScriptBinaryBitOperation(this.dartBitOperation);
+
+  String get name => dartBitOperation.name;
+
+  ConstantValue fold(ConstantValue left, ConstantValue right) {
     // In JavaScript we don't check for -0 and treat it as if it was zero.
     if (left.isMinusZero) {
       left = createInt(BigInt.zero);
@@ -349,7 +362,7 @@
     if (right.isMinusZero) {
       right = createInt(BigInt.zero);
     }
-    IntConstantValue result = _fold(left, right);
+    IntConstantValue result = dartBitOperation.fold(left, right);
     if (result != null) {
       // We convert the result of bit-operations to 32 bit unsigned integers.
       return _createInt32(result.intValue);
@@ -357,55 +370,33 @@
     return result;
   }
 
-  BigInt foldInts(BigInt left, BigInt right);
+  apply(left, right) => dartBitOperation.apply(left, right);
 }
 
 class BitAndOperation extends BinaryBitOperation {
-  @override
   final String name = '&';
-
   const BitAndOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) => left & right;
-
-  @override
   apply(left, right) => left & right;
 }
 
 class BitOrOperation extends BinaryBitOperation {
-  @override
   final String name = '|';
-
   const BitOrOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) => left | right;
-
-  @override
   apply(left, right) => left | right;
 }
 
 class BitXorOperation extends BinaryBitOperation {
-  @override
   final String name = '^';
-
   const BitXorOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) => left ^ right;
-
-  @override
   apply(left, right) => left ^ right;
 }
 
 class ShiftLeftOperation extends BinaryBitOperation {
-  @override
   final String name = '<<';
-
   const ShiftLeftOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) {
     // TODO(floitsch): find a better way to guard against excessive shifts to
     // the left.
@@ -413,17 +404,23 @@
     return left << right.toInt();
   }
 
-  @override
   apply(left, right) => left << right;
 }
 
 class ShiftRightOperation extends BinaryBitOperation {
-  @override
   final String name = '>>';
-
   const ShiftRightOperation();
+  BigInt foldInts(BigInt left, BigInt right) {
+    if (right < BigInt.zero) return null;
+    return left >> right.toInt();
+  }
 
-  @override
+  apply(left, right) => left >> right;
+}
+
+class JavaScriptShiftRightOperation extends JavaScriptBinaryBitOperation {
+  const JavaScriptShiftRightOperation() : super(const ShiftRightOperation());
+
   ConstantValue fold(ConstantValue left, ConstantValue right) {
     // Truncate the input value to 32 bits if necessary.
     if (left.isInt) {
@@ -450,21 +447,10 @@
     }
     return super.fold(left, right);
   }
-
-  @override
-  BigInt foldInts(BigInt left, BigInt right) {
-    if (right < BigInt.zero) return null;
-    return left >> right.toInt();
-  }
-
-  @override
-  apply(left, right) => left >> right;
 }
 
 abstract class BinaryBoolOperation implements BinaryOperation {
   const BinaryBoolOperation();
-
-  @override
   ConstantValue fold(ConstantValue left, ConstantValue right) {
     if (left.isBool && right.isBool) {
       BoolConstantValue leftBool = left;
@@ -479,63 +465,43 @@
 }
 
 class BooleanAndOperation extends BinaryBoolOperation {
-  @override
   final String name = '&&';
-
   const BooleanAndOperation();
-
-  @override
   bool foldBools(bool left, bool right) => left && right;
-
-  @override
   apply(left, right) => left && right;
 }
 
 class BooleanOrOperation extends BinaryBoolOperation {
-  @override
   final String name = '||';
-
   const BooleanOrOperation();
-
-  @override
   bool foldBools(bool left, bool right) => left || right;
-
-  @override
   apply(left, right) => left || right;
 }
 
 abstract class ArithmeticNumOperation implements BinaryOperation {
   const ArithmeticNumOperation();
-
-  @override
   ConstantValue fold(ConstantValue left, ConstantValue right) {
-    ConstantValue _fold(ConstantValue left, ConstantValue right) {
-      if (left.isNum && right.isNum) {
-        NumConstantValue leftNum = left;
-        NumConstantValue rightNum = right;
-        var foldedValue;
-        if (left.isInt && right.isInt) {
-          IntConstantValue leftInt = leftNum;
-          IntConstantValue rightInt = rightNum;
-          foldedValue = foldInts(leftInt.intValue, rightInt.intValue);
-        } else {
-          foldedValue = foldNums(leftNum.doubleValue, rightNum.doubleValue);
-        }
-        // A division by 0 means that we might not have a folded value.
-        if (foldedValue == null) return null;
-        if (left.isInt && right.isInt && !isDivide() || isTruncatingDivide()) {
-          assert(foldedValue is BigInt);
-          return createInt(foldedValue);
-        } else {
-          return createDouble(foldedValue);
-        }
+    if (left.isNum && right.isNum) {
+      NumConstantValue leftNum = left;
+      NumConstantValue rightNum = right;
+      var foldedValue;
+      if (left.isInt && right.isInt) {
+        IntConstantValue leftInt = leftNum;
+        IntConstantValue rightInt = rightNum;
+        foldedValue = foldInts(leftInt.intValue, rightInt.intValue);
+      } else {
+        foldedValue = foldNums(leftNum.doubleValue, rightNum.doubleValue);
       }
-      return null;
+      // A division by 0 means that we might not have a folded value.
+      if (foldedValue == null) return null;
+      if (left.isInt && right.isInt && !isDivide() || isTruncatingDivide()) {
+        assert(foldedValue is BigInt);
+        return createInt(foldedValue);
+      } else {
+        return createDouble(foldedValue);
+      }
     }
-
-    ConstantValue result = _fold(left, right);
-    if (result == null) return result;
-    return _convertToJavaScriptConstant(result);
+    return null;
   }
 
   bool isDivide() => false;
@@ -544,165 +510,137 @@
   foldNums(num left, num right);
 }
 
+class JavaScriptBinaryArithmeticOperation implements BinaryOperation {
+  final BinaryOperation dartArithmeticOperation;
+
+  const JavaScriptBinaryArithmeticOperation(this.dartArithmeticOperation);
+
+  String get name => dartArithmeticOperation.name;
+
+  ConstantValue fold(ConstantValue left, ConstantValue right) {
+    ConstantValue result = dartArithmeticOperation.fold(left, right);
+    if (result == null) return result;
+    return _convertToJavaScriptConstant(result);
+  }
+
+  apply(left, right) => dartArithmeticOperation.apply(left, right);
+}
+
 class SubtractOperation extends ArithmeticNumOperation {
-  @override
   final String name = '-';
-
   const SubtractOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) => left - right;
-
-  @override
   num foldNums(num left, num right) => left - right;
-
-  @override
   apply(left, right) => left - right;
 }
 
 class MultiplyOperation extends ArithmeticNumOperation {
-  @override
   final String name = '*';
-
   const MultiplyOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) => left * right;
-
-  @override
   num foldNums(num left, num right) => left * right;
-
-  @override
   apply(left, right) => left * right;
 }
 
 class ModuloOperation extends ArithmeticNumOperation {
-  @override
   final String name = '%';
-
   const ModuloOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) {
     if (right == BigInt.zero) return null;
     return left % right;
   }
 
-  @override
   num foldNums(num left, num right) => left % right;
-
-  @override
   apply(left, right) => left % right;
 }
 
-class RemainderOperation extends ArithmeticNumOperation {
-  @override
-  final String name = 'remainder';
+class JavaScriptRemainderOperation extends ArithmeticNumOperation {
+  String get name => 'remainder';
 
-  const RemainderOperation();
+  const JavaScriptRemainderOperation();
 
-  @override
   BigInt foldInts(BigInt left, BigInt right) {
     if (right == BigInt.zero) return null;
     return left.remainder(right);
   }
 
-  @override
   num foldNums(num left, num right) => left.remainder(right);
-
-  @override
   apply(left, right) => left.remainder(right);
 }
 
 class TruncatingDivideOperation extends ArithmeticNumOperation {
-  @override
   final String name = '~/';
-
   const TruncatingDivideOperation();
-
-  @override
   BigInt foldInts(BigInt left, BigInt right) {
     if (right == BigInt.zero) return null;
     return left ~/ right;
   }
 
-  @override
   BigInt foldNums(num left, num right) {
     num ratio = left / right;
     if (ratio.isNaN || ratio.isInfinite) return null;
     return new BigInt.from(ratio.truncate().toInt());
   }
 
-  @override
   apply(left, right) => left ~/ right;
-
-  @override
   bool isTruncatingDivide() => true;
 }
 
 class DivideOperation extends ArithmeticNumOperation {
-  @override
   final String name = '/';
-
   const DivideOperation();
-
-  @override
   double foldInts(BigInt left, BigInt right) => left / right;
-
-  @override
   num foldNums(num left, num right) => left / right;
-
-  @override
   bool isDivide() => true;
-
-  @override
   apply(left, right) => left / right;
 }
 
 class AddOperation implements BinaryOperation {
-  @override
   final String name = '+';
-
   const AddOperation();
-
-  @override
   ConstantValue fold(ConstantValue left, ConstantValue right) {
-    ConstantValue _fold(ConstantValue left, ConstantValue right) {
-      if (left.isInt && right.isInt) {
-        IntConstantValue leftInt = left;
-        IntConstantValue rightInt = right;
-        BigInt result = leftInt.intValue + rightInt.intValue;
-        return createInt(result);
-      } else if (left.isNum && right.isNum) {
-        NumConstantValue leftNum = left;
-        NumConstantValue rightNum = right;
-        double result = leftNum.doubleValue + rightNum.doubleValue;
-        return createDouble(result);
-      } else if (left.isString && right.isString) {
-        StringConstantValue leftString = left;
-        StringConstantValue rightString = right;
-        String result = leftString.stringValue + rightString.stringValue;
-        return createString(result);
-      } else {
-        return null;
-      }
+    if (left.isInt && right.isInt) {
+      IntConstantValue leftInt = left;
+      IntConstantValue rightInt = right;
+      BigInt result = leftInt.intValue + rightInt.intValue;
+      return createInt(result);
+    } else if (left.isNum && right.isNum) {
+      NumConstantValue leftNum = left;
+      NumConstantValue rightNum = right;
+      double result = leftNum.doubleValue + rightNum.doubleValue;
+      return createDouble(result);
+    } else if (left.isString && right.isString) {
+      StringConstantValue leftString = left;
+      StringConstantValue rightString = right;
+      String result = leftString.stringValue + rightString.stringValue;
+      return createString(result);
+    } else {
+      return null;
     }
+  }
 
-    ConstantValue result = _fold(left, right);
+  apply(left, right) => left + right;
+}
+
+class JavaScriptAddOperation implements BinaryOperation {
+  final _addOperation = const AddOperation();
+  String get name => _addOperation.name;
+
+  const JavaScriptAddOperation();
+
+  ConstantValue fold(ConstantValue left, ConstantValue right) {
+    ConstantValue result = _addOperation.fold(left, right);
     if (result != null && result.isNum) {
       return _convertToJavaScriptConstant(result);
     }
     return result;
   }
 
-  @override
-  apply(left, right) => left + right;
+  apply(left, right) => _addOperation.apply(left, right);
 }
 
 abstract class RelationalNumOperation implements BinaryOperation {
   const RelationalNumOperation();
-
-  @override
   ConstantValue fold(ConstantValue left, ConstantValue right) {
     if (!left.isNum || !right.isNum) return null;
     bool foldedValue;
@@ -724,76 +662,40 @@
 }
 
 class LessOperation extends RelationalNumOperation {
-  @override
   final String name = '<';
-
   const LessOperation();
-
-  @override
   bool foldInts(BigInt left, BigInt right) => left < right;
-
-  @override
   bool foldNums(num left, num right) => left < right;
-
-  @override
   apply(left, right) => left < right;
 }
 
 class LessEqualOperation extends RelationalNumOperation {
-  @override
   final String name = '<=';
-
   const LessEqualOperation();
-
-  @override
   bool foldInts(BigInt left, BigInt right) => left <= right;
-
-  @override
   bool foldNums(num left, num right) => left <= right;
-
-  @override
   apply(left, right) => left <= right;
 }
 
 class GreaterOperation extends RelationalNumOperation {
-  @override
   final String name = '>';
-
   const GreaterOperation();
-
-  @override
   bool foldInts(BigInt left, BigInt right) => left > right;
-
-  @override
   bool foldNums(num left, num right) => left > right;
-
-  @override
   apply(left, right) => left > right;
 }
 
 class GreaterEqualOperation extends RelationalNumOperation {
-  @override
   final String name = '>=';
-
   const GreaterEqualOperation();
-
-  @override
   bool foldInts(BigInt left, BigInt right) => left >= right;
-
-  @override
   bool foldNums(num left, num right) => left >= right;
-
-  @override
   apply(left, right) => left >= right;
 }
 
 class EqualsOperation implements BinaryOperation {
-  @override
   final String name = '==';
-
   const EqualsOperation();
-
-  @override
   ConstantValue fold(ConstantValue left, ConstantValue right) {
     // Numbers need to be treated specially because: NaN != NaN, -0.0 == 0.0,
     // and 1 == 1.0.
@@ -823,27 +725,32 @@
     return createBool(left == right);
   }
 
-  @override
   apply(left, right) => left == right;
 }
 
 class IdentityOperation implements BinaryOperation {
-  @override
   final String name = '===';
-
   const IdentityOperation();
-
-  @override
   BoolConstantValue fold(ConstantValue left, ConstantValue right) {
-    BoolConstantValue _fold(ConstantValue left, ConstantValue right) {
-      // In order to preserve runtime semantics which says that NaN !== NaN
-      // don't constant fold NaN === NaN. Otherwise the output depends on
-      // inlined variables and other optimizations.
-      if (left.isNaN && right.isNaN) return null;
-      return createBool(left == right);
-    }
+    // In order to preserve runtime semantics which says that NaN !== NaN don't
+    // constant fold NaN === NaN. Otherwise the output depends on inlined
+    // variables and other optimizations.
+    if (left.isNaN && right.isNaN) return null;
+    return createBool(left == right);
+  }
 
-    BoolConstantValue result = _fold(left, right);
+  apply(left, right) => identical(left, right);
+}
+
+class JavaScriptIdentityOperation implements BinaryOperation {
+  final IdentityOperation dartIdentityOperation = const IdentityOperation();
+
+  const JavaScriptIdentityOperation();
+
+  String get name => dartIdentityOperation.name;
+
+  BoolConstantValue fold(ConstantValue left, ConstantValue right) {
+    BoolConstantValue result = dartIdentityOperation.fold(left, right);
     if (result == null || result.boolValue) return result;
     // In JavaScript -0.0 === 0 and all doubles are equal to their integer
     // values. Furthermore NaN !== NaN.
@@ -862,33 +769,29 @@
     return result;
   }
 
-  @override
   apply(left, right) => identical(left, right);
 }
 
 class IfNullOperation implements BinaryOperation {
-  @override
   final String name = '??';
-
   const IfNullOperation();
-
-  @override
   ConstantValue fold(ConstantValue left, ConstantValue right) {
     if (left.isNull) return right;
     return left;
   }
 
-  @override
   apply(left, right) => left ?? right;
 }
 
 class CodeUnitAtOperation implements BinaryOperation {
-  @override
-  final String name = 'charCodeAt';
-
+  String get name => 'charCodeAt';
   const CodeUnitAtOperation();
+  ConstantValue fold(ConstantValue left, ConstantValue right) => null;
+  apply(left, right) => left.codeUnitAt(right);
+}
 
-  @override
+class CodeUnitAtRuntimeOperation extends CodeUnitAtOperation {
+  const CodeUnitAtRuntimeOperation();
   IntConstantValue fold(ConstantValue left, ConstantValue right) {
     if (left.isString && right.isInt) {
       StringConstantValue stringConstant = left;
@@ -901,18 +804,11 @@
     }
     return null;
   }
-
-  @override
-  apply(left, right) => left.codeUnitAt(right);
 }
 
-class RoundOperation implements UnaryOperation {
-  @override
-  final String name = 'round';
-
-  const RoundOperation();
-
-  @override
+class JavaScriptRoundOperation implements UnaryOperation {
+  const JavaScriptRoundOperation();
+  String get name => round.name;
   ConstantValue fold(ConstantValue constant) {
     // Be careful to round() only values that do not throw on either the host or
     // target platform.
@@ -950,12 +846,8 @@
 }
 
 class UnfoldedUnaryOperation implements UnaryOperation {
-  @override
   final String name;
-
   const UnfoldedUnaryOperation(this.name);
-
-  @override
   ConstantValue fold(ConstantValue constant) {
     return null;
   }