Remove the Dart constant system and move all required classes into the
JavaScript constant system.
Change-Id: I2a31ea743270459284c42b615b0dffd1c7aa2584
Reviewed-on: https://dart-review.googlesource.com/c/93746
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/constant_system_dart.dart b/pkg/compiler/lib/src/constant_system_dart.dart
deleted file mode 100644
index c610490..0000000
--- a/pkg/compiler/lib/src/constant_system_dart.dart
+++ /dev/null
@@ -1,503 +0,0 @@
-// Copyright (c) 2012, 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.
-
-library dart2js.constant_system.dart;
-
-import 'constants/constant_system.dart';
-import 'constants/values.dart';
-import 'common_elements.dart' show CommonElements;
-import 'elements/types.dart';
-
-const DART_CONSTANT_SYSTEM = const DartConstantSystem();
-
-class BitNotOperation implements UnaryOperation {
- final String name = '~';
- const BitNotOperation();
- ConstantValue fold(ConstantValue constant) {
- if (constant.isInt) {
- IntConstantValue intConstant = constant;
- return DART_CONSTANT_SYSTEM.createInt(~intConstant.intValue);
- }
- return null;
- }
-}
-
-class NegateOperation implements UnaryOperation {
- final String name = 'negate';
- const NegateOperation();
- ConstantValue fold(ConstantValue constant) {
- if (constant.isInt) {
- IntConstantValue intConstant = constant;
- return DART_CONSTANT_SYSTEM.createInt(-intConstant.intValue);
- }
- if (constant.isDouble) {
- DoubleConstantValue doubleConstant = constant;
- return DART_CONSTANT_SYSTEM.createDouble(-doubleConstant.doubleValue);
- }
- return null;
- }
-}
-
-class NotOperation implements UnaryOperation {
- final String name = '!';
- const NotOperation();
- ConstantValue fold(ConstantValue constant) {
- if (constant.isBool) {
- BoolConstantValue boolConstant = constant;
- return DART_CONSTANT_SYSTEM.createBool(!boolConstant.boolValue);
- }
- return null;
- }
-}
-
-/// Operations that only work if both arguments are integers.
-abstract class BinaryBitOperation implements BinaryOperation {
- const BinaryBitOperation();
- 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 DART_CONSTANT_SYSTEM.createInt(resultValue);
- }
- return null;
- }
-
- BigInt foldInts(BigInt left, BigInt right);
-}
-
-class BitOrOperation extends BinaryBitOperation {
- final String name = '|';
- const BitOrOperation();
- BigInt foldInts(BigInt left, BigInt right) => left | right;
- apply(left, right) => left | right;
-}
-
-class BitAndOperation extends BinaryBitOperation {
- final String name = '&';
- const BitAndOperation();
- BigInt foldInts(BigInt left, BigInt right) => left & right;
- apply(left, right) => left & right;
-}
-
-class BitXorOperation extends BinaryBitOperation {
- final String name = '^';
- const BitXorOperation();
- BigInt foldInts(BigInt left, BigInt right) => left ^ right;
- apply(left, right) => left ^ right;
-}
-
-class ShiftLeftOperation extends BinaryBitOperation {
- final String name = '<<';
- const ShiftLeftOperation();
- BigInt foldInts(BigInt left, BigInt right) {
- // TODO(floitsch): find a better way to guard against excessive shifts to
- // the left.
- if (right > new BigInt.from(100) || right < BigInt.zero) return null;
- return left << right.toInt();
- }
-
- apply(left, right) => left << right;
-}
-
-class ShiftRightOperation extends BinaryBitOperation {
- 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;
-}
-
-abstract class BinaryBoolOperation implements BinaryOperation {
- const BinaryBoolOperation();
- ConstantValue fold(ConstantValue left, ConstantValue right) {
- if (left.isBool && right.isBool) {
- BoolConstantValue leftBool = left;
- BoolConstantValue rightBool = right;
- bool resultValue = foldBools(leftBool.boolValue, rightBool.boolValue);
- return DART_CONSTANT_SYSTEM.createBool(resultValue);
- }
- return null;
- }
-
- bool foldBools(bool left, bool right);
-}
-
-class BooleanAndOperation extends BinaryBoolOperation {
- final String name = '&&';
- const BooleanAndOperation();
- bool foldBools(bool left, bool right) => left && right;
- apply(left, right) => left && right;
-}
-
-class BooleanOrOperation extends BinaryBoolOperation {
- final String name = '||';
- const BooleanOrOperation();
- bool foldBools(bool left, bool right) => left || right;
- apply(left, right) => left || right;
-}
-
-abstract class ArithmeticNumOperation implements BinaryOperation {
- const ArithmeticNumOperation();
- 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 DART_CONSTANT_SYSTEM.createInt(foldedValue);
- } else {
- return DART_CONSTANT_SYSTEM.createDouble(foldedValue);
- }
- }
- return null;
- }
-
- bool isDivide() => false;
- bool isTruncatingDivide() => false;
- foldInts(BigInt left, BigInt right);
- foldNums(num left, num right);
-}
-
-class SubtractOperation extends ArithmeticNumOperation {
- final String name = '-';
- const SubtractOperation();
- BigInt foldInts(BigInt left, BigInt right) => left - right;
- num foldNums(num left, num right) => left - right;
- apply(left, right) => left - right;
-}
-
-class MultiplyOperation extends ArithmeticNumOperation {
- final String name = '*';
- const MultiplyOperation();
- BigInt foldInts(BigInt left, BigInt right) => left * right;
- num foldNums(num left, num right) => left * right;
- apply(left, right) => left * right;
-}
-
-class ModuloOperation extends ArithmeticNumOperation {
- final String name = '%';
- const ModuloOperation();
- BigInt foldInts(BigInt left, BigInt right) {
- if (right == BigInt.zero) return null;
- return left % right;
- }
-
- num foldNums(num left, num right) => left % right;
- apply(left, right) => left % right;
-}
-
-class RemainderOperation extends ArithmeticNumOperation {
- final String name = 'remainder';
- const RemainderOperation();
- BigInt foldInts(BigInt left, BigInt right) => null;
- // Not a defined constant operation.
- num foldNums(num left, num right) => null;
- apply(left, right) => left.remainder(right);
-}
-
-class TruncatingDivideOperation extends ArithmeticNumOperation {
- final String name = '~/';
- const TruncatingDivideOperation();
- BigInt foldInts(BigInt left, BigInt right) {
- if (right == BigInt.zero) return null;
- return left ~/ right;
- }
-
- BigInt foldNums(num left, num right) {
- num ratio = left / right;
- if (ratio.isNaN || ratio.isInfinite) return null;
- return new BigInt.from(ratio.truncate().toInt());
- }
-
- apply(left, right) => left ~/ right;
- bool isTruncatingDivide() => true;
-}
-
-class DivideOperation extends ArithmeticNumOperation {
- final String name = '/';
- const DivideOperation();
- double foldInts(BigInt left, BigInt right) => left / right;
- num foldNums(num left, num right) => left / right;
- bool isDivide() => true;
- apply(left, right) => left / right;
-}
-
-class AddOperation implements BinaryOperation {
- final String name = '+';
- const AddOperation();
- ConstantValue fold(ConstantValue left, ConstantValue right) {
- if (left.isInt && right.isInt) {
- IntConstantValue leftInt = left;
- IntConstantValue rightInt = right;
- BigInt result = leftInt.intValue + rightInt.intValue;
- return DART_CONSTANT_SYSTEM.createInt(result);
- } else if (left.isNum && right.isNum) {
- NumConstantValue leftNum = left;
- NumConstantValue rightNum = right;
- double result = leftNum.doubleValue + rightNum.doubleValue;
- return DART_CONSTANT_SYSTEM.createDouble(result);
- } else if (left.isString && right.isString) {
- StringConstantValue leftString = left;
- StringConstantValue rightString = right;
- String result = leftString.stringValue + rightString.stringValue;
- return DART_CONSTANT_SYSTEM.createString(result);
- } else {
- return null;
- }
- }
-
- apply(left, right) => left + right;
-}
-
-abstract class RelationalNumOperation implements BinaryOperation {
- const RelationalNumOperation();
- ConstantValue fold(ConstantValue left, ConstantValue right) {
- if (!left.isNum || !right.isNum) return null;
- bool foldedValue;
- if (left.isInt && right.isInt) {
- IntConstantValue leftInt = left;
- IntConstantValue rightInt = right;
- foldedValue = foldInts(leftInt.intValue, rightInt.intValue);
- } else {
- NumConstantValue leftNum = left;
- NumConstantValue rightNum = right;
- foldedValue = foldNums(leftNum.doubleValue, rightNum.doubleValue);
- }
- assert(foldedValue != null);
- return DART_CONSTANT_SYSTEM.createBool(foldedValue);
- }
-
- bool foldInts(BigInt left, BigInt right);
- bool foldNums(num left, num right);
-}
-
-class LessOperation extends RelationalNumOperation {
- final String name = '<';
- const LessOperation();
- bool foldInts(BigInt left, BigInt right) => left < right;
- bool foldNums(num left, num right) => left < right;
- apply(left, right) => left < right;
-}
-
-class LessEqualOperation extends RelationalNumOperation {
- final String name = '<=';
- const LessEqualOperation();
- bool foldInts(BigInt left, BigInt right) => left <= right;
- bool foldNums(num left, num right) => left <= right;
- apply(left, right) => left <= right;
-}
-
-class GreaterOperation extends RelationalNumOperation {
- final String name = '>';
- const GreaterOperation();
- bool foldInts(BigInt left, BigInt right) => left > right;
- bool foldNums(num left, num right) => left > right;
- apply(left, right) => left > right;
-}
-
-class GreaterEqualOperation extends RelationalNumOperation {
- final String name = '>=';
- const GreaterEqualOperation();
- bool foldInts(BigInt left, BigInt right) => left >= right;
- bool foldNums(num left, num right) => left >= right;
- apply(left, right) => left >= right;
-}
-
-class EqualsOperation implements BinaryOperation {
- final String name = '==';
- const EqualsOperation();
- ConstantValue fold(ConstantValue left, ConstantValue right) {
- // Numbers need to be treated specially because: NaN != NaN, -0.0 == 0.0,
- // and 1 == 1.0.
- if (left.isInt && right.isInt) {
- IntConstantValue leftInt = left;
- IntConstantValue rightInt = right;
- bool result = leftInt.intValue == rightInt.intValue;
- return DART_CONSTANT_SYSTEM.createBool(result);
- }
-
- if (left.isNum && right.isNum) {
- NumConstantValue leftNum = left;
- NumConstantValue rightNum = right;
- bool result = leftNum.doubleValue == rightNum.doubleValue;
- return DART_CONSTANT_SYSTEM.createBool(result);
- }
-
- if (left.isConstructedObject) {
- if (right.isNull) {
- return DART_CONSTANT_SYSTEM.createBool(false);
- }
- // Unless we know that the user-defined object does not implement the
- // equality operator we cannot fold here.
- return null;
- }
-
- return DART_CONSTANT_SYSTEM.createBool(left == right);
- }
-
- apply(left, right) => left == right;
-}
-
-class IdentityOperation implements BinaryOperation {
- final String name = '===';
- const IdentityOperation();
- 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 DART_CONSTANT_SYSTEM.createBool(left == right);
- }
-
- apply(left, right) => identical(left, right);
-}
-
-class IfNullOperation implements BinaryOperation {
- final String name = '??';
- const IfNullOperation();
- ConstantValue fold(ConstantValue left, ConstantValue right) {
- if (left.isNull) return right;
- return left;
- }
-
- 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);
-}
-
-class CodeUnitAtRuntimeOperation extends CodeUnitAtOperation {
- const CodeUnitAtRuntimeOperation();
- IntConstantValue fold(ConstantValue left, ConstantValue right) {
- if (left.isString && right.isInt) {
- StringConstantValue stringConstant = left;
- IntConstantValue indexConstant = right;
- String string = stringConstant.stringValue;
- int index = indexConstant.intValue.toInt();
- if (index < 0 || index >= string.length) return null;
- int value = string.codeUnitAt(index);
- return DART_CONSTANT_SYSTEM.createIntFromInt(value);
- }
- return null;
- }
-}
-
-class UnfoldedUnaryOperation implements UnaryOperation {
- final String name;
- const UnfoldedUnaryOperation(this.name);
- ConstantValue fold(ConstantValue constant) {
- return null;
- }
-}
-
-/// A constant system implementing the Dart semantics. This system relies on
-/// the underlying runtime-system. That is, if dart2js is run in an environment
-/// that doesn't correctly implement Dart's semantics this constant system will
-/// not return the correct values.
-class DartConstantSystem extends ConstantSystem {
- final add = const AddOperation();
- final bitAnd = const BitAndOperation();
- final bitNot = const BitNotOperation();
- final bitOr = const BitOrOperation();
- final bitXor = const BitXorOperation();
- final booleanAnd = const BooleanAndOperation();
- final booleanOr = const BooleanOrOperation();
- final divide = const DivideOperation();
- final equal = const EqualsOperation();
- final greaterEqual = const GreaterEqualOperation();
- final greater = const GreaterOperation();
- final identity = const IdentityOperation();
- final ifNull = const IfNullOperation();
- final lessEqual = const LessEqualOperation();
- final less = const LessOperation();
- final modulo = const ModuloOperation();
- final multiply = const MultiplyOperation();
- final negate = const NegateOperation();
- final not = const NotOperation();
- final remainder = const RemainderOperation();
- final shiftLeft = const ShiftLeftOperation();
- final shiftRight = const ShiftRightOperation();
- final subtract = const SubtractOperation();
- final truncatingDivide = const TruncatingDivideOperation();
- final codeUnitAt = const CodeUnitAtOperation();
- final round = const UnfoldedUnaryOperation('round');
- final abs = const UnfoldedUnaryOperation('abs');
-
- // TODO(johnniwinther): Delete this (embed parts into JavaScript constant
- // system where needed).
- const DartConstantSystem();
-
- @override
- IntConstantValue createInt(BigInt i) => new IntConstantValue(i);
-
- @override
- DoubleConstantValue createDouble(double d) => new DoubleConstantValue(d);
-
- @override
- StringConstantValue createString(String string) {
- return new StringConstantValue(string);
- }
-
- @override
- BoolConstantValue createBool(bool value) => new BoolConstantValue(value);
-
- @override
- NullConstantValue createNull() => new NullConstantValue();
-
- @override
- ListConstantValue createList(InterfaceType type, List<ConstantValue> values) {
- return new ListConstantValue(type, values);
- }
-
- @override
- SetConstantValue createSet(CommonElements commonElements, InterfaceType type,
- List<ConstantValue> values) =>
- new SetConstantValue(type, values);
-
- @override
- MapConstantValue createMap(CommonElements commonElements, InterfaceType type,
- List<ConstantValue> keys, List<ConstantValue> values) {
- throw new UnsupportedError('DartConstantSystem.createMap');
- }
-
- @override
- ConstantValue createType(CommonElements commonElements, DartType type) {
- InterfaceType implementationType = commonElements.typeLiteralType;
- return new TypeConstantValue(type, implementationType);
- }
-
- @override
- ConstantValue createSymbol(CommonElements commonElements, String text) {
- throw new UnsupportedError('DartConstantSystem.createSymbol');
- }
-
- bool isInt(ConstantValue constant) => constant.isInt;
- bool isDouble(ConstantValue constant) => constant.isDouble;
- bool isString(ConstantValue constant) => constant.isString;
- bool isBool(ConstantValue constant) => constant.isBool;
- bool isNull(ConstantValue constant) => constant.isNull;
-
- bool isSubtype(DartTypes types, DartType s, DartType t) {
- return types.isSubtype(s, t);
- }
-}
diff --git a/pkg/compiler/lib/src/js_backend/constant_handler_javascript.dart b/pkg/compiler/lib/src/js_backend/constant_handler_javascript.dart
index 6b9de6a..fe256f7 100644
--- a/pkg/compiler/lib/src/js_backend/constant_handler_javascript.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_handler_javascript.dart
@@ -5,7 +5,6 @@
import '../compile_time_constants.dart';
import '../compiler.dart' show Compiler;
import '../constants/constant_system.dart';
-import '../constant_system_dart.dart';
import '../elements/entities.dart';
import 'constant_system_javascript.dart';
@@ -17,18 +16,16 @@
/// [DartConstantCompiler] for the frontend interpretation of the constants and
/// to a [JavaScriptConstantCompiler] for the backend interpretation.
class JavaScriptConstantTask extends ConstantCompilerTask {
- ConstantSystem dartConstantSystem;
JavaScriptConstantCompiler jsConstantCompiler;
JavaScriptConstantTask(Compiler compiler)
- : this.dartConstantSystem = const DartConstantSystem(),
- this.jsConstantCompiler = new JavaScriptConstantCompiler(),
+ : this.jsConstantCompiler = new JavaScriptConstantCompiler(),
super(compiler.measurer);
String get name => 'ConstantHandler';
@override
- ConstantSystem get constantSystem => dartConstantSystem;
+ ConstantSystem get constantSystem => JavaScriptConstantSystem.only;
}
/// The [JavaScriptConstantCompiler] is used to keep track of compile-time
diff --git a/pkg/compiler/lib/src/js_backend/constant_system_javascript.dart b/pkg/compiler/lib/src/js_backend/constant_system_javascript.dart
index 5d3c205..3b1b3ce 100644
--- a/pkg/compiler/lib/src/js_backend/constant_system_javascript.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_system_javascript.dart
@@ -4,21 +4,21 @@
library dart2js.constant_system.js;
-import '../constant_system_dart.dart';
import '../constants/constant_system.dart';
import '../constants/values.dart';
import '../common_elements.dart' show CommonElements;
import '../elements/types.dart';
import '../elements/entities.dart';
-class JavaScriptBitNotOperation extends BitNotOperation {
+class JavaScriptBitNotOperation implements UnaryOperation {
+ final String name = '~';
const JavaScriptBitNotOperation();
ConstantValue fold(ConstantValue constant) {
if (JavaScriptConstantSystem.only.isInt(constant)) {
// In JavaScript we don't check for -0 and treat it as if it was zero.
if (constant.isMinusZero) {
- constant = DART_CONSTANT_SYSTEM.createInt(BigInt.zero);
+ constant = JavaScriptConstantSystem.only.createInt(BigInt.zero);
}
IntConstantValue intConstant = constant;
// We convert the result of bit-operations to 32 bit unsigned integers.
@@ -28,6 +28,23 @@
}
}
+/// Operations that only work if both arguments are integers.
+abstract class BinaryBitOperation implements BinaryOperation {
+ const BinaryBitOperation();
+ 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 JavaScriptConstantSystem.only.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 {
@@ -40,10 +57,10 @@
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 = DART_CONSTANT_SYSTEM.createInt(BigInt.zero);
+ left = JavaScriptConstantSystem.only.createInt(BigInt.zero);
}
if (right.isMinusZero) {
- right = DART_CONSTANT_SYSTEM.createInt(BigInt.zero);
+ right = JavaScriptConstantSystem.only.createInt(BigInt.zero);
}
IntConstantValue result = dartBitOperation.fold(left, right);
if (result != null) {
@@ -56,6 +73,17 @@
apply(left, right) => dartBitOperation.apply(left, right);
}
+class ShiftRightOperation extends BinaryBitOperation {
+ 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());
@@ -80,13 +108,30 @@
truncatedValue -= BigInt.two * (truncatedValue & SIGN_BIT);
}
if (value != truncatedValue) {
- left = DART_CONSTANT_SYSTEM.createInt(truncatedValue);
+ left = JavaScriptConstantSystem.only.createInt(truncatedValue);
}
}
return super.fold(left, right);
}
}
+class NegateOperation implements UnaryOperation {
+ final String name = 'negate';
+ const NegateOperation();
+ ConstantValue fold(ConstantValue constant) {
+ if (constant.isInt) {
+ IntConstantValue intConstant = constant;
+ return JavaScriptConstantSystem.only.createInt(-intConstant.intValue);
+ }
+ if (constant.isDouble) {
+ DoubleConstantValue doubleConstant = constant;
+ return JavaScriptConstantSystem.only
+ .createDouble(-doubleConstant.doubleValue);
+ }
+ return null;
+ }
+}
+
class JavaScriptNegateOperation implements UnaryOperation {
final NegateOperation dartNegateOperation = const NegateOperation();
@@ -105,6 +150,33 @@
}
}
+class AddOperation implements BinaryOperation {
+ final String name = '+';
+ const AddOperation();
+ ConstantValue fold(ConstantValue left, ConstantValue right) {
+ if (left.isInt && right.isInt) {
+ IntConstantValue leftInt = left;
+ IntConstantValue rightInt = right;
+ BigInt result = leftInt.intValue + rightInt.intValue;
+ return JavaScriptConstantSystem.only.createInt(result);
+ } else if (left.isNum && right.isNum) {
+ NumConstantValue leftNum = left;
+ NumConstantValue rightNum = right;
+ double result = leftNum.doubleValue + rightNum.doubleValue;
+ return JavaScriptConstantSystem.only.createDouble(result);
+ } else if (left.isString && right.isString) {
+ StringConstantValue leftString = left;
+ StringConstantValue rightString = right;
+ String result = leftString.stringValue + rightString.stringValue;
+ return JavaScriptConstantSystem.only.createString(result);
+ } else {
+ return null;
+ }
+ }
+
+ apply(left, right) => left + right;
+}
+
class JavaScriptAddOperation implements BinaryOperation {
final _addOperation = const AddOperation();
String get name => _addOperation.name;
@@ -122,6 +194,38 @@
apply(left, right) => _addOperation.apply(left, right);
}
+abstract class ArithmeticNumOperation implements BinaryOperation {
+ const ArithmeticNumOperation();
+ 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 JavaScriptConstantSystem.only.createInt(foldedValue);
+ } else {
+ return JavaScriptConstantSystem.only.createDouble(foldedValue);
+ }
+ }
+ return null;
+ }
+
+ bool isDivide() => false;
+ bool isTruncatingDivide() => false;
+ foldInts(BigInt left, BigInt right);
+ foldNums(num left, num right);
+}
+
class JavaScriptRemainderOperation extends ArithmeticNumOperation {
String get name => 'remainder';
@@ -152,6 +256,20 @@
apply(left, right) => dartArithmeticOperation.apply(left, right);
}
+class IdentityOperation implements BinaryOperation {
+ final String name = '===';
+ const IdentityOperation();
+ 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 JavaScriptConstantSystem.only.createBool(left == right);
+ }
+
+ apply(left, right) => identical(left, right);
+}
+
class JavaScriptIdentityOperation implements BinaryOperation {
final IdentityOperation dartIdentityOperation = const IdentityOperation();
@@ -184,7 +302,7 @@
class JavaScriptRoundOperation implements UnaryOperation {
const JavaScriptRoundOperation();
- String get name => DART_CONSTANT_SYSTEM.round.name;
+ String get name => JavaScriptConstantSystem.only.round.name;
ConstantValue fold(ConstantValue constant) {
// Be careful to round() only values that do not throw on either the host or
// target platform.
@@ -221,6 +339,267 @@
}
}
+class BitAndOperation extends BinaryBitOperation {
+ final String name = '&';
+ const BitAndOperation();
+ BigInt foldInts(BigInt left, BigInt right) => left & right;
+ apply(left, right) => left & right;
+}
+
+class BitOrOperation extends BinaryBitOperation {
+ final String name = '|';
+ const BitOrOperation();
+ BigInt foldInts(BigInt left, BigInt right) => left | right;
+ apply(left, right) => left | right;
+}
+
+class BitXorOperation extends BinaryBitOperation {
+ final String name = '^';
+ const BitXorOperation();
+ BigInt foldInts(BigInt left, BigInt right) => left ^ right;
+ apply(left, right) => left ^ right;
+}
+
+abstract class BinaryBoolOperation implements BinaryOperation {
+ const BinaryBoolOperation();
+ ConstantValue fold(ConstantValue left, ConstantValue right) {
+ if (left.isBool && right.isBool) {
+ BoolConstantValue leftBool = left;
+ BoolConstantValue rightBool = right;
+ bool resultValue = foldBools(leftBool.boolValue, rightBool.boolValue);
+ return JavaScriptConstantSystem.only.createBool(resultValue);
+ }
+ return null;
+ }
+
+ bool foldBools(bool left, bool right);
+}
+
+class BooleanAndOperation extends BinaryBoolOperation {
+ final String name = '&&';
+ const BooleanAndOperation();
+ bool foldBools(bool left, bool right) => left && right;
+ apply(left, right) => left && right;
+}
+
+class BooleanOrOperation extends BinaryBoolOperation {
+ final String name = '||';
+ const BooleanOrOperation();
+ bool foldBools(bool left, bool right) => left || right;
+ apply(left, right) => left || right;
+}
+
+class DivideOperation extends ArithmeticNumOperation {
+ final String name = '/';
+ const DivideOperation();
+ double foldInts(BigInt left, BigInt right) => left / right;
+ num foldNums(num left, num right) => left / right;
+ bool isDivide() => true;
+ apply(left, right) => left / right;
+}
+
+class EqualsOperation implements BinaryOperation {
+ final String name = '==';
+ const EqualsOperation();
+ ConstantValue fold(ConstantValue left, ConstantValue right) {
+ // Numbers need to be treated specially because: NaN != NaN, -0.0 == 0.0,
+ // and 1 == 1.0.
+ if (left.isInt && right.isInt) {
+ IntConstantValue leftInt = left;
+ IntConstantValue rightInt = right;
+ bool result = leftInt.intValue == rightInt.intValue;
+ return JavaScriptConstantSystem.only.createBool(result);
+ }
+
+ if (left.isNum && right.isNum) {
+ NumConstantValue leftNum = left;
+ NumConstantValue rightNum = right;
+ bool result = leftNum.doubleValue == rightNum.doubleValue;
+ return JavaScriptConstantSystem.only.createBool(result);
+ }
+
+ if (left.isConstructedObject) {
+ if (right.isNull) {
+ return JavaScriptConstantSystem.only.createBool(false);
+ }
+ // Unless we know that the user-defined object does not implement the
+ // equality operator we cannot fold here.
+ return null;
+ }
+
+ return JavaScriptConstantSystem.only.createBool(left == right);
+ }
+
+ apply(left, right) => left == right;
+}
+
+abstract class RelationalNumOperation implements BinaryOperation {
+ const RelationalNumOperation();
+ ConstantValue fold(ConstantValue left, ConstantValue right) {
+ if (!left.isNum || !right.isNum) return null;
+ bool foldedValue;
+ if (left.isInt && right.isInt) {
+ IntConstantValue leftInt = left;
+ IntConstantValue rightInt = right;
+ foldedValue = foldInts(leftInt.intValue, rightInt.intValue);
+ } else {
+ NumConstantValue leftNum = left;
+ NumConstantValue rightNum = right;
+ foldedValue = foldNums(leftNum.doubleValue, rightNum.doubleValue);
+ }
+ assert(foldedValue != null);
+ return JavaScriptConstantSystem.only.createBool(foldedValue);
+ }
+
+ bool foldInts(BigInt left, BigInt right);
+ bool foldNums(num left, num right);
+}
+
+class GreaterEqualOperation extends RelationalNumOperation {
+ final String name = '>=';
+ const GreaterEqualOperation();
+ bool foldInts(BigInt left, BigInt right) => left >= right;
+ bool foldNums(num left, num right) => left >= right;
+ apply(left, right) => left >= right;
+}
+
+class GreaterOperation extends RelationalNumOperation {
+ final String name = '>';
+ const GreaterOperation();
+ bool foldInts(BigInt left, BigInt right) => left > right;
+ bool foldNums(num left, num right) => left > right;
+ apply(left, right) => left > right;
+}
+
+class IfNullOperation implements BinaryOperation {
+ final String name = '??';
+ const IfNullOperation();
+ ConstantValue fold(ConstantValue left, ConstantValue right) {
+ if (left.isNull) return right;
+ return left;
+ }
+
+ apply(left, right) => left ?? right;
+}
+
+class LessEqualOperation extends RelationalNumOperation {
+ final String name = '<=';
+ const LessEqualOperation();
+ bool foldInts(BigInt left, BigInt right) => left <= right;
+ bool foldNums(num left, num right) => left <= right;
+ apply(left, right) => left <= right;
+}
+
+class LessOperation extends RelationalNumOperation {
+ final String name = '<';
+ const LessOperation();
+ bool foldInts(BigInt left, BigInt right) => left < right;
+ bool foldNums(num left, num right) => left < right;
+ apply(left, right) => left < right;
+}
+
+class ModuloOperation extends ArithmeticNumOperation {
+ final String name = '%';
+ const ModuloOperation();
+ BigInt foldInts(BigInt left, BigInt right) {
+ if (right == BigInt.zero) return null;
+ return left % right;
+ }
+
+ num foldNums(num left, num right) => left % right;
+ apply(left, right) => left % right;
+}
+
+class MultiplyOperation extends ArithmeticNumOperation {
+ final String name = '*';
+ const MultiplyOperation();
+ BigInt foldInts(BigInt left, BigInt right) => left * right;
+ num foldNums(num left, num right) => left * right;
+ apply(left, right) => left * right;
+}
+
+class NotOperation implements UnaryOperation {
+ final String name = '!';
+ const NotOperation();
+ ConstantValue fold(ConstantValue constant) {
+ if (constant.isBool) {
+ BoolConstantValue boolConstant = constant;
+ return JavaScriptConstantSystem.only.createBool(!boolConstant.boolValue);
+ }
+ return null;
+ }
+}
+
+class ShiftLeftOperation extends BinaryBitOperation {
+ final String name = '<<';
+ const ShiftLeftOperation();
+ BigInt foldInts(BigInt left, BigInt right) {
+ // TODO(floitsch): find a better way to guard against excessive shifts to
+ // the left.
+ if (right > new BigInt.from(100) || right < BigInt.zero) return null;
+ return left << right.toInt();
+ }
+
+ apply(left, right) => left << right;
+}
+
+class SubtractOperation extends ArithmeticNumOperation {
+ final String name = '-';
+ const SubtractOperation();
+ BigInt foldInts(BigInt left, BigInt right) => left - right;
+ num foldNums(num left, num right) => left - right;
+ apply(left, right) => left - right;
+}
+
+class TruncatingDivideOperation extends ArithmeticNumOperation {
+ final String name = '~/';
+ const TruncatingDivideOperation();
+ BigInt foldInts(BigInt left, BigInt right) {
+ if (right == BigInt.zero) return null;
+ return left ~/ right;
+ }
+
+ BigInt foldNums(num left, num right) {
+ num ratio = left / right;
+ if (ratio.isNaN || ratio.isInfinite) return null;
+ return new BigInt.from(ratio.truncate().toInt());
+ }
+
+ apply(left, right) => left ~/ right;
+ bool isTruncatingDivide() => true;
+}
+
+class CodeUnitAtOperation implements BinaryOperation {
+ String get name => 'charCodeAt';
+ const CodeUnitAtOperation();
+ ConstantValue fold(ConstantValue left, ConstantValue right) => null;
+ apply(left, right) => left.codeUnitAt(right);
+}
+
+class CodeUnitAtRuntimeOperation extends CodeUnitAtOperation {
+ const CodeUnitAtRuntimeOperation();
+ IntConstantValue fold(ConstantValue left, ConstantValue right) {
+ if (left.isString && right.isInt) {
+ StringConstantValue stringConstant = left;
+ IntConstantValue indexConstant = right;
+ String string = stringConstant.stringValue;
+ int index = indexConstant.intValue.toInt();
+ if (index < 0 || index >= string.length) return null;
+ int value = string.codeUnitAt(index);
+ return JavaScriptConstantSystem.only.createIntFromInt(value);
+ }
+ return null;
+ }
+}
+
+class UnfoldedUnaryOperation implements UnaryOperation {
+ final String name;
+ const UnfoldedUnaryOperation(this.name);
+ ConstantValue fold(ConstantValue constant) {
+ return null;
+ }
+}
+
/// Constant system following the semantics for Dart code that has been
/// compiled to JavaScript.
class JavaScriptConstantSystem extends ConstantSystem {
diff --git a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
index a4bfd96..f0c2da5 100644
--- a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
+++ b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
@@ -2,9 +2,9 @@
// 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 '../constant_system_dart.dart';
import '../constants/constant_system.dart';
import '../constants/values.dart';
+import '../js_backend/constant_system_javascript.dart';
import '../world.dart' show JClosedWorld;
import 'nodes.dart';
import 'optimize.dart';
diff --git a/tests/compiler/dart2js/analyses/dart2js_allowed.json b/tests/compiler/dart2js/analyses/dart2js_allowed.json
index 916228a..215449d 100644
--- a/tests/compiler/dart2js/analyses/dart2js_allowed.json
+++ b/tests/compiler/dart2js/analyses/dart2js_allowed.json
@@ -147,7 +147,23 @@
"Dynamic invocation of '-'.": 1
},
"pkg/compiler/lib/src/js_backend/constant_system_javascript.dart": {
- "Dynamic invocation of 'remainder'.": 1
+ "Dynamic invocation of '>>'.": 1,
+ "Dynamic invocation of '+'.": 1,
+ "Dynamic invocation of 'remainder'.": 1,
+ "Dynamic invocation of '&'.": 1,
+ "Dynamic invocation of '|'.": 1,
+ "Dynamic invocation of '^'.": 1,
+ "Dynamic invocation of '/'.": 1,
+ "Dynamic invocation of '>='.": 1,
+ "Dynamic invocation of '>'.": 1,
+ "Dynamic invocation of '<='.": 1,
+ "Dynamic invocation of '<'.": 1,
+ "Dynamic invocation of '%'.": 1,
+ "Dynamic invocation of '*'.": 1,
+ "Dynamic invocation of '<<'.": 1,
+ "Dynamic invocation of '-'.": 1,
+ "Dynamic invocation of '~/'.": 1,
+ "Dynamic invocation of 'codeUnitAt'.": 1
},
"pkg/compiler/lib/src/serialization/binary_sink.dart": {
"Dynamic access of 'index'.": 1
@@ -200,25 +216,6 @@
"Dynamic access of 'usedBy'.": 2,
"Dynamic access of 'inputs'.": 1
},
- "pkg/compiler/lib/src/constant_system_dart.dart": {
- "Dynamic invocation of '|'.": 1,
- "Dynamic invocation of '&'.": 1,
- "Dynamic invocation of '^'.": 1,
- "Dynamic invocation of '<<'.": 1,
- "Dynamic invocation of '>>'.": 1,
- "Dynamic invocation of '-'.": 1,
- "Dynamic invocation of '*'.": 1,
- "Dynamic invocation of '%'.": 1,
- "Dynamic invocation of 'remainder'.": 1,
- "Dynamic invocation of '~/'.": 1,
- "Dynamic invocation of '/'.": 1,
- "Dynamic invocation of '+'.": 1,
- "Dynamic invocation of '<'.": 1,
- "Dynamic invocation of '<='.": 1,
- "Dynamic invocation of '>'.": 1,
- "Dynamic invocation of '>='.": 1,
- "Dynamic invocation of 'codeUnitAt'.": 1
- },
"third_party/pkg/dart2js_info/lib/src/util.dart": {
"Dynamic access of 'name'.": 1,
"Dynamic invocation of '-'.": 1
diff --git a/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart b/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
index 0660b7f..98366be 100644
--- a/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
+++ b/tests/compiler/dart2js/model/constant_expression_evaluate_test.dart
@@ -147,7 +147,6 @@
const ConstantData('true', 'BoolConstant(true)'),
const ConstantData('0', 'IntConstant(0)'),
const ConstantData('0.0', 'IntConstant(0)'),
- const ConstantData('0.5', 'DoubleConstant(0.5)'),
const ConstantData('"foo"', 'StringConstant("foo")'),
const ConstantData('1 + 2', 'IntConstant(3)'),
const ConstantData('-(1)', 'IntConstant(-1)'),