| // 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; | |
| import '../common.dart'; | |
| import '../common/elements.dart' show CommonElements; | |
| import '../deferred_load/output_unit.dart' show OutputUnit; | |
| import '../elements/entities.dart'; | |
| import '../elements/types.dart'; | |
| import '../js/js.dart' as js; | |
| import '../universe/record_shape.dart'; | |
| import '../util/util.dart'; | |
| enum ConstantValueKind { | |
| function, | |
| null_, | |
| int, | |
| double, | |
| bool, | |
| string, | |
| list, | |
| set, | |
| map, | |
| constructed, | |
| record, | |
| type, | |
| interceptor, | |
| javaScriptObject, | |
| jsName, | |
| dummy, | |
| lateSentinel, | |
| unreachable, | |
| instantiation, | |
| deferredGlobal, | |
| } | |
| abstract class ConstantValueVisitor<R, A> { | |
| const ConstantValueVisitor(); | |
| R visitFunction(covariant FunctionConstantValue constant, covariant A arg); | |
| R visitNull(covariant NullConstantValue constant, covariant A arg); | |
| R visitInt(covariant IntConstantValue constant, covariant A arg); | |
| R visitDouble(covariant DoubleConstantValue constant, covariant A arg); | |
| R visitBool(covariant BoolConstantValue constant, covariant A arg); | |
| R visitString(covariant StringConstantValue constant, covariant A arg); | |
| R visitList(covariant ListConstantValue constant, covariant A arg); | |
| R visitSet(covariant SetConstantValue constant, covariant A arg); | |
| R visitMap(covariant MapConstantValue constant, covariant A arg); | |
| R visitConstructed( | |
| covariant ConstructedConstantValue constant, | |
| covariant A arg, | |
| ); | |
| R visitRecord(covariant RecordConstantValue constant, covariant A arg); | |
| R visitType(covariant TypeConstantValue constant, covariant A arg); | |
| R visitInterceptor( | |
| covariant InterceptorConstantValue constant, | |
| covariant A arg, | |
| ); | |
| R visitJavaScriptObject( | |
| covariant JavaScriptObjectConstantValue constant, | |
| covariant A arg, | |
| ); | |
| R visitDummy(covariant DummyConstantValue constant, covariant A arg); | |
| R visitLateSentinel( | |
| covariant LateSentinelConstantValue constant, | |
| covariant A arg, | |
| ); | |
| R visitUnreachable( | |
| covariant UnreachableConstantValue constant, | |
| covariant A arg, | |
| ); | |
| R visitJsName(covariant JsNameConstantValue constant, covariant A arg); | |
| R visitDeferredGlobal( | |
| covariant DeferredGlobalConstantValue constant, | |
| covariant A arg, | |
| ); | |
| R visitInstantiation( | |
| covariant InstantiationConstantValue constant, | |
| covariant A arg, | |
| ); | |
| } | |
| abstract class ConstantValue { | |
| const ConstantValue(); | |
| /// `true` if this is a valid constant value. | |
| bool get isConstant => true; | |
| /// Returns true if the constant is a list, a map or a constructed object. | |
| bool get isDummy => false; | |
| bool get isNaN => false; | |
| bool get isMinusZero => false; | |
| bool get isZero => false; | |
| bool get isOne => false; | |
| bool get isPositiveInfinity => false; | |
| bool get isNegativeInfinity => false; | |
| // TODO(johnniwinther): Replace with a 'type' getter. | |
| DartType getType(CommonElements types); | |
| List<ConstantValue> getDependencies(); | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg); | |
| /// The value of this constant in Dart syntax, if possible. | |
| /// | |
| /// For [ConstructedConstantValue]s there is no way to create a valid const | |
| /// expression from the value so the unparse of these is best effort. | |
| /// | |
| /// For the synthetic constants, [DeferredConstantValue], | |
| /// [DeferredGlobalConstantValue], [SyntheticConstantValue], | |
| /// [InterceptorConstantValue] the unparse is descriptive only. | |
| String toDartText(DartTypes? dartTypes); | |
| /// Returns a structured representation of this constant suited for debugging. | |
| String toStructuredText(DartTypes? dartTypes); | |
| ConstantValueKind get kind; | |
| @override | |
| String toString() { | |
| assertDebugMode( | |
| "Use ConstantValue.toDartText() or " | |
| "ConstantValue.toStructuredText() " | |
| "instead of ConstantValue.toString().", | |
| ); | |
| return toStructuredText(null); | |
| } | |
| } | |
| bool _listsEqual(List<ConstantValue> a, List<ConstantValue> b) { | |
| if (a.length != b.length) return false; | |
| for (int i = 0; i < a.length; i++) { | |
| if (a[i] != b[i]) return false; | |
| } | |
| return true; | |
| } | |
| class FunctionConstantValue extends ConstantValue { | |
| final FunctionEntity element; | |
| // TODO(johnniwinther): Should the type be derived from [element]. | |
| final FunctionType type; | |
| FunctionConstantValue(this.element, this.type); | |
| @override | |
| bool operator ==(other) { | |
| if (other is! FunctionConstantValue) return false; | |
| return identical(other.element, element); | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| @override | |
| FunctionType getType(CommonElements types) => type; | |
| @override | |
| int get hashCode => (17 * element.hashCode) & 0x7fffffff; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitFunction(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.function; | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| if (element.enclosingClass != null) { | |
| return '${element.enclosingClass!.name}.${element.name}'; | |
| } else { | |
| return '${element.name}'; | |
| } | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| return 'FunctionConstant(${toDartText(dartTypes)})'; | |
| } | |
| } | |
| abstract class PrimitiveConstantValue extends ConstantValue { | |
| const PrimitiveConstantValue(); | |
| @override | |
| bool operator ==(other) { | |
| // Making this method abstract does not give us an error. | |
| throw UnsupportedError('PrimitiveConstant.=='); | |
| } | |
| @override | |
| int get hashCode => throw UnsupportedError('PrimitiveConstant.hashCode'); | |
| // Primitive constants don't have dependencies. | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| } | |
| class NullConstantValue extends PrimitiveConstantValue { | |
| /// The value a Dart null is compiled to in JavaScript. | |
| static const String jsNull = "null"; | |
| const factory NullConstantValue() = NullConstantValue._internal; | |
| const NullConstantValue._internal(); | |
| @override | |
| DartType getType(CommonElements types) => types.nullType; | |
| @override | |
| bool operator ==(other) => other is NullConstantValue; | |
| // The magic constant has no meaning. It is just a random value. | |
| @override | |
| int get hashCode => 785965825; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitNull(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.null_; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => 'NullConstant'; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => 'null'; | |
| } | |
| abstract class NumConstantValue extends PrimitiveConstantValue { | |
| double get doubleValue; | |
| const NumConstantValue(); | |
| } | |
| class IntConstantValue extends NumConstantValue { | |
| final BigInt intValue; | |
| // Caching IntConstantValues representing -2 through 10 so that we don't have | |
| // to create new ones every time those values are used. | |
| static final Map<BigInt, IntConstantValue> _cachedValues = {}; | |
| @override | |
| double get doubleValue => intValue.toDouble(); | |
| factory IntConstantValue(BigInt value) { | |
| var existing = _cachedValues[value]; | |
| if (existing != null) return existing; | |
| var intConstantVal = IntConstantValue._internal(value); | |
| var intValue = value.toInt(); | |
| if (intValue <= -2 && intValue >= 10) { | |
| _cachedValues[value] = intConstantVal; | |
| } | |
| return intConstantVal; | |
| } | |
| const IntConstantValue._internal(this.intValue); | |
| bool isUInt31() => intValue.toUnsigned(31) == intValue; | |
| bool isUInt32() => intValue.toUnsigned(32) == intValue; | |
| bool isPositive() => intValue >= BigInt.zero; | |
| @override | |
| bool get isZero => intValue == BigInt.zero; | |
| @override | |
| bool get isOne => intValue == BigInt.one; | |
| @override | |
| DartType getType(CommonElements types) => types.intType; | |
| @override | |
| bool operator ==(other) { | |
| // Ints and doubles are treated as separate constants. | |
| if (other is! IntConstantValue) return false; | |
| IntConstantValue otherInt = other; | |
| return intValue == otherInt.intValue; | |
| } | |
| @override | |
| int get hashCode => intValue.hashCode & Hashing.smiMask; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitInt(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.int; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => | |
| 'IntConstant(${toDartText(dartTypes)})'; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => intValue.toString(); | |
| } | |
| class DoubleConstantValue extends NumConstantValue { | |
| @override | |
| final double doubleValue; | |
| factory DoubleConstantValue(double value) { | |
| if (value.isNaN) { | |
| return const DoubleConstantValue._internal(double.nan); | |
| } else if (value == double.infinity) { | |
| return const DoubleConstantValue._internal(double.infinity); | |
| } else if (value == -double.infinity) { | |
| return const DoubleConstantValue._internal(-double.infinity); | |
| } else if (value == 0.0 && !value.isNegative) { | |
| return const DoubleConstantValue._internal(0.0); | |
| } else if (value == 1.0) { | |
| return const DoubleConstantValue._internal(1.0); | |
| } else { | |
| return DoubleConstantValue._internal(value); | |
| } | |
| } | |
| const DoubleConstantValue._internal(this.doubleValue); | |
| @override | |
| bool get isNaN => doubleValue.isNaN; | |
| // We need to check for the negative sign since -0.0 == 0.0. | |
| @override | |
| bool get isMinusZero => doubleValue == 0.0 && doubleValue.isNegative; | |
| @override | |
| bool get isZero => doubleValue == 0.0; | |
| @override | |
| bool get isOne => doubleValue == 1.0; | |
| @override | |
| bool get isPositiveInfinity => doubleValue == double.infinity; | |
| @override | |
| bool get isNegativeInfinity => doubleValue == -double.infinity; | |
| @override | |
| DartType getType(CommonElements types) => types.doubleType; | |
| @override | |
| bool operator ==(other) { | |
| if (other is! DoubleConstantValue) return false; | |
| DoubleConstantValue otherDouble = other; | |
| double otherValue = otherDouble.doubleValue; | |
| if (doubleValue == 0.0 && otherValue == 0.0) { | |
| return doubleValue.isNegative == otherValue.isNegative; | |
| } else if (doubleValue.isNaN) { | |
| return otherValue.isNaN; | |
| } else { | |
| return doubleValue == otherValue; | |
| } | |
| } | |
| @override | |
| int get hashCode => doubleValue.hashCode; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitDouble(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.double; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => | |
| 'DoubleConstant(${toDartText(dartTypes)})'; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => doubleValue.toString(); | |
| } | |
| abstract class BoolConstantValue extends PrimitiveConstantValue { | |
| factory BoolConstantValue(bool value) { | |
| return value ? TrueConstantValue() : FalseConstantValue(); | |
| } | |
| const BoolConstantValue._internal(); | |
| bool get boolValue; | |
| @override | |
| DartType getType(CommonElements types) => types.boolType; | |
| BoolConstantValue negate(); | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitBool(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.bool; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => | |
| 'BoolConstant(${toDartText(dartTypes)})'; | |
| } | |
| class TrueConstantValue extends BoolConstantValue { | |
| factory TrueConstantValue() => const TrueConstantValue._internal(); | |
| const TrueConstantValue._internal() : super._internal(); | |
| @override | |
| bool get boolValue => true; | |
| @override | |
| FalseConstantValue negate() => FalseConstantValue(); | |
| @override | |
| bool operator ==(other) => identical(this, other); | |
| // The magic constant is just a random value. It does not have any | |
| // significance. | |
| @override | |
| int get hashCode => 499; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => boolValue.toString(); | |
| } | |
| class FalseConstantValue extends BoolConstantValue { | |
| factory FalseConstantValue() => const FalseConstantValue._internal(); | |
| const FalseConstantValue._internal() : super._internal(); | |
| @override | |
| bool get boolValue => false; | |
| @override | |
| TrueConstantValue negate() => TrueConstantValue(); | |
| @override | |
| bool operator ==(other) => identical(this, other); | |
| // The magic constant is just a random value. It does not have any | |
| // significance. | |
| @override | |
| int get hashCode => 536555975; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => boolValue.toString(); | |
| } | |
| class StringConstantValue extends PrimitiveConstantValue { | |
| final String stringValue; | |
| @override | |
| final int hashCode; | |
| // TODO(floitsch): cache StringConstants. | |
| StringConstantValue(String value) | |
| : stringValue = value, | |
| hashCode = value.hashCode; | |
| @override | |
| DartType getType(CommonElements types) => types.stringType; | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) return true; | |
| if (other is! StringConstantValue) return false; | |
| StringConstantValue otherString = other; | |
| return hashCode == otherString.hashCode && | |
| stringValue == otherString.stringValue; | |
| } | |
| String toDartString() => stringValue; | |
| int get length => stringValue.length; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitString(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.string; | |
| // TODO(johnniwinther): Ensure correct escaping. | |
| @override | |
| String toDartText(DartTypes? dartTypes) => '"$stringValue"'; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => | |
| 'StringConstant(${toDartText(dartTypes)})'; | |
| } | |
| abstract class ObjectConstantValue extends ConstantValue { | |
| final InterfaceType type; | |
| ObjectConstantValue(this.type); | |
| @override | |
| DartType getType(CommonElements types) => type; | |
| void _unparseTypeArguments(DartTypes? dartTypes, StringBuffer sb) { | |
| if (dartTypes == null || !dartTypes.treatAsRawType(type)) { | |
| sb.write('<'); | |
| sb.write(type.typeArguments.join(', ')); | |
| sb.write('>'); | |
| } | |
| } | |
| } | |
| class TypeConstantValue extends ObjectConstantValue { | |
| /// The user type that this constant represents. | |
| final DartType representedType; | |
| TypeConstantValue(this.representedType, InterfaceType type) : super(type); | |
| @override | |
| bool operator ==(other) { | |
| return other is TypeConstantValue && | |
| representedType == other.representedType; | |
| } | |
| @override | |
| int get hashCode => representedType.hashCode * 13; | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitType(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.type; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => '$representedType'; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => | |
| 'TypeConstant($representedType)'; | |
| } | |
| class ListConstantValue extends ObjectConstantValue { | |
| final List<ConstantValue> entries; | |
| @override | |
| final int hashCode; | |
| ListConstantValue(super.type, this.entries) | |
| : hashCode = Hashing.listHash(entries, Hashing.objectHash(type)); | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) return true; | |
| if (other is! ListConstantValue) return false; | |
| ListConstantValue otherList = other; | |
| if (hashCode != otherList.hashCode) return false; | |
| if (type != otherList.type) return false; | |
| if (entries.length != otherList.entries.length) return false; | |
| for (int i = 0; i < entries.length; i++) { | |
| if (entries[i] != otherList.entries[i]) return false; | |
| } | |
| return true; | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => entries; | |
| int get length => entries.length; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitList(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.list; | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| _unparseTypeArguments(dartTypes, sb); | |
| sb.write('['); | |
| for (int i = 0; i < length; i++) { | |
| if (i > 0) sb.write(','); | |
| sb.write(entries[i].toDartText(dartTypes)); | |
| } | |
| sb.write(']'); | |
| return sb.toString(); | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('ListConstant('); | |
| _unparseTypeArguments(dartTypes, sb); | |
| sb.write('['); | |
| for (int i = 0; i < length; i++) { | |
| if (i > 0) sb.write(', '); | |
| sb.write(entries[i].toStructuredText(dartTypes)); | |
| } | |
| sb.write('])'); | |
| return sb.toString(); | |
| } | |
| } | |
| abstract class SetConstantValue extends ObjectConstantValue { | |
| final List<ConstantValue> values; | |
| @override | |
| final int hashCode; | |
| SetConstantValue(super.type, this.values) | |
| : hashCode = Hashing.listHash(values, Hashing.objectHash(type)); | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) return true; | |
| if (other is! SetConstantValue) return false; | |
| SetConstantValue otherSet = other; | |
| if (hashCode != otherSet.hashCode) return false; | |
| if (type != otherSet.type) return false; | |
| if (length != otherSet.length) return false; | |
| for (int i = 0; i < values.length; i++) { | |
| if (values[i] != otherSet.values[i]) return false; | |
| } | |
| return true; | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => values; | |
| int get length => values.length; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitSet(this, arg); | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| _unparseTypeArguments(dartTypes, sb); | |
| sb.write('{'); | |
| sb.writeAll(values.map((v) => v.toDartText(dartTypes)), ','); | |
| sb.write('}'); | |
| return sb.toString(); | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('SetConstant('); | |
| _unparseTypeArguments(dartTypes, sb); | |
| sb.write('{'); | |
| sb.writeAll(values.map((v) => v.toStructuredText(dartTypes)), ', '); | |
| sb.write('})'); | |
| return sb.toString(); | |
| } | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.set; | |
| } | |
| abstract class MapConstantValue extends ObjectConstantValue { | |
| final List<ConstantValue> keys; | |
| final List<ConstantValue> values; | |
| @override | |
| final int hashCode; | |
| Map<ConstantValue, ConstantValue>? _lookupMap; | |
| MapConstantValue(super.type, this.keys, this.values) | |
| : hashCode = Hashing.listHash( | |
| values, | |
| Hashing.listHash(keys, Hashing.objectHash(type)), | |
| ) { | |
| assert(keys.length == values.length); | |
| } | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) return true; | |
| if (other is! MapConstantValue) return false; | |
| MapConstantValue otherMap = other; | |
| if (hashCode != otherMap.hashCode) return false; | |
| if (type != other.type) return false; | |
| if (length != other.length) return false; | |
| for (int i = 0; i < length; i++) { | |
| if (keys[i] != otherMap.keys[i]) return false; | |
| if (values[i] != otherMap.values[i]) return false; | |
| } | |
| return true; | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => [...keys, ...values]; | |
| int get length => keys.length; | |
| ConstantValue? lookup(ConstantValue key) { | |
| var lookupMap = _lookupMap ??= Map.fromIterables(keys, values); | |
| return lookupMap[key]; | |
| } | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitMap(this, arg); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.map; | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| _unparseTypeArguments(dartTypes, sb); | |
| sb.write('{'); | |
| for (int i = 0; i < length; i++) { | |
| if (i > 0) sb.write(','); | |
| sb.write(keys[i].toDartText(dartTypes)); | |
| sb.write(':'); | |
| sb.write(values[i].toDartText(dartTypes)); | |
| } | |
| sb.write('}'); | |
| return sb.toString(); | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('MapConstant('); | |
| _unparseTypeArguments(dartTypes, sb); | |
| sb.write('{'); | |
| for (int i = 0; i < length; i++) { | |
| if (i > 0) sb.write(', '); | |
| sb.write(keys[i].toStructuredText(dartTypes)); | |
| sb.write(': '); | |
| sb.write(values[i].toStructuredText(dartTypes)); | |
| } | |
| sb.write('})'); | |
| return sb.toString(); | |
| } | |
| } | |
| class InterceptorConstantValue extends ConstantValue { | |
| /// The class for which this interceptor holds the methods. The constant | |
| /// is a dispatch table for this class. | |
| final ClassEntity cls; | |
| InterceptorConstantValue(this.cls); | |
| @override | |
| bool operator ==(other) { | |
| return other is InterceptorConstantValue && cls == other.cls; | |
| } | |
| @override | |
| int get hashCode => cls.hashCode * 43; | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) { | |
| return visitor.visitInterceptor(this, arg); | |
| } | |
| @override | |
| DartType getType(CommonElements types) => types.dynamicType; | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.interceptor; | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| return 'interceptor($cls)'; | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| return 'InterceptorConstant(${cls.name})'; | |
| } | |
| } | |
| class JsNameConstantValue extends ConstantValue { | |
| final js.LiteralString name; | |
| JsNameConstantValue(this.name); | |
| @override | |
| bool get isDummy => true; | |
| @override | |
| bool operator ==(other) { | |
| return other is JsNameConstantValue && name == other.name; | |
| } | |
| @override | |
| int get hashCode => name.hashCode * 17; | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) { | |
| return visitor.visitJsName(this, arg); | |
| } | |
| @override | |
| DartType getType(CommonElements types) => types.dynamicType; | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.jsName; | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| if (name.isFinalized) return 'js_name($name)'; | |
| return 'js_name(name.nonfinalizedDebugText())'; | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| if (name.isFinalized) return 'JsNameConstant($name)'; | |
| return 'JsNameConstant(name.nonfinalizedDebugText())'; | |
| } | |
| } | |
| /// A constant used as an argument or receiver when the target does not use the | |
| /// corresponding parameter. Used as the dummy receiver value for calls using | |
| /// the intercepted calling convention when the target is 'self-intercepting' | |
| /// and does not use the explicit receiver parameter. | |
| class DummyConstantValue extends ConstantValue { | |
| factory DummyConstantValue() => const DummyConstantValue._(); | |
| const DummyConstantValue._(); | |
| @override | |
| bool get isDummy => true; | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) { | |
| return visitor.visitDummy(this, arg); | |
| } | |
| @override | |
| DartType getType(CommonElements types) => types.dynamicType; | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.dummy; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => 'dummy()'; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => 'DummyConstant()'; | |
| } | |
| /// A constant used to represent the sentinel for uninitialized late fields and | |
| /// variables. | |
| class LateSentinelConstantValue extends ConstantValue { | |
| factory LateSentinelConstantValue() => const LateSentinelConstantValue._(); | |
| const LateSentinelConstantValue._(); | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) { | |
| return visitor.visitLateSentinel(this, arg); | |
| } | |
| @override | |
| DartType getType(CommonElements types) => types.dartTypes.neverType(); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.lateSentinel; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => 'late_sentinel()'; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => 'LateSentinelConstant()'; | |
| } | |
| // A constant with an empty type used in [HInstruction]s of an expression | |
| // in an unreachable context. | |
| class UnreachableConstantValue extends ConstantValue { | |
| factory UnreachableConstantValue() => const UnreachableConstantValue._(); | |
| const UnreachableConstantValue._(); | |
| @override | |
| bool get isDummy => true; | |
| @override | |
| List<ConstantValue> getDependencies() => const []; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) { | |
| return visitor.visitUnreachable(this, arg); | |
| } | |
| @override | |
| DartType getType(CommonElements types) => types.dynamicType; | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.unreachable; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => 'unreachable()'; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) => 'UnreachableConstant()'; | |
| } | |
| class ConstructedConstantValue extends ObjectConstantValue { | |
| // TODO(johnniwinther): Make [fields] private to avoid misuse of the map | |
| // ordering and mutability. | |
| final Map<FieldEntity, ConstantValue> fields; | |
| @override | |
| final int hashCode; | |
| ConstructedConstantValue(super.type, this.fields) | |
| : hashCode = Hashing.unorderedMapHash(fields, Hashing.objectHash(type)); | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) return true; | |
| if (other is! ConstructedConstantValue) return false; | |
| if (hashCode != other.hashCode) return false; | |
| if (type != other.type) return false; | |
| if (fields.length != other.fields.length) return false; | |
| for (FieldEntity field in fields.keys) { | |
| if (fields[field] != other.fields[field]) return false; | |
| } | |
| return true; | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => fields.values.toList(); | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) { | |
| return visitor.visitConstructed(this, arg); | |
| } | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.constructed; | |
| Iterable<FieldEntity> get _fieldsSortedByName { | |
| return fields.keys.toList()..sort((a, b) => a.name!.compareTo(b.name!)); | |
| } | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write(type.element.name); | |
| _unparseTypeArguments(dartTypes, sb); | |
| sb.write('('); | |
| int i = 0; | |
| for (FieldEntity field in _fieldsSortedByName) { | |
| ConstantValue value = fields[field]!; | |
| if (i > 0) sb.write(','); | |
| sb.write(field.name); | |
| sb.write('='); | |
| sb.write(value.toDartText(dartTypes)); | |
| i++; | |
| } | |
| sb.write(')'); | |
| return sb.toString(); | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('ConstructedConstant('); | |
| sb.write(type); | |
| sb.write('('); | |
| int i = 0; | |
| for (FieldEntity field in _fieldsSortedByName) { | |
| ConstantValue value = fields[field]!; | |
| if (i > 0) sb.write(','); | |
| sb.write(field.name); | |
| sb.write('='); | |
| sb.write(value.toStructuredText(dartTypes)); | |
| i++; | |
| } | |
| sb.write('))'); | |
| return sb.toString(); | |
| } | |
| } | |
| class RecordConstantValue extends ConstantValue { | |
| final RecordShape shape; | |
| final List<ConstantValue> values; | |
| @override | |
| final int hashCode; | |
| RecordConstantValue(this.shape, this.values) | |
| : assert(shape.fieldCount == values.length), | |
| hashCode = Hashing.objectHash(shape, Hashing.listHash(values)); | |
| @override | |
| bool operator ==(Object other) { | |
| return other is RecordConstantValue && | |
| hashCode == other.hashCode && | |
| shape == other.shape && | |
| _listsEqual(values, other.values); | |
| } | |
| @override | |
| DartType getType(CommonElements types) { | |
| return types.dartTypes.recordType( | |
| shape, | |
| values.map((value) => value.getType(types)).toList(), | |
| ); | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => values; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) { | |
| return visitor.visitRecord(this, arg); | |
| } | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.record; | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('('); | |
| for (int i = 0; i < values.length; i++) { | |
| if (i > 0) sb.write(','); | |
| if (i >= shape.positionalFieldCount) { | |
| sb.write(shape.fieldNames[i - shape.positionalFieldCount]); | |
| sb.write(': '); | |
| } | |
| sb.write(values[i].toDartText(dartTypes)); | |
| } | |
| sb.write(')'); | |
| return sb.toString(); | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('RecordConstant('); | |
| sb.write(shape); | |
| sb.write('('); | |
| for (int i = 0; i < values.length; i++) { | |
| if (i > 0) sb.write(','); | |
| if (i >= shape.positionalFieldCount) { | |
| sb.write(shape.fieldNames[i - shape.positionalFieldCount]); | |
| sb.write(': '); | |
| } | |
| sb.write(values[i].toStructuredText(dartTypes)); | |
| } | |
| sb.write('))'); | |
| return sb.toString(); | |
| } | |
| } | |
| class InstantiationConstantValue extends ConstantValue { | |
| final List<DartType> typeArguments; | |
| final FunctionConstantValue function; | |
| InstantiationConstantValue(this.typeArguments, this.function); | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) return true; | |
| return other is InstantiationConstantValue && | |
| function == other.function && | |
| equalElements(typeArguments, other.typeArguments); | |
| } | |
| @override | |
| int get hashCode { | |
| return Hashing.objectHash(function, Hashing.listHash(typeArguments)); | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => [function]; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitInstantiation(this, arg); | |
| @override | |
| DartType getType(CommonElements types) { | |
| FunctionType type = function.getType(types); | |
| return types.dartTypes.instantiate(type, typeArguments); | |
| } | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.instantiation; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => | |
| '<${typeArguments.join(', ')}>(${function.toDartText(dartTypes)})'; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| return 'InstantiationConstant($typeArguments,' | |
| '${function.toStructuredText(dartTypes)})'; | |
| } | |
| } | |
| /// A JavaScript Object Literal used as a constant. | |
| class JavaScriptObjectConstantValue extends ConstantValue { | |
| final List<ConstantValue> keys; | |
| final List<ConstantValue> values; | |
| @override | |
| late final int hashCode = Hashing.listHash(values, Hashing.listHash(keys, 9)); | |
| JavaScriptObjectConstantValue(this.keys, this.values) { | |
| assert(keys.length == values.length); | |
| } | |
| @override | |
| bool operator ==(other) { | |
| return identical(this, other) || | |
| other is JavaScriptObjectConstantValue && _equals(this, other); | |
| } | |
| static bool _equals( | |
| JavaScriptObjectConstantValue a, | |
| JavaScriptObjectConstantValue b, | |
| ) { | |
| if (a.hashCode != b.hashCode) return false; | |
| if (a.length != b.length) return false; | |
| if (!_listsEqual(a.keys, b.keys)) return false; | |
| if (!_listsEqual(a.values, b.values)) return false; | |
| return true; | |
| } | |
| @override | |
| List<ConstantValue> getDependencies() => [...keys, ...values]; | |
| int get length => keys.length; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitJavaScriptObject(this, arg); | |
| @override | |
| DartType getType(CommonElements types) { | |
| return types.dynamicType; // TODO: Lookup JavaScriptObject. | |
| } | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.javaScriptObject; | |
| @override | |
| String toDartText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('{'); | |
| for (int i = 0; i < length; i++) { | |
| if (i > 0) sb.write(','); | |
| sb.write(keys[i].toDartText(dartTypes)); | |
| sb.write(':'); | |
| sb.write(values[i].toDartText(dartTypes)); | |
| } | |
| sb.write('}'); | |
| return sb.toString(); | |
| } | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write('JavaScriptObject('); | |
| sb.write('{'); | |
| for (int i = 0; i < length; i++) { | |
| if (i > 0) sb.write(', '); | |
| sb.write(keys[i].toStructuredText(dartTypes)); | |
| sb.write(': '); | |
| sb.write(values[i].toStructuredText(dartTypes)); | |
| } | |
| sb.write('})'); | |
| return sb.toString(); | |
| } | |
| } | |
| /// A reference to a constant in another output unit. | |
| /// | |
| /// Used for referring to deferred constants that appear as initializers of | |
| /// final (non-const) global fields. | |
| /// | |
| // TODO(sigmund): this should eventually not be a constant value. In particular, | |
| // [DeferredConstantValue] is introduced by the constant evaluator when it first | |
| // sees constants used in the program. [DeferredGlobalConstantValue] are | |
| // introduced later by the SSA builder and should be represented | |
| // with a dedicated JEntity instead. We currently model them as a regular | |
| // constant to take advantage of the machinery we already have in place to | |
| // generate deferred constants in the emitter. | |
| class DeferredGlobalConstantValue extends ConstantValue { | |
| DeferredGlobalConstantValue(this.referenced, this.unit); | |
| final ConstantValue referenced; | |
| final OutputUnit unit; | |
| bool get isReference => true; | |
| @override | |
| bool operator ==(other) { | |
| return other is DeferredGlobalConstantValue && | |
| referenced == other.referenced && | |
| unit == other.unit; | |
| } | |
| @override | |
| int get hashCode => (referenced.hashCode * 17 + unit.hashCode) & 0x3fffffff; | |
| @override | |
| List<ConstantValue> getDependencies() => [referenced]; | |
| @override | |
| R accept<R, A>(ConstantValueVisitor<R, A> visitor, A arg) => | |
| visitor.visitDeferredGlobal(this, arg); | |
| @override | |
| DartType getType(CommonElements types) => referenced.getType(types); | |
| @override | |
| ConstantValueKind get kind => ConstantValueKind.deferredGlobal; | |
| @override | |
| String toDartText(DartTypes? dartTypes) => | |
| 'deferred_global(${referenced.toDartText(dartTypes)})'; | |
| @override | |
| String toStructuredText(DartTypes? dartTypes) { | |
| return 'DeferredGlobalConstant(${referenced.toStructuredText(dartTypes)})'; | |
| } | |
| } |