| Index: sdk/lib/_internal/compiler/implementation/cps_ir/const_expression.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/cps_ir/const_expression.dart b/sdk/lib/_internal/compiler/implementation/cps_ir/const_expression.dart
|
| index f451e0f785f6d1322702e4fcc0c622940340328b..78e40583dd134afc06776b201bcddc234c02f925 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/cps_ir/const_expression.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/cps_ir/const_expression.dart
|
| @@ -21,15 +21,28 @@ import '../universe/universe.dart';
|
| /// multiple `const` constructors may be used to create the same object, and
|
| /// different `const` variables may hold the same value.
|
| abstract class ConstExp {
|
| + /// Returns the value of this constant expression.
|
| + Constant get value;
|
| +
|
| + // TODO(johnniwinther): Unify precedence handled between constants, front-end
|
| + // and back-end.
|
| + int get precedence => 16;
|
| +
|
| accept(ConstExpVisitor visitor);
|
| +
|
| + String toString() {
|
| + ConstExpPrinter printer = new ConstExpPrinter();
|
| + accept(printer);
|
| + return printer.toString();
|
| + }
|
| }
|
|
|
| /// Boolean, int, double, string, or null constant.
|
| class PrimitiveConstExp extends ConstExp {
|
| - final PrimitiveConstant constant;
|
| + final PrimitiveConstant value;
|
|
|
| - PrimitiveConstExp(this.constant) {
|
| - assert(constant != null);
|
| + PrimitiveConstExp(this.value) {
|
| + assert(value != null);
|
| }
|
|
|
| accept(ConstExpVisitor visitor) => visitor.visitPrimitive(this);
|
| @@ -37,33 +50,40 @@ class PrimitiveConstExp extends ConstExp {
|
|
|
| /// Literal list constant.
|
| class ListConstExp extends ConstExp {
|
| + final ListConstant value;
|
| final GenericType type;
|
| final List<ConstExp> values;
|
|
|
| - ListConstExp(this.type, this.values);
|
| + ListConstExp(this.value, this.type, this.values);
|
|
|
| accept(ConstExpVisitor visitor) => visitor.visitList(this);
|
| }
|
|
|
| /// Literal map constant.
|
| class MapConstExp extends ConstExp {
|
| + final MapConstant value;
|
| final GenericType type;
|
| final List<ConstExp> keys;
|
| final List<ConstExp> values;
|
|
|
| - MapConstExp(this.type, this.keys, this.values);
|
| + MapConstExp(this.value, this.type, this.keys, this.values);
|
|
|
| accept(ConstExpVisitor visitor) => visitor.visitMap(this);
|
| }
|
|
|
| /// Invocation of a const constructor.
|
| class ConstructorConstExp extends ConstExp {
|
| + final Constant value;
|
| final GenericType type;
|
| final FunctionElement target;
|
| final Selector selector;
|
| final List<ConstExp> arguments;
|
|
|
| - ConstructorConstExp(this.type, this.target, this.selector, this.arguments) {
|
| + ConstructorConstExp(this.value,
|
| + this.type,
|
| + this.target,
|
| + this.selector,
|
| + this.arguments) {
|
| assert(type.element == target.enclosingClass);
|
| }
|
|
|
| @@ -71,29 +91,33 @@ class ConstructorConstExp extends ConstExp {
|
| }
|
|
|
| /// String literal with juxtaposition and/or interpolations.
|
| +// TODO(johnniwinther): Do we need this?
|
| class ConcatenateConstExp extends ConstExp {
|
| + final StringConstant value;
|
| final List<ConstExp> arguments;
|
|
|
| - ConcatenateConstExp(this.arguments);
|
| + ConcatenateConstExp(this.value, this.arguments);
|
|
|
| accept(ConstExpVisitor visitor) => visitor.visitConcatenate(this);
|
| }
|
|
|
| /// Symbol literal.
|
| class SymbolConstExp extends ConstExp {
|
| + final ConstructedConstant value;
|
| final String name;
|
|
|
| - SymbolConstExp(this.name);
|
| + SymbolConstExp(this.value, this.name);
|
|
|
| accept(ConstExpVisitor visitor) => visitor.visitSymbol(this);
|
| }
|
|
|
| /// Type literal.
|
| class TypeConstExp extends ConstExp {
|
| + final TypeConstant value;
|
| /// Either [DynamicType] or a raw [GenericType].
|
| final DartType type;
|
|
|
| - TypeConstExp(this.type) {
|
| + TypeConstExp(this.value, this.type) {
|
| assert(type is GenericType || type is DynamicType);
|
| }
|
|
|
| @@ -102,22 +126,98 @@ class TypeConstExp extends ConstExp {
|
|
|
| /// A constant local, top-level, or static variable.
|
| class VariableConstExp extends ConstExp {
|
| + final Constant value;
|
| final VariableElement element;
|
|
|
| - VariableConstExp(this.element);
|
| + VariableConstExp(this.value, this.element);
|
|
|
| accept(ConstExpVisitor visitor) => visitor.visitVariable(this);
|
| }
|
|
|
| /// Reference to a top-level or static function.
|
| class FunctionConstExp extends ConstExp {
|
| + final FunctionConstant value;
|
| final FunctionElement element;
|
|
|
| - FunctionConstExp(this.element);
|
| + FunctionConstExp(this.value, this.element);
|
|
|
| accept(ConstExpVisitor visitor) => visitor.visitFunction(this);
|
| }
|
|
|
| +/// A constant binary expression like `a * b` or `identical(a, b)`.
|
| +class BinaryConstExp extends ConstExp {
|
| + final Constant value;
|
| + final ConstExp left;
|
| + final String operator;
|
| + final ConstExp right;
|
| +
|
| + BinaryConstExp(this.value, this.left, this.operator, this.right) {
|
| + assert(PRECEDENCE_MAP[operator] != null);
|
| + }
|
| +
|
| + accept(ConstExpVisitor visitor) => visitor.visitBinary(this);
|
| +
|
| + int get precedence => PRECEDENCE_MAP[operator];
|
| +
|
| + static const Map<String, int> PRECEDENCE_MAP = const {
|
| + 'identical': 15,
|
| + '==': 6,
|
| + '!=': 6,
|
| + '&&': 5,
|
| + '||': 4,
|
| + '^': 9,
|
| + '&': 10,
|
| + '|': 8,
|
| + '>>': 11,
|
| + '<<': 11,
|
| + '+': 12,
|
| + '-': 12,
|
| + '*': 13,
|
| + '/': 13,
|
| + '~/': 13,
|
| + '>': 7,
|
| + '<': 7,
|
| + '>=': 7,
|
| + '<=': 7,
|
| + '%': 13,
|
| + };
|
| +}
|
| +
|
| +/// A unary constant expression like `-a`.
|
| +class UnaryConstExp extends ConstExp {
|
| + final Constant value;
|
| + final String operator;
|
| + final ConstExp expression;
|
| +
|
| + UnaryConstExp(this.value, this.operator, this.expression) {
|
| + assert(PRECEDENCE_MAP[operator] != null);
|
| + }
|
| +
|
| + accept(ConstExpVisitor visitor) => visitor.visitUnary(this);
|
| +
|
| + int get precedence => PRECEDENCE_MAP[operator];
|
| +
|
| + static const Map<String, int> PRECEDENCE_MAP = const {
|
| + '!': 14,
|
| + '~': 14,
|
| + '-': 14,
|
| + };
|
| +}
|
| +
|
| +/// A constant conditional expression like `a ? b : c`.
|
| +class ConditionalConstExp extends ConstExp {
|
| + final Constant value;
|
| + final ConstExp condition;
|
| + final ConstExp trueExp;
|
| + final ConstExp falseExp;
|
| +
|
| + ConditionalConstExp(this.value, this.condition, this.trueExp, this.falseExp);
|
| +
|
| + accept(ConstExpVisitor visitor) => visitor.visitConditional(this);
|
| +
|
| + int get precedence => 3;
|
| +}
|
| +
|
| abstract class ConstExpVisitor<T> {
|
| T visit(ConstExp constant) => constant.accept(this);
|
|
|
| @@ -130,6 +230,9 @@ abstract class ConstExpVisitor<T> {
|
| T visitType(TypeConstExp exp);
|
| T visitVariable(VariableConstExp exp);
|
| T visitFunction(FunctionConstExp exp);
|
| + T visitBinary(BinaryConstExp exp);
|
| + T visitUnary(UnaryConstExp exp);
|
| + T visitConditional(ConditionalConstExp exp);
|
| }
|
|
|
| /// Represents the declaration of a constant [element] with value [expression].
|
| @@ -139,3 +242,157 @@ class ConstDeclaration {
|
|
|
| ConstDeclaration(this.element, this.expression);
|
| }
|
| +
|
| +class ConstExpPrinter extends ConstExpVisitor {
|
| + final StringBuffer sb = new StringBuffer();
|
| +
|
| + write(ConstExp parent, ConstExp child, {bool leftAssociative: true}) {
|
| + if (child.precedence < parent.precedence ||
|
| + !leftAssociative && child.precedence == parent.precedence) {
|
| + sb.write('(');
|
| + child.accept(this);
|
| + sb.write(')');
|
| + } else {
|
| + child.accept(this);
|
| + }
|
| + }
|
| +
|
| + writeTypeArguments(InterfaceType type) {
|
| + if (type.treatAsRaw) return;
|
| + sb.write('<');
|
| + bool needsComma = false;
|
| + for (DartType value in type.typeArguments) {
|
| + if (needsComma) {
|
| + sb.write(', ');
|
| + }
|
| + sb.write(value);
|
| + needsComma = true;
|
| + }
|
| + sb.write('>');
|
| + }
|
| +
|
| + visitPrimitive(PrimitiveConstExp exp) {
|
| + sb.write(exp.value.toSyntax());
|
| + }
|
| +
|
| + visitList(ListConstExp exp) {
|
| + sb.write('const ');
|
| + writeTypeArguments(exp.type);
|
| + sb.write('[');
|
| + bool needsComma = false;
|
| + for (ConstExp value in exp.values) {
|
| + if (needsComma) {
|
| + sb.write(', ');
|
| + }
|
| + visit(value);
|
| + needsComma = true;
|
| + }
|
| + sb.write(']');
|
| + }
|
| +
|
| + visitMap(MapConstExp exp) {
|
| + sb.write('const ');
|
| + writeTypeArguments(exp.type);
|
| + sb.write('{');
|
| + for (int index = 0; index < exp.keys.length; index++) {
|
| + if (index > 0) {
|
| + sb.write(', ');
|
| + }
|
| + visit(exp.keys[index]);
|
| + sb.write(': ');
|
| + visit(exp.values[index]);
|
| + }
|
| + sb.write('}');
|
| + }
|
| +
|
| + visitConstructor(ConstructorConstExp exp) {
|
| + sb.write('const ');
|
| + sb.write(exp.target.enclosingClass.name);
|
| + if (exp.target.name != '') {
|
| + sb.write('.');
|
| + sb.write(exp.target.name);
|
| + }
|
| + writeTypeArguments(exp.type);
|
| + sb.write('(');
|
| + bool needsComma = false;
|
| +
|
| + int namedOffset = exp.selector.positionalArgumentCount;
|
| + for (int index = 0; index < namedOffset; index++) {
|
| + if (needsComma) {
|
| + sb.write(', ');
|
| + }
|
| + visit(exp.arguments[index]);
|
| + needsComma = true;
|
| + }
|
| + for (int index = 0; index < exp.selector.namedArgumentCount; index++) {
|
| + if (needsComma) {
|
| + sb.write(', ');
|
| + }
|
| + sb.write(exp.selector.namedArguments[index]);
|
| + sb.write(': ');
|
| + visit(exp.arguments[namedOffset + index]);
|
| + needsComma = true;
|
| + }
|
| + sb.write(')');
|
| + }
|
| +
|
| + visitConcatenate(ConcatenateConstExp exp) {
|
| + sb.write(exp.value.toSyntax());
|
| + }
|
| +
|
| + visitSymbol(SymbolConstExp exp) {
|
| + sb.write('#');
|
| + sb.write(exp.name);
|
| + }
|
| +
|
| + visitType(TypeConstExp exp) {
|
| + sb.write(exp.type.name);
|
| + }
|
| +
|
| + visitVariable(VariableConstExp exp) {
|
| + if (exp.element.isStatic) {
|
| + sb.write(exp.element.enclosingClass.name);
|
| + sb.write('.');
|
| + }
|
| + sb.write(exp.element.name);
|
| + }
|
| +
|
| + visitFunction(FunctionConstExp exp) {
|
| + if (exp.element.isStatic) {
|
| + sb.write(exp.element.enclosingClass.name);
|
| + sb.write('.');
|
| + }
|
| + sb.write(exp.element.name);
|
| + }
|
| +
|
| + visitBinary(BinaryConstExp exp) {
|
| + if (exp.operator == 'identical') {
|
| + sb.write('identical(');
|
| + visit(exp.left);
|
| + sb.write(', ');
|
| + visit(exp.right);
|
| + sb.write(')');
|
| + } else {
|
| + write(exp, exp.left);
|
| + sb.write(' ');
|
| + sb.write(exp.operator);
|
| + sb.write(' ');
|
| + write(exp, exp.right);
|
| + }
|
| + }
|
| +
|
| + visitUnary(UnaryConstExp exp) {
|
| + sb.write(exp.operator);
|
| + write(exp, exp.expression);
|
| + }
|
| +
|
| + visitConditional(ConditionalConstExp exp) {
|
| + write(exp, exp.condition, leftAssociative: false);
|
| + sb.write(' ? ');
|
| + write(exp, exp.trueExp);
|
| + sb.write(' : ');
|
| + write(exp, exp.falseExp);
|
| + }
|
| +
|
| + String toString() => sb.toString();
|
| +}
|
|
|