Reland "Flatten constant operations to implement JS semantics by default."
This is a reland of 8172270c954513813eef066b4615d0f46b389e6e
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>
Change-Id: I33a167324bfb0825c23860b3f96fcb9d735395d1
Reviewed-on: https://dart-review.googlesource.com/c/94108
Reviewed-by: Sigmund Cherem <sigmund@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 5e2f563..61c1eab 100644
--- a/pkg/compiler/lib/src/constants/constant_system.dart
+++ b/pkg/compiler/lib/src/constants/constant_system.dart
@@ -14,38 +14,32 @@
final _BITS32 = new BigInt.from(0xFFFFFFFF);
-const add = const JavaScriptAddOperation();
-const bitAnd = const JavaScriptBinaryBitOperation(const BitAndOperation());
+const add = const AddOperation();
+const bitAnd = const BitAndOperation();
const bitNot = const BitNotOperation();
-const bitOr = const JavaScriptBinaryBitOperation(const BitOrOperation());
-const bitXor = const JavaScriptBinaryBitOperation(const BitXorOperation());
+const bitOr = const BitOrOperation();
+const bitXor = const BitXorOperation();
const booleanAnd = const BooleanAndOperation();
const booleanOr = const BooleanOrOperation();
-const divide =
- const JavaScriptBinaryArithmeticOperation(const DivideOperation());
+const divide = const DivideOperation();
const equal = const EqualsOperation();
const greaterEqual = const GreaterEqualOperation();
const greater = const GreaterOperation();
-const identity = const JavaScriptIdentityOperation();
+const identity = const IdentityOperation();
const ifNull = const IfNullOperation();
const lessEqual = const LessEqualOperation();
const less = const LessOperation();
-const modulo =
- const JavaScriptBinaryArithmeticOperation(const ModuloOperation());
-const multiply =
- const JavaScriptBinaryArithmeticOperation(const MultiplyOperation());
-const negate = const JavaScriptNegateOperation();
+const modulo = const ModuloOperation();
+const multiply = const MultiplyOperation();
+const negate = const NegateOperation();
const not = const NotOperation();
-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 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 abs = const UnfoldedUnaryOperation('abs');
/// Returns true if [value] will turn into NaN or infinity
@@ -265,9 +259,12 @@
}
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.
@@ -283,42 +280,42 @@
}
class NegateOperation implements UnaryOperation {
+ @override
final String name = 'negate';
+
const NegateOperation();
+
+ @override
ConstantValue fold(ConstantValue constant) {
- if (constant.isInt) {
- IntConstantValue intConstant = constant;
- return createInt(-intConstant.intValue);
+ 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.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 dartNegateOperation.fold(constant);
+ return _fold(constant);
}
}
class NotOperation implements UnaryOperation {
+ @override
final String name = '!';
+
const NotOperation();
+
+ @override
ConstantValue fold(ConstantValue constant) {
if (constant.isBool) {
BoolConstantValue boolConstant = constant;
@@ -331,30 +328,20 @@
/// Operations that only work if both arguments are integers.
abstract class BinaryBitOperation implements BinaryOperation {
const BinaryBitOperation();
+
+ @override
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);
+ 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;
}
- 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);
@@ -362,7 +349,7 @@
if (right.isMinusZero) {
right = createInt(BigInt.zero);
}
- IntConstantValue result = dartBitOperation.fold(left, right);
+ IntConstantValue result = _fold(left, right);
if (result != null) {
// We convert the result of bit-operations to 32 bit unsigned integers.
return _createInt32(result.intValue);
@@ -370,33 +357,55 @@
return result;
}
- apply(left, right) => dartBitOperation.apply(left, right);
+ BigInt foldInts(BigInt left, BigInt 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.
@@ -404,23 +413,17 @@
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();
- }
- apply(left, right) => left >> right;
-}
-
-class JavaScriptShiftRightOperation extends JavaScriptBinaryBitOperation {
- const JavaScriptShiftRightOperation() : super(const ShiftRightOperation());
-
+ @override
ConstantValue fold(ConstantValue left, ConstantValue right) {
// Truncate the input value to 32 bits if necessary.
if (left.isInt) {
@@ -447,10 +450,21 @@
}
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;
@@ -465,43 +479,63 @@
}
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) {
- 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);
+ 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);
+ }
}
- // 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);
- }
+ return null;
}
- return null;
+
+ ConstantValue result = _fold(left, right);
+ if (result == null) return result;
+ return _convertToJavaScriptConstant(result);
}
bool isDivide() => false;
@@ -510,137 +544,165 @@
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 JavaScriptRemainderOperation extends ArithmeticNumOperation {
- String get name => 'remainder';
+class RemainderOperation extends ArithmeticNumOperation {
+ @override
+ final String name = 'remainder';
- const JavaScriptRemainderOperation();
+ const RemainderOperation();
+ @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) {
- 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 _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;
+ }
}
- }
- 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);
+ ConstantValue result = _fold(left, right);
if (result != null && result.isNum) {
return _convertToJavaScriptConstant(result);
}
return result;
}
- apply(left, right) => _addOperation.apply(left, right);
+ @override
+ apply(left, right) => 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;
@@ -662,40 +724,76 @@
}
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.
@@ -725,32 +823,27 @@
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) {
- // 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 _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);
+ }
- 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);
+ BoolConstantValue result = _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.
@@ -769,29 +862,33 @@
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 {
- String get name => 'charCodeAt';
- const CodeUnitAtOperation();
- ConstantValue fold(ConstantValue left, ConstantValue right) => null;
- apply(left, right) => left.codeUnitAt(right);
-}
+ @override
+ final String name = 'charCodeAt';
-class CodeUnitAtRuntimeOperation extends CodeUnitAtOperation {
- const CodeUnitAtRuntimeOperation();
+ const CodeUnitAtOperation();
+
+ @override
IntConstantValue fold(ConstantValue left, ConstantValue right) {
if (left.isString && right.isInt) {
StringConstantValue stringConstant = left;
@@ -804,11 +901,18 @@
}
return null;
}
+
+ @override
+ apply(left, right) => left.codeUnitAt(right);
}
-class JavaScriptRoundOperation implements UnaryOperation {
- const JavaScriptRoundOperation();
- String get name => round.name;
+class RoundOperation implements UnaryOperation {
+ @override
+ final String name = 'round';
+
+ const RoundOperation();
+
+ @override
ConstantValue fold(ConstantValue constant) {
// Be careful to round() only values that do not throw on either the host or
// target platform.
@@ -846,8 +950,12 @@
}
class UnfoldedUnaryOperation implements UnaryOperation {
+ @override
final String name;
+
const UnfoldedUnaryOperation(this.name);
+
+ @override
ConstantValue fold(ConstantValue constant) {
return null;
}