| // Copyright (c) 2014, 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. | |
| /// The implementation of the class [DartObject]. | |
| /// | |
| /// @docImport 'package:analyzer/src/dart/constant/evaluation.dart'; | |
| library; | |
| import 'dart:collection'; | |
| import 'package:analyzer/dart/analysis/features.dart'; | |
| import 'package:analyzer/dart/ast/ast.dart' as ast show ConstructorInvocation; | |
| import 'package:analyzer/dart/ast/ast.dart' hide ConstructorInvocation; | |
| import 'package:analyzer/dart/ast/syntactic_entity.dart'; | |
| import 'package:analyzer/dart/constant/value.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/src/dart/constant/has_invalid_type.dart'; | |
| import 'package:analyzer/src/dart/constant/has_type_parameter_reference.dart'; | |
| import 'package:analyzer/src/dart/element/element.dart'; | |
| import 'package:analyzer/src/dart/element/extensions.dart'; | |
| import 'package:analyzer/src/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/element/type_system.dart'; | |
| import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag; | |
| import 'package:analyzer/src/error/codes.dart'; | |
| import 'package:analyzer/src/utilities/extensions/object.dart'; | |
| /// The state of an object representing a boolean value. | |
| class BoolState extends InstanceState { | |
| /// An instance representing the boolean value 'false'. | |
| static BoolState FALSE_STATE = BoolState(false); | |
| /// An instance representing the boolean value 'true'. | |
| static BoolState TRUE_STATE = BoolState(true); | |
| /// A state that can be used to represent a boolean whose value is not known. | |
| static BoolState UNKNOWN_VALUE = BoolState(null); | |
| /// The value of this instance. | |
| final bool? value; | |
| /// Initialize a newly created state to represent the given [value]. | |
| BoolState(this.value); | |
| @override | |
| int get hashCode => value == null ? 0 : (value! ? 2 : 3); | |
| @override | |
| bool get isBool => true; | |
| @override | |
| bool get isBoolNumStringOrNull => true; | |
| @override | |
| bool get isUnknown => value == null; | |
| @override | |
| String get typeName => "bool"; | |
| @override | |
| bool operator ==(Object other) => | |
| other is BoolState && identical(value, other.value); | |
| @override | |
| BoolState convertToBool() => this; | |
| @override | |
| StringState convertToString() { | |
| if (value == null) { | |
| return StringState.UNKNOWN_VALUE; | |
| } | |
| return StringState(value! ? "true" : "false"); | |
| } | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is BoolState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(identical(value, rightValue)); | |
| } | |
| return FALSE_STATE; | |
| } | |
| @override | |
| BoolState lazyAnd(InstanceState? Function() rightOperandComputer) { | |
| if (value == false) { | |
| return FALSE_STATE; | |
| } | |
| var rightOperand = rightOperandComputer(); | |
| assertBool(rightOperand); | |
| return value == null ? UNKNOWN_VALUE : rightOperand!.convertToBool(); | |
| } | |
| @override | |
| BoolState lazyOr(InstanceState? Function() rightOperandComputer) { | |
| if (value == true) { | |
| return TRUE_STATE; | |
| } | |
| var rightOperand = rightOperandComputer(); | |
| assertBool(rightOperand); | |
| return value == null ? UNKNOWN_VALUE : rightOperand!.convertToBool(); | |
| } | |
| @override | |
| BoolState logicalNot() { | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return value! ? FALSE_STATE : TRUE_STATE; | |
| } | |
| @override | |
| String toString() => | |
| value == null ? "-unknown-" : (value! ? "true" : "false"); | |
| /// Return the boolean state representing the given boolean [value]. | |
| static BoolState from(bool value) => | |
| value ? BoolState.TRUE_STATE : BoolState.FALSE_STATE; | |
| } | |
| /// A valid or invalid constant used by constant evaluator. | |
| /// | |
| /// [DartObjectImpl] represents a valid result. Note that the [DartObjectImpl] | |
| /// could have an unknown state and still be a valid constant. | |
| /// [InvalidConstant] represents an invalid result with error information. | |
| sealed class Constant {} | |
| class ConstructorInvocationDataImpl implements ConstructorInvocation { | |
| @override | |
| final ConstructorElement constructor; | |
| @override | |
| final List<DartObjectImpl> positionalArguments; | |
| @override | |
| final Map<String, DartObjectImpl> namedArguments; | |
| ConstructorInvocationDataImpl( | |
| this.constructor, | |
| this.positionalArguments, | |
| this.namedArguments, | |
| ); | |
| } | |
| /// A representation of an instance of a Dart class. | |
| class DartObjectImpl implements DartObject, Constant { | |
| final TypeSystemImpl _typeSystem; | |
| @override | |
| final TypeImpl type; | |
| @override | |
| final TypeImpl typeNotExtensionTypeErased; | |
| /// The state of the object. | |
| final InstanceState state; | |
| @override | |
| final VariableElementImpl? variable; | |
| /// Initialize a newly created object to have the given [type] and [state]. | |
| factory DartObjectImpl( | |
| TypeSystemImpl typeSystem, | |
| TypeImpl type, | |
| InstanceState state, { | |
| VariableElementImpl? variable, | |
| }) { | |
| return DartObjectImpl._( | |
| typeSystem, | |
| type.extensionTypeErasure, | |
| type, | |
| state, | |
| variable: variable, | |
| ); | |
| } | |
| /// Creates a duplicate instance of [other], tied to [variable]. | |
| factory DartObjectImpl.forVariable( | |
| DartObjectImpl other, | |
| VariableElementImpl variable, | |
| ) { | |
| return DartObjectImpl._( | |
| other._typeSystem, | |
| other.type, | |
| other.typeNotExtensionTypeErased, | |
| other.state, | |
| variable: variable, | |
| ); | |
| } | |
| /// Create an object to represent an unknown value. | |
| factory DartObjectImpl.validWithUnknownValue( | |
| TypeSystemImpl typeSystem, | |
| TypeImpl type, | |
| ) { | |
| if (type.isDartCoreBool) { | |
| return DartObjectImpl(typeSystem, type, BoolState.UNKNOWN_VALUE); | |
| } else if (type.isDartCoreDouble) { | |
| return DartObjectImpl(typeSystem, type, DoubleState.UNKNOWN_VALUE); | |
| } else if (type.isDartCoreInt) { | |
| return DartObjectImpl(typeSystem, type, IntState.UNKNOWN_VALUE); | |
| } else if (type.isDartCoreList) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| type, | |
| ListState.unknown(typeSystem.typeProvider.dynamicType), | |
| ); | |
| } else if (type.isDartCoreMap) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| type, | |
| MapState.unknown( | |
| typeSystem.typeProvider.dynamicType, | |
| typeSystem.typeProvider.dynamicType, | |
| ), | |
| ); | |
| } else if (type.isDartCoreSet) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| type, | |
| SetState.unknown(typeSystem.typeProvider.dynamicType), | |
| ); | |
| } else if (type.isDartCoreString) { | |
| return DartObjectImpl(typeSystem, type, StringState.UNKNOWN_VALUE); | |
| } | |
| return DartObjectImpl(typeSystem, type, GenericState({}, isUnknown: true)); | |
| } | |
| factory DartObjectImpl.withExtensionType({ | |
| required TypeSystemImpl typeSystem, | |
| required TypeImpl type, | |
| required TypeImpl typeNotExtensionTypeErased, | |
| required InstanceState state, | |
| VariableElementImpl? variable, | |
| }) { | |
| return DartObjectImpl._( | |
| typeSystem, | |
| type, | |
| typeNotExtensionTypeErased, | |
| state, | |
| variable: variable, | |
| ); | |
| } | |
| /// Initialize a newly created object to have the given [type] and [state]. | |
| DartObjectImpl._( | |
| this._typeSystem, | |
| this.type, | |
| this.typeNotExtensionTypeErased, | |
| this.state, { | |
| this.variable, | |
| }) { | |
| if (state case GenericState state) { | |
| state._object = this; | |
| } | |
| } | |
| @override | |
| ConstructorInvocationDataImpl? get constructorInvocation { | |
| return state.tryCast<GenericState>()?.invocation; | |
| } | |
| Map<String, DartObjectImpl>? get fields => state.fields; | |
| @override | |
| int get hashCode => Object.hash(type, state); | |
| @override | |
| bool get hasKnownValue => !state.isUnknown; | |
| /// Return `true` if this object represents an object whose type is 'bool'. | |
| bool get isBool => state.isBool; | |
| /// Return `true` if this object represents an object whose type is either | |
| /// 'bool', 'num', 'String', or 'Null'. | |
| bool get isBoolNumStringOrNull => state.isBoolNumStringOrNull; | |
| /// Return `true` if this object represents an object whose type is 'int'. | |
| bool get isInt => state.isInt; | |
| /// Return `true` if this object represents an object whose type is an invalid | |
| /// type. | |
| bool get isInvalid => state.isInvalid || hasInvalidType(type); | |
| @override | |
| bool get isNull => state is NullState; | |
| /// Return `true` if this object represents an unknown value. | |
| bool get isUnknown => state.isUnknown; | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is DartObjectImpl) { | |
| return _typeSystem.runtimeTypesEqual(type, other.type) && | |
| state == other.state; | |
| } | |
| return false; | |
| } | |
| /// Return the result of invoking the '+' operator on this object with the | |
| /// given [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl add(TypeSystemImpl typeSystem, DartObjectImpl rightOperand) { | |
| InstanceState result = state.add(rightOperand.state); | |
| if (result is IntState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| result, | |
| ); | |
| } else if (result is DoubleState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.doubleType, | |
| result, | |
| ); | |
| } else if (result is StringState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.stringType, | |
| result, | |
| ); | |
| } | |
| // We should never get here. | |
| throw StateError("add returned a ${result.runtimeType}"); | |
| } | |
| /// Return the result of invoking the '~' operator on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl bitNot(TypeSystemImpl typeSystem) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.bitNot(), | |
| ); | |
| } | |
| /// Return the result of casting this object to the given [castType]. | |
| DartObjectImpl castToType( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl castType, | |
| ) { | |
| _assertType(castType); | |
| var resultType = (castType.state as TypeState).type; | |
| // If we don't know the type, we cannot prove that the cast will fail. | |
| if (resultType == null) { | |
| return this; | |
| } | |
| // If any type is unresolved, we cannot prove that the cast will fail. | |
| if (isInvalid || castType.isInvalid) { | |
| return this; | |
| } | |
| // We don't know the actual value of a type parameter. | |
| // So, the object type might be a subtype of the result type. | |
| if (hasTypeParameterReference(resultType)) { | |
| return this; | |
| } | |
| if (!typeSystem.isSubtypeOf(type, resultType)) { | |
| // TODO(kallentu): Make a more specific error for casting. | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| return this; | |
| } | |
| /// Return the result of invoking the ' ' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl concatenate( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.stringType, | |
| state.concatenate(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of applying boolean conversion to this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl convertToBool(TypeSystemImpl typeSystem) { | |
| var boolType = typeSystem.typeProvider.boolType; | |
| if (identical(type, boolType)) { | |
| return this; | |
| } | |
| return DartObjectImpl(typeSystem, boolType, state.convertToBool()); | |
| } | |
| /// Return the result of invoking the '/' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for | |
| /// an object of this kind. | |
| DartObjectImpl divide( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| InstanceState result = state.divide(rightOperand.state); | |
| if (result is IntState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| result, | |
| ); | |
| } else if (result is DoubleState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.doubleType, | |
| result, | |
| ); | |
| } | |
| // We should never get here. | |
| throw StateError("divide returned a ${result.runtimeType}"); | |
| } | |
| /// Return the result of invoking the '&' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl eagerAnd( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| if (isBool && rightOperand.isBool) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.logicalAnd(rightOperand.state), | |
| ); | |
| } else if (isInt && rightOperand.isInt) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.bitAnd(rightOperand.state), | |
| ); | |
| } | |
| throw EvaluationException(diag.constEvalTypeBoolInt); | |
| } | |
| /// Return the result of invoking the '|' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl eagerOr( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| if (isBool && rightOperand.isBool) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.logicalOr(rightOperand.state), | |
| ); | |
| } else if (isInt && rightOperand.isInt) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.bitOr(rightOperand.state), | |
| ); | |
| } | |
| throw EvaluationException(diag.constEvalTypeBoolInt); | |
| } | |
| /// Return the result of invoking the '^' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl eagerXor( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| if (isBool && rightOperand.isBool) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.logicalXor(rightOperand.state), | |
| ); | |
| } else if (isInt && rightOperand.isInt) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.bitXor(rightOperand.state), | |
| ); | |
| } | |
| throw EvaluationException(diag.constEvalTypeBoolInt); | |
| } | |
| /// Returns the result of invoking the '==' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl equalEqual( | |
| TypeSystemImpl typeSystem, | |
| FeatureSet featureSet, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| if (isNull || rightOperand.isNull) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| isNull && rightOperand.isNull | |
| ? BoolState.TRUE_STATE | |
| : BoolState.FALSE_STATE, | |
| ); | |
| } | |
| if (featureSet.isEnabled(Feature.patterns)) { | |
| if (state is DoubleState || hasPrimitiveEquality(featureSet)) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.equalEqual(typeSystem, rightOperand.state), | |
| ); | |
| } | |
| } else { | |
| if (isBoolNumStringOrNull) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.equalEqual(typeSystem, rightOperand.state), | |
| ); | |
| } | |
| } | |
| throw EvaluationException( | |
| featureSet.isEnabled(Feature.patterns) | |
| ? diag.constEvalPrimitiveEquality | |
| : diag.constEvalTypeBoolNumString, | |
| ); | |
| } | |
| @override | |
| DartObject? getField(String name) { | |
| var state = this.state; | |
| if (state is GenericState) { | |
| return state.fields[name]; | |
| } else if (state is RecordState) { | |
| return state.getField(name); | |
| } | |
| return null; | |
| } | |
| /// Return the result of invoking the '>' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl greaterThan( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.greaterThan(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of invoking the '>=' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl greaterThanOrEqual( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.greaterThanOrEqual(rightOperand.state), | |
| ); | |
| } | |
| /// Returns `true` if this value, inside a library with the [featureSet], | |
| /// has primitive equality, so can be used at compile-time. | |
| bool hasPrimitiveEquality(FeatureSet featureSet) { | |
| return state.hasPrimitiveEquality(featureSet); | |
| } | |
| /// Return the result of testing whether this object has the given | |
| /// [testedType]. | |
| DartObjectImpl hasType(TypeSystemImpl typeSystem, DartObjectImpl testedType) { | |
| _assertType(testedType); | |
| var typeType = (testedType.state as TypeState).type; | |
| BoolState state; | |
| if (typeType == null) { | |
| state = BoolState.TRUE_STATE; | |
| } else { | |
| state = BoolState.from(typeSystem.isSubtypeOf(type, typeType)); | |
| } | |
| return DartObjectImpl(typeSystem, typeSystem.typeProvider.boolType, state); | |
| } | |
| /// Return the result of invoking the '~/' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl integerDivide( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.integerDivide(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of invoking the identical function on this object with | |
| /// the [rightOperand]. | |
| DartObjectImpl isIdentical2( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| // Workaround for Flutter `const kIsWeb = identical(0, 0.0)`. | |
| if (type.isDartCoreInt && rightOperand.type.isDartCoreDouble || | |
| type.isDartCoreDouble && rightOperand.type.isDartCoreInt) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| BoolState.UNKNOWN_VALUE, | |
| ); | |
| } | |
| if (!_typeSystem.runtimeTypesEqual(type, rightOperand.type)) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| BoolState(false), | |
| ); | |
| } | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.isIdentical(typeSystem, rightOperand.state), | |
| ); | |
| } | |
| /// Returns the result of invoking the '&&' operator on this object with the | |
| /// [rightOperandComputer]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl lazyAnd( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl Function() rightOperandComputer, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.lazyAnd(() => rightOperandComputer().state), | |
| ); | |
| } | |
| /// Returns the result of invoking the '||' operator on this object with the | |
| /// [rightOperandComputer]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl lazyOr( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl Function() rightOperandComputer, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.lazyOr(() => rightOperandComputer().state), | |
| ); | |
| } | |
| /// Return the result of invoking the '<' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl lessThan( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.lessThan(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of invoking the '<=' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl lessThanOrEqual( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.lessThanOrEqual(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of invoking the '!' operator on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl logicalNot(TypeSystemImpl typeSystem) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.boolType, | |
| state.logicalNot(), | |
| ); | |
| } | |
| /// Return the result of invoking the '>>>' operator on this object | |
| /// with the [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl logicalShiftRight( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.logicalShiftRight(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of invoking the '-' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl minus(TypeSystemImpl typeSystem, DartObjectImpl rightOperand) { | |
| InstanceState result = state.minus(rightOperand.state); | |
| if (result is IntState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| result, | |
| ); | |
| } else if (result is DoubleState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.doubleType, | |
| result, | |
| ); | |
| } | |
| // We should never get here. | |
| throw StateError("minus returned a ${result.runtimeType}"); | |
| } | |
| /// Return the result of invoking the '-' operator on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl negated(TypeSystemImpl typeSystem) { | |
| InstanceState result = state.negated(); | |
| if (result is IntState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| result, | |
| ); | |
| } else if (result is DoubleState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.doubleType, | |
| result, | |
| ); | |
| } | |
| // We should never get here. | |
| throw StateError("negated returned a ${result.runtimeType}"); | |
| } | |
| /// Returns the result of invoking the '!=' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl notEqual( | |
| TypeSystemImpl typeSystem, | |
| FeatureSet featureSet, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return equalEqual( | |
| typeSystem, | |
| featureSet, | |
| rightOperand, | |
| ).logicalNot(typeSystem); | |
| } | |
| /// Return the result of converting this object to a 'String'. | |
| /// | |
| /// Throws an [EvaluationException] if the object cannot be converted to a | |
| /// 'String'. | |
| DartObjectImpl performToString(TypeSystemImpl typeSystem) { | |
| var stringType = typeSystem.typeProvider.stringType; | |
| if (identical(type, stringType)) { | |
| return this; | |
| } | |
| return DartObjectImpl(typeSystem, stringType, state.convertToString()); | |
| } | |
| /// Return the result of invoking the '%' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl remainder( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| InstanceState result = state.remainder(rightOperand.state); | |
| if (result is IntState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| result, | |
| ); | |
| } else if (result is DoubleState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.doubleType, | |
| result, | |
| ); | |
| } | |
| // We should never get here. | |
| throw StateError("remainder returned a ${result.runtimeType}"); | |
| } | |
| /// Return the result of invoking the '<<' operator on this object with | |
| /// the [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl shiftLeft( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.shiftLeft(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of invoking the '>>' operator on this object with | |
| /// the [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl shiftRight( | |
| TypeSystemImpl typeSystem, | |
| DartObjectImpl rightOperand, | |
| ) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.shiftRight(rightOperand.state), | |
| ); | |
| } | |
| /// Return the result of invoking the 'length' getter on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl stringLength(TypeSystemImpl typeSystem) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| state.stringLength(), | |
| ); | |
| } | |
| /// Return the result of invoking the '*' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| DartObjectImpl times(TypeSystemImpl typeSystem, DartObjectImpl rightOperand) { | |
| InstanceState result = state.times(rightOperand.state); | |
| if (result is IntState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.intType, | |
| result, | |
| ); | |
| } else if (result is DoubleState) { | |
| return DartObjectImpl( | |
| typeSystem, | |
| typeSystem.typeProvider.doubleType, | |
| result, | |
| ); | |
| } | |
| // We should never get here. | |
| throw StateError("times returned a ${result.runtimeType}"); | |
| } | |
| @override | |
| bool? toBoolValue() { | |
| var state = this.state; | |
| if (state is BoolState) { | |
| return state.value; | |
| } | |
| return null; | |
| } | |
| @override | |
| double? toDoubleValue() { | |
| var state = this.state; | |
| if (state is DoubleState) { | |
| return state.value; | |
| } | |
| return null; | |
| } | |
| @override | |
| InternalExecutableElement? toFunctionValue() { | |
| var state = this.state; | |
| return state is FunctionState ? state.element : null; | |
| } | |
| @override | |
| int? toIntValue() { | |
| var state = this.state; | |
| if (state is IntState) { | |
| return state.value; | |
| } | |
| return null; | |
| } | |
| @override | |
| List<DartObjectImpl>? toListValue() { | |
| var state = this.state; | |
| if (state is ListState) { | |
| return state.elements; | |
| } | |
| return null; | |
| } | |
| @override | |
| Map<DartObjectImpl, DartObjectImpl>? toMapValue() { | |
| var state = this.state; | |
| if (state is MapState) { | |
| return state.entries; | |
| } | |
| return null; | |
| } | |
| @override | |
| ({List<DartObject> positional, Map<String, DartObject> named})? | |
| toRecordValue() { | |
| if (state case RecordState(:var positionalFields, :var namedFields)) { | |
| return (positional: positionalFields, named: namedFields); | |
| } else { | |
| return null; | |
| } | |
| } | |
| @override | |
| Set<DartObjectImpl>? toSetValue() { | |
| var state = this.state; | |
| if (state is SetState) { | |
| return state.elements; | |
| } | |
| return null; | |
| } | |
| @override | |
| String toString() { | |
| return "${type.getDisplayString()} ($state)"; | |
| } | |
| @override | |
| String? toStringValue() { | |
| var state = this.state; | |
| if (state is StringState) { | |
| return state.value; | |
| } | |
| return null; | |
| } | |
| @override | |
| String? toSymbolValue() { | |
| var state = this.state; | |
| if (state is SymbolState) { | |
| return state.value; | |
| } | |
| return null; | |
| } | |
| @override | |
| TypeImpl? toTypeValue() { | |
| return state.tryCast<TypeState>()?.type; | |
| } | |
| @override | |
| TypeImpl? toTypeValueNotExtensionTypeErased() { | |
| return state.tryCast<TypeState>()?.typeNotExtensionTypeErased; | |
| } | |
| /// Return the result of type-instantiating this object as [type]. | |
| /// | |
| /// [typeArguments] are the type arguments used in the instantiation. | |
| DartObjectImpl typeInstantiate( | |
| TypeSystemImpl typeSystem, | |
| FunctionTypeImpl type, | |
| List<TypeImpl> typeArguments, | |
| ) { | |
| var functionState = state as FunctionState; | |
| return DartObjectImpl( | |
| typeSystem, | |
| type, | |
| FunctionState( | |
| functionState.element, | |
| typeArguments: typeArguments, | |
| viaTypeAlias: functionState._viaTypeAlias, | |
| ), | |
| ); | |
| } | |
| /// Set the `index` and `_name` fields for this enum constant. | |
| void updateEnumConstant({required int index, required String name}) { | |
| var state = this.state as GenericState; | |
| state._hashCode = null; | |
| var fields = state.fields; | |
| fields['index'] = DartObjectImpl( | |
| _typeSystem, | |
| _typeSystem.typeProvider.intType, | |
| IntState(index), | |
| ); | |
| fields['_name'] = DartObjectImpl( | |
| _typeSystem, | |
| _typeSystem.typeProvider.stringType, | |
| StringState(name), | |
| ); | |
| } | |
| /// Throw an exception if the given [object]'s state does not represent a Type | |
| /// value. | |
| void _assertType(DartObjectImpl object) { | |
| if (object.state is! TypeState) { | |
| throw EvaluationException(diag.constEvalTypeType); | |
| } | |
| } | |
| } | |
| /// The state of an object representing a double. | |
| class DoubleState extends NumState { | |
| /// A state that can be used to represent a double whose value is not known. | |
| static DoubleState UNKNOWN_VALUE = DoubleState(null); | |
| /// The value of this instance. | |
| final double? value; | |
| /// Initialize a newly created state to represent a double with the given | |
| /// [value]. | |
| DoubleState(this.value); | |
| @override | |
| int get hashCode => value == null ? 0 : value.hashCode; | |
| @override | |
| bool get isUnknown => value == null; | |
| @override | |
| String get typeName => "double"; | |
| @override | |
| bool operator ==(Object other) => | |
| other is DoubleState && identical(value, other.value); | |
| @override | |
| NumState add(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! + rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! + rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| StringState convertToString() { | |
| if (value == null) { | |
| return StringState.UNKNOWN_VALUE; | |
| } | |
| return StringState(value.toString()); | |
| } | |
| @override | |
| NumState divide(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! / rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! / rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState greaterThan(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! > rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! > rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! >= rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! >= rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| IntState integerDivide(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return IntState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return IntState.UNKNOWN_VALUE; | |
| } | |
| var result = value! / rightValue.toDouble(); | |
| if (result.isFinite) { | |
| return IntState(result.toInt()); | |
| } | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return IntState.UNKNOWN_VALUE; | |
| } | |
| double result = value! / rightValue; | |
| if (result.isFinite) { | |
| return IntState(result.toInt()); | |
| } | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| var value = this.value; | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } else if (value.isNaN) { | |
| // `double.nan` equality will always be `false`. | |
| return BoolState.FALSE_STATE; | |
| } | |
| if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } else if (rightValue.isNaN) { | |
| // `double.nan` equality will always be `false`. | |
| return BoolState.FALSE_STATE; | |
| } | |
| return BoolState.from(identical(value, rightValue)); | |
| } else if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(identical(value, rightValue.toDouble())); | |
| } | |
| return BoolState.FALSE_STATE; | |
| } | |
| @override | |
| BoolState lessThan(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! < rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! < rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState lessThanOrEqual(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! <= rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value! <= rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| NumState minus(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! - rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! - rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| NumState negated() { | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(-value!); | |
| } | |
| @override | |
| NumState remainder(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! % rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! % rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| NumState times(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! * rightValue.toDouble()); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value! * rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| String toString() => value == null ? "-unknown-" : value.toString(); | |
| } | |
| /// Exception that would be thrown during the evaluation of Dart code. | |
| class EvaluationException { | |
| /// The [LocatableDiagnostic] associated with the exception. | |
| final LocatableDiagnostic locatableDiagnostic; | |
| /// Returns `true` if the evaluation exception is a runtime exception. | |
| final bool isRuntimeException; | |
| /// Initialize a newly created exception to have the given | |
| /// [locatableDiagnostic]. | |
| EvaluationException( | |
| this.locatableDiagnostic, { | |
| this.isRuntimeException = false, | |
| }); | |
| } | |
| /// The state of an object representing a function. | |
| class FunctionState extends InstanceState { | |
| /// The element representing the function being modeled. | |
| final ExecutableElementImpl element; | |
| final List<TypeImpl>? typeArguments; | |
| /// The type alias which was referenced when tearing off a constructor, | |
| /// if this function is a constructor tear-off, referenced via a type alias, | |
| /// and the type alias is not a proper rename for the class, and the | |
| /// constructor tear-off is generic, so the tear-off cannot be considered | |
| /// equivalent to tearing off the associated constructor function of the | |
| /// aliased class. | |
| /// | |
| /// Otherwise null. | |
| final TypeAliasElementImpl? _viaTypeAlias; | |
| /// Initialize a newly created state to represent the function with the given | |
| /// [element]. | |
| FunctionState( | |
| this.element, { | |
| this.typeArguments, | |
| TypeAliasElementImpl? viaTypeAlias, | |
| }) : _viaTypeAlias = viaTypeAlias; | |
| @override | |
| int get hashCode => element.hashCode; | |
| @override | |
| String get typeName => "Function"; | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is! FunctionState) { | |
| return false; | |
| } | |
| if (element != other.element) { | |
| return false; | |
| } | |
| var typeArguments = this.typeArguments; | |
| var otherTypeArguments = other.typeArguments; | |
| if (typeArguments == null || otherTypeArguments == null) { | |
| return typeArguments == null && otherTypeArguments == null; | |
| } | |
| if (typeArguments.length != otherTypeArguments.length) { | |
| return false; | |
| } | |
| if (_viaTypeAlias != other._viaTypeAlias) { | |
| return false; | |
| } | |
| for (var i = 0; i < typeArguments.length; i++) { | |
| if (typeArguments[i] != otherTypeArguments[i]) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| @override | |
| StringState convertToString() => StringState(element.name); | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (rightOperand is FunctionState) { | |
| var otherElement = rightOperand.element; | |
| if (element.baseElement != otherElement.baseElement) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| if (_viaTypeAlias != rightOperand._viaTypeAlias) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| var typeArguments = this.typeArguments; | |
| var otherTypeArguments = rightOperand.typeArguments; | |
| if (typeArguments == null || otherTypeArguments == null) { | |
| return BoolState.from( | |
| typeArguments == null && otherTypeArguments == null, | |
| ); | |
| } | |
| if (typeArguments.length != otherTypeArguments.length) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| for (var i = 0; i < typeArguments.length; i++) { | |
| if (!typeSystem.runtimeTypesEqual( | |
| typeArguments[i], | |
| otherTypeArguments[i], | |
| )) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| } | |
| return BoolState.TRUE_STATE; | |
| } | |
| return BoolState.FALSE_STATE; | |
| } | |
| @override | |
| String toString() => element.name ?? '<unnamed>'; | |
| } | |
| /// The state of an object representing a Dart object for which there is no more | |
| /// specific state. | |
| class GenericState extends InstanceState { | |
| /// Pseudo-field that we use to represent fields in the superclass. | |
| static String SUPERCLASS_FIELD = "(super)"; | |
| /// The enclosing [DartObjectImpl]. | |
| late DartObjectImpl _object; | |
| /// The values of the fields of this instance. | |
| final Map<String, DartObjectImpl> _fieldMap; | |
| /// The cached hash code. | |
| int? _hashCode; | |
| /// Information about the constructor invoked to generate this instance. | |
| final ConstructorInvocationDataImpl? invocation; | |
| @override | |
| final bool isUnknown; | |
| GenericState(this._fieldMap, {this.invocation, this.isUnknown = false}); | |
| @override | |
| Map<String, DartObjectImpl> get fields => _fieldMap; | |
| @override | |
| int get hashCode { | |
| return _hashCode ??= Object.hashAll([ | |
| ..._fieldMap.keys, | |
| ..._fieldMap.values, | |
| ]); | |
| } | |
| @override | |
| String get typeName => "user defined type"; | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is GenericState) { | |
| HashSet<String> otherFields = HashSet<String>.from( | |
| other._fieldMap.keys.toSet(), | |
| ); | |
| for (String fieldName in _fieldMap.keys.toSet()) { | |
| if (_fieldMap[fieldName] != other._fieldMap[fieldName]) { | |
| return false; | |
| } | |
| otherFields.remove(fieldName); | |
| } | |
| for (String fieldName in otherFields) { | |
| if (other._fieldMap[fieldName] != _fieldMap[fieldName]) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| @override | |
| StringState convertToString() => StringState.UNKNOWN_VALUE; | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) { | |
| var type = _object.type; | |
| if (type is InterfaceTypeImpl) { | |
| bool isFromDartCoreObject(ExecutableElement? element) { | |
| var enclosing = element?.enclosingElement; | |
| return enclosing is ClassElement && enclosing.isDartCoreObject; | |
| } | |
| var element = type.element; | |
| var library = element.library; | |
| var eqEq = type.lookUpMethod('==', library, concrete: true); | |
| if (!isFromDartCoreObject(eqEq)) { | |
| return false; | |
| } | |
| if (featureSet.isEnabled(Feature.patterns)) { | |
| var hash = type.lookUpGetter('hashCode', library, concrete: true); | |
| if (!isFromDartCoreObject(hash)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return BoolState.from(this == rightOperand); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer buffer = StringBuffer(); | |
| List<String> fieldNames = _fieldMap.keys.toList(); | |
| fieldNames.sort(); | |
| bool first = true; | |
| for (String fieldName in fieldNames) { | |
| if (first) { | |
| first = false; | |
| } else { | |
| buffer.write('; '); | |
| } | |
| buffer.write(fieldName); | |
| buffer.write(' = '); | |
| buffer.write(_fieldMap[fieldName]); | |
| } | |
| return buffer.toString(); | |
| } | |
| } | |
| /// The state of an object representing a Dart object. | |
| abstract class InstanceState { | |
| /// If this represents a generic dart object, return a map from its field | |
| /// names to their values. Otherwise return null. | |
| Map<String, DartObjectImpl>? get fields => null; | |
| /// Return `true` if this object represents an object whose type is 'bool'. | |
| bool get isBool => false; | |
| /// Return `true` if this object represents an object whose type is either | |
| /// 'bool', 'num', 'String', or 'Null'. | |
| bool get isBoolNumStringOrNull => false; | |
| /// Return `true` if this object represents an object whose type is 'int'. | |
| bool get isInt => false; | |
| /// Return `true` if this object represents an object whose type is an invalid | |
| /// type. | |
| bool get isInvalid => false; | |
| /// Return `true` if this object represents the value 'null'. | |
| bool get isNull => false; | |
| /// Return `true` if this object represents an unknown value. | |
| bool get isUnknown => false; | |
| /// Return the name of the type of this value. | |
| String get typeName; | |
| /// Returns the result of invoking the '+' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| InstanceState add(InstanceState rightOperand) { | |
| if (this is StringState && rightOperand is StringState) { | |
| return concatenate(rightOperand); | |
| } | |
| assertNumStringOrNull(this); | |
| assertNumStringOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Throws an exception if the given [state] does not represent a `bool` | |
| /// value. | |
| void assertBool(InstanceState? state) { | |
| if (state is! BoolState) { | |
| throw EvaluationException(diag.constEvalTypeBool); | |
| } | |
| } | |
| /// Throws an exception if the given [state] does not represent an `int` or | |
| /// `null` value. | |
| void assertIntOrNull(InstanceState state) { | |
| if (!(state is IntState || state is NullState)) { | |
| throw EvaluationException(diag.constEvalTypeInt); | |
| } | |
| } | |
| /// Throws an exception if the given [state] does not represent a `num` or | |
| /// `null` value. | |
| void assertNumOrNull(InstanceState state) { | |
| if (!(state is NumState || state is NullState)) { | |
| throw EvaluationException(diag.constEvalTypeNum); | |
| } | |
| } | |
| /// Throws an exception if the given [state] does not represent a `num`, | |
| /// `String`, or `null` value. | |
| void assertNumStringOrNull(InstanceState state) { | |
| if (!(state is NumState || state is StringState || state is NullState)) { | |
| throw EvaluationException(diag.constEvalTypeNumString); | |
| } | |
| } | |
| /// Throws an exception if the given [state] does not represent a `String` | |
| /// value. | |
| void assertString(InstanceState state) { | |
| if (state is! StringState) { | |
| throw EvaluationException(diag.constEvalTypeString); | |
| } | |
| } | |
| /// Return the result of invoking the '&' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState bitAnd(InstanceState rightOperand) { | |
| assertIntOrNull(this); | |
| assertIntOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '~' operator on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState bitNot() { | |
| assertIntOrNull(this); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '|' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState bitOr(InstanceState rightOperand) { | |
| assertIntOrNull(this); | |
| assertIntOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '^' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState bitXor(InstanceState rightOperand) { | |
| assertIntOrNull(this); | |
| assertIntOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the ' ' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| StringState concatenate(InstanceState rightOperand) { | |
| assertString(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of applying boolean conversion to this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState convertToBool() => BoolState.FALSE_STATE; | |
| /// Return the result of converting this object to a String. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| StringState convertToString(); | |
| /// Return the result of invoking the '/' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| NumState divide(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '==' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand); | |
| /// Return the result of invoking the '>' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState greaterThan(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '>=' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Returns `true` if this value, inside a library with the [featureSet], | |
| /// has primitive equality, so can be used at compile-time. | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => false; | |
| /// Return the result of invoking the '~/' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState integerDivide(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the identical function on this object with | |
| /// the [rightOperand]. | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand); | |
| /// Returns the result of invoking the '&&' operator on this object with the | |
| /// [rightOperandComputer]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState lazyAnd(InstanceState? Function() rightOperandComputer) { | |
| assertBool(this); | |
| if (convertToBool() == BoolState.FALSE_STATE) { | |
| return this as BoolState; | |
| } | |
| var rightOperand = rightOperandComputer(); | |
| assertBool(rightOperand); | |
| return rightOperand!.convertToBool(); | |
| } | |
| /// Returns the result of invoking the '||' operator on this object with the | |
| /// [rightOperandComputer]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState lazyOr(InstanceState? Function() rightOperandComputer) { | |
| assertBool(this); | |
| if (convertToBool() == BoolState.TRUE_STATE) { | |
| return this as BoolState; | |
| } | |
| var rightOperand = rightOperandComputer(); | |
| assertBool(rightOperand); | |
| return rightOperand!.convertToBool(); | |
| } | |
| /// Return the result of invoking the '<' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState lessThan(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '<=' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState lessThanOrEqual(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '&' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState logicalAnd(InstanceState rightOperand) { | |
| assertBool(this); | |
| assertBool(rightOperand); | |
| var leftValue = convertToBool().value; | |
| var rightValue = rightOperand.convertToBool().value; | |
| if (leftValue == null || rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(leftValue & rightValue); | |
| } | |
| /// Return the result of invoking the '!' operator on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState logicalNot() { | |
| assertBool(this); | |
| return BoolState.TRUE_STATE; | |
| } | |
| /// Return the result of invoking the '|' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState logicalOr(InstanceState rightOperand) { | |
| assertBool(this); | |
| assertBool(rightOperand); | |
| var leftValue = convertToBool().value; | |
| var rightValue = rightOperand.convertToBool().value; | |
| if (leftValue == null || rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(leftValue | rightValue); | |
| } | |
| /// Return the result of invoking the '>>>' operator on this object | |
| /// with the [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState logicalShiftRight(InstanceState rightOperand) { | |
| assertIntOrNull(this); | |
| assertIntOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '^' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| BoolState logicalXor(InstanceState rightOperand) { | |
| assertBool(this); | |
| assertBool(rightOperand); | |
| var leftValue = convertToBool().value; | |
| var rightValue = rightOperand.convertToBool().value; | |
| if (leftValue == null || rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(leftValue ^ rightValue); | |
| } | |
| /// Return the result of invoking the '-' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| NumState minus(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '-' operator on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| NumState negated() { | |
| assertNumOrNull(this); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '%' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| NumState remainder(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '<<' operator on this object with | |
| /// the [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState shiftLeft(InstanceState rightOperand) { | |
| assertIntOrNull(this); | |
| assertIntOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '>>' operator on this object with | |
| /// the [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState shiftRight(InstanceState rightOperand) { | |
| assertIntOrNull(this); | |
| assertIntOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the 'length' getter on this object. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| IntState stringLength() { | |
| assertString(this); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| /// Return the result of invoking the '*' operator on this object with the | |
| /// [rightOperand]. | |
| /// | |
| /// Throws an [EvaluationException] if the operator is not appropriate for an | |
| /// object of this kind. | |
| NumState times(InstanceState rightOperand) { | |
| assertNumOrNull(this); | |
| assertNumOrNull(rightOperand); | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| } | |
| /// The state of an object representing an int. | |
| class IntState extends NumState { | |
| /// A state that can be used to represent an int whose value is not known. | |
| static IntState UNKNOWN_VALUE = IntState(null); | |
| /// The value of this instance. | |
| final int? value; | |
| /// Initialize a newly created state to represent an int with the given | |
| /// [value]. | |
| IntState(this.value); | |
| @override | |
| int get hashCode => value == null ? 0 : value.hashCode; | |
| @override | |
| bool get isInt => true; | |
| @override | |
| bool get isUnknown => value == null; | |
| @override | |
| String get typeName => "int"; | |
| @override | |
| bool operator ==(Object other) => other is IntState && (value == other.value); | |
| @override | |
| NumState add(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| if (rightOperand is DoubleState) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(value! + rightValue); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value!.toDouble() + rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| IntState bitAnd(InstanceState rightOperand) { | |
| assertIntOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(value! & rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| IntState bitNot() { | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(~value!); | |
| } | |
| @override | |
| IntState bitOr(InstanceState rightOperand) { | |
| assertIntOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(value! | rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| IntState bitXor(InstanceState rightOperand) { | |
| assertIntOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(value! ^ rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| StringState convertToString() { | |
| if (value == null) { | |
| return StringState.UNKNOWN_VALUE; | |
| } | |
| return StringState(value.toString()); | |
| } | |
| @override | |
| NumState divide(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } else { | |
| return DoubleState(value!.toDouble() / rightValue.toDouble()); | |
| } | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value!.toDouble() / rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState greaterThan(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.compareTo(rightValue) > 0); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.toDouble() > rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.compareTo(rightValue) >= 0); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.toDouble() >= rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| IntState integerDivide(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } else if (rightValue == 0) { | |
| throw EvaluationException( | |
| diag.constEvalThrowsIdbze, | |
| isRuntimeException: true, | |
| ); | |
| } | |
| return IntState(value! ~/ rightValue); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| double result = value!.toDouble() / rightValue; | |
| if (result.isFinite) { | |
| return IntState(result.toInt()); | |
| } | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value == rightValue); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(rightValue == value!.toDouble()); | |
| } | |
| return BoolState.FALSE_STATE; | |
| } | |
| @override | |
| BoolState lessThan(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.compareTo(rightValue) < 0); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.toDouble() < rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| BoolState lessThanOrEqual(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.compareTo(rightValue) <= 0); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value!.toDouble() <= rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| IntState logicalShiftRight(InstanceState rightOperand) { | |
| assertIntOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } else if (rightValue >= 64) { | |
| return IntState(0); | |
| } else if (rightValue >= 0) { | |
| // TODO(srawlins): Replace with real operator once stable, like: | |
| // return new IntState(value >>> rightValue); | |
| return IntState( | |
| (value! >> rightValue) & ((1 << (64 - rightValue)) - 1), | |
| ); | |
| } | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| NumState minus(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| if (rightOperand is DoubleState) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(value! - rightValue); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value!.toDouble() - rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| NumState negated() { | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(-value!); | |
| } | |
| @override | |
| NumState remainder(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| if (rightOperand is DoubleState) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightValue != 0) { | |
| return IntState(value! % rightValue); | |
| } | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value!.toDouble() % rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| IntState shiftLeft(InstanceState rightOperand) { | |
| assertIntOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } else if (rightValue.bitLength > 31) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightValue >= 0) { | |
| return IntState(value! << rightValue); | |
| } | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| IntState shiftRight(InstanceState rightOperand) { | |
| assertIntOrNull(rightOperand); | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } else if (rightValue.bitLength > 31) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightValue >= 0) { | |
| return IntState(value! >> rightValue); | |
| } | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| NumState times(InstanceState rightOperand) { | |
| assertNumOrNull(rightOperand); | |
| if (value == null) { | |
| if (rightOperand is DoubleState) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is IntState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return IntState(value! * rightValue); | |
| } else if (rightOperand is DoubleState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return DoubleState.UNKNOWN_VALUE; | |
| } | |
| return DoubleState(value!.toDouble() * rightValue); | |
| } | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| String toString() => value == null ? "-unknown-" : value.toString(); | |
| } | |
| /// An invalid constant that contains diagnostic information. | |
| class InvalidConstant implements Constant { | |
| /// The offset of the entity that the evaluation error is reported at. | |
| final int offset; | |
| /// The length of the entity that the evaluation error is reported at. | |
| final int length; | |
| /// The [LocatableDiagnostic] that is being reported. | |
| /// | |
| /// This object encapsulates the diagnostic code that is being reported, the | |
| /// arguments required to complete the diagnostic message, and additional | |
| /// context messages for the error, including stack trace information if the | |
| /// error occurs within a constructor. | |
| LocatableDiagnostic locatableDiagnostic; | |
| /// Whether to omit reporting this error. | |
| /// | |
| /// If set to `true`, error reporting will ignore this invalid constant. | |
| /// Defaults to `false`. | |
| /// | |
| /// The `ConstantVisitor` can change this to `true` when there's already an | |
| /// error reported and this invalid constant would be an unnecessary follow-on | |
| /// error. | |
| bool avoidReporting; | |
| /// Whether this error was an exception thrown during constant evaluation. | |
| /// | |
| /// In [ConstantEvaluationEngine.evaluateAndFormatErrorsInConstructorCall], | |
| /// we convert this error into a | |
| /// [diag.constEvalThrowsException] with a context message | |
| /// pointing to where the exception was thrown. | |
| final bool isRuntimeException; | |
| /// Whether the constant evaluation encounters an unresolved expression. | |
| final bool isUnresolved; | |
| /// Whether this error is for a named read that resolution has already | |
| /// reported as invalid, such as an undefined name. | |
| /// | |
| /// Diagnostics that say that the expression isn't constant would only | |
| /// repeat that report, so the constant verifier doesn't report them. The | |
| /// evaluator still reports them to its own diagnostic reporter, because | |
| /// clients read them from [ElementAnnotation.constantEvaluationErrors]. | |
| final bool isInvalidNamedRead; | |
| /// Creates a duplicate instance of [other], with a different [entity]. | |
| factory InvalidConstant.copyWithEntity({ | |
| required InvalidConstant other, | |
| required SyntacticEntity entity, | |
| }) { | |
| return InvalidConstant.forEntity( | |
| entity: entity, | |
| locatableDiagnostic: other.locatableDiagnostic, | |
| avoidReporting: other.avoidReporting, | |
| isUnresolved: other.isUnresolved, | |
| isInvalidNamedRead: other.isInvalidNamedRead, | |
| isRuntimeException: other.isRuntimeException, | |
| ); | |
| } | |
| /// Creates a constant evaluation error associated with an [element]. | |
| InvalidConstant.forElement({ | |
| required Element element, | |
| required LocatableDiagnostic locatableDiagnostic, | |
| bool avoidReporting = false, | |
| bool isUnresolved = false, | |
| bool isRuntimeException = false, | |
| }) : this._( | |
| length: element.name!.length, | |
| offset: element.firstFragment.nameOffset ?? -1, | |
| locatableDiagnostic: locatableDiagnostic, | |
| avoidReporting: avoidReporting, | |
| isUnresolved: isUnresolved, | |
| isRuntimeException: isRuntimeException, | |
| ); | |
| /// Creates a constant evaluation error associated with a token or node | |
| /// [entity]. | |
| InvalidConstant.forEntity({ | |
| required SyntacticEntity entity, | |
| required LocatableDiagnostic locatableDiagnostic, | |
| bool avoidReporting = false, | |
| bool isUnresolved = false, | |
| bool isInvalidNamedRead = false, | |
| bool isRuntimeException = false, | |
| }) : this._( | |
| offset: entity.offset, | |
| length: entity.length, | |
| locatableDiagnostic: locatableDiagnostic, | |
| avoidReporting: avoidReporting, | |
| isUnresolved: isUnresolved, | |
| isInvalidNamedRead: isInvalidNamedRead, | |
| isRuntimeException: isRuntimeException, | |
| ); | |
| /// Creates a generic error depending on the [node] provided. | |
| factory InvalidConstant.genericError({ | |
| required AstNode node, | |
| bool isUnresolved = false, | |
| bool isInvalidNamedRead = false, | |
| }) { | |
| var parent = node.parent2; | |
| // The value of a named argument is in the argument list too. | |
| if (parent is NamedArgument) { | |
| parent = parent.parent2; | |
| } | |
| // Don't ask an `Annotation` for its parent: an element annotation's AST is | |
| // detached, and `Annotation.parent2` throws then. | |
| if (parent is ArgumentList) { | |
| if (parent.parent2 case ast.ConstructorInvocation invocation | |
| when invocation.isConst) { | |
| return InvalidConstant.forEntity( | |
| entity: node, | |
| locatableDiagnostic: diag.constWithNonConstantArgument, | |
| isUnresolved: isUnresolved, | |
| isInvalidNamedRead: isInvalidNamedRead, | |
| ); | |
| } | |
| } | |
| return InvalidConstant.forEntity( | |
| entity: node, | |
| locatableDiagnostic: diag.invalidConstant, | |
| isUnresolved: isUnresolved, | |
| isInvalidNamedRead: isInvalidNamedRead, | |
| ); | |
| } | |
| InvalidConstant._({ | |
| required this.offset, | |
| required this.length, | |
| required this.locatableDiagnostic, | |
| this.avoidReporting = false, | |
| this.isUnresolved = false, | |
| this.isInvalidNamedRead = false, | |
| this.isRuntimeException = false, | |
| }); | |
| } | |
| /// The state of an object representing a list. | |
| class ListState extends InstanceState { | |
| final TypeImpl elementType; | |
| final List<DartObjectImpl> elements; | |
| @override | |
| final bool isUnknown; | |
| @override | |
| late final int hashCode = Object.hashAll(elements); | |
| ListState({ | |
| required TypeImpl elementType, | |
| required this.elements, | |
| this.isUnknown = false, | |
| }) : elementType = elementType.extensionTypeErasure; | |
| /// Creates a state that represents a list whose value is not known. | |
| factory ListState.unknown(TypeImpl elementType) => | |
| ListState(elementType: elementType, elements: [], isUnknown: true); | |
| @override | |
| String get typeName => "List"; | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is ListState) { | |
| List<DartObjectImpl> otherElements = other.elements; | |
| int count = elements.length; | |
| if (otherElements.length != count) { | |
| return false; | |
| } else if (count == 0) { | |
| return true; | |
| } | |
| for (int i = 0; i < count; i++) { | |
| if (elements[i] != otherElements[i]) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| @override | |
| StringState convertToString() => StringState.UNKNOWN_VALUE; | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (isUnknown || rightOperand.isUnknown) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is! ListState) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| return BoolState.from( | |
| typeSystem.normalize(elementType) == | |
| typeSystem.normalize(rightOperand.elementType) && | |
| this == rightOperand, | |
| ); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer buffer = StringBuffer(); | |
| buffer.write('['); | |
| bool first = true; | |
| for (var element in elements) { | |
| if (first) { | |
| first = false; | |
| } else { | |
| buffer.write(', '); | |
| } | |
| buffer.write(element); | |
| } | |
| buffer.write(']'); | |
| return buffer.toString(); | |
| } | |
| } | |
| /// The state of an object representing a map. | |
| class MapState extends InstanceState { | |
| final TypeImpl _keyType; | |
| final TypeImpl _valueType; | |
| /// The entries in the map. | |
| final Map<DartObjectImpl, DartObjectImpl> entries; | |
| /// Whether the map contains an entry that has an unknown value. | |
| final bool _isUnknown; | |
| @override | |
| late final int hashCode = Object.hashAll([ | |
| ...entries.keys, | |
| ...entries.values, | |
| ]); | |
| /// Initializes a newly created state to represent a set with the given | |
| /// [entries]. | |
| MapState({ | |
| required TypeImpl keyType, | |
| required TypeImpl valueType, | |
| required this.entries, | |
| bool isUnknown = false, | |
| }) : _keyType = keyType.extensionTypeErasure, | |
| _valueType = valueType.extensionTypeErasure, | |
| _isUnknown = isUnknown; | |
| /// Creates a state that represents a map whose value is not known. | |
| factory MapState.unknown(TypeImpl keyType, TypeImpl valueType) => MapState( | |
| keyType: keyType, | |
| valueType: valueType, | |
| entries: {}, | |
| isUnknown: true, | |
| ); | |
| @override | |
| bool get isUnknown => _isUnknown; | |
| @override | |
| String get typeName => "Map"; | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is MapState) { | |
| Map<DartObjectImpl, DartObjectImpl> otherElements = other.entries; | |
| int count = entries.length; | |
| if (otherElements.length != count) { | |
| return false; | |
| } else if (count == 0) { | |
| return true; | |
| } | |
| for (DartObjectImpl key in entries.keys) { | |
| var value = entries[key]; | |
| var otherValue = otherElements[key]; | |
| if (value != otherValue) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| @override | |
| StringState convertToString() => StringState.UNKNOWN_VALUE; | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (isUnknown || rightOperand.isUnknown) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is! MapState) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| return BoolState.from( | |
| typeSystem.normalize(_keyType) == | |
| typeSystem.normalize(rightOperand._keyType) && | |
| typeSystem.normalize(_valueType) == | |
| typeSystem.normalize(rightOperand._valueType) && | |
| this == rightOperand, | |
| ); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer buffer = StringBuffer(); | |
| buffer.write('{'); | |
| bool first = true; | |
| entries.forEach((DartObjectImpl key, DartObjectImpl value) { | |
| if (first) { | |
| first = false; | |
| } else { | |
| buffer.write(', '); | |
| } | |
| buffer.write(key); | |
| buffer.write(' = '); | |
| buffer.write(value); | |
| }); | |
| buffer.write('}'); | |
| return buffer.toString(); | |
| } | |
| } | |
| /// The state of an object representing the value 'null'. | |
| class NullState extends InstanceState { | |
| /// An instance representing the boolean value 'null'. | |
| static NullState NULL_STATE = NullState(); | |
| @override | |
| final bool isInvalid; | |
| NullState({this.isInvalid = false}); | |
| @override | |
| int get hashCode => 0; | |
| @override | |
| bool get isBoolNumStringOrNull => true; | |
| @override | |
| bool get isNull => true; | |
| @override | |
| String get typeName => "Null"; | |
| @override | |
| bool operator ==(Object other) => other is NullState; | |
| @override | |
| BoolState convertToBool() { | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| StringState convertToString() => StringState("null"); | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return BoolState.from(rightOperand is NullState); | |
| } | |
| @override | |
| BoolState logicalNot() { | |
| throw EvaluationException(diag.constEvalThrowsException); | |
| } | |
| @override | |
| String toString() => "null"; | |
| } | |
| /// The state of an object representing a number. | |
| abstract class NumState extends InstanceState { | |
| @override | |
| bool get isBoolNumStringOrNull => true; | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| } | |
| /// The state of an object representing a record. | |
| class RecordState extends InstanceState { | |
| /// The values of the positional fields. | |
| final List<DartObjectImpl> positionalFields; | |
| /// The values of the named fields. | |
| final Map<String, DartObjectImpl> namedFields; | |
| @override | |
| late final int hashCode = Object.hashAll([ | |
| ...positionalFields, | |
| ...namedFields.values, | |
| ]); | |
| /// Initialize a newly created state to represent a record with the given | |
| /// values of [positionalFields] and [namedFields]. | |
| RecordState(this.positionalFields, this.namedFields); | |
| @override | |
| String get typeName => 'Record'; | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is! RecordState) { | |
| return false; | |
| } | |
| var positionalCount = positionalFields.length; | |
| var otherPositionalFields = other.positionalFields; | |
| if (otherPositionalFields.length != positionalCount) { | |
| return false; | |
| } | |
| var namedCount = namedFields.length; | |
| var otherNamedFields = other.namedFields; | |
| if (otherNamedFields.length != namedCount) { | |
| return false; | |
| } | |
| for (var i = 0; i < positionalCount; i++) { | |
| if (positionalFields[i] != otherPositionalFields[i]) { | |
| return false; | |
| } | |
| } | |
| for (var entry in namedFields.entries) { | |
| var otherValue = otherNamedFields[entry.key]; | |
| if (otherValue == null) { | |
| return false; | |
| } | |
| if (entry.value != otherValue) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| @override | |
| // The behavior of `toString` is undefined. | |
| StringState convertToString() => StringState.UNKNOWN_VALUE; | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| /// Returns the value of the field with the given [name]. | |
| DartObject? getField(String name) { | |
| var index = RecordTypeExtension.positionalFieldIndex(name); | |
| if (index != null && index < positionalFields.length) { | |
| return positionalFields[index]; | |
| } else { | |
| return namedFields[name]; | |
| } | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) { | |
| return [ | |
| ...positionalFields, | |
| ...namedFields.values, | |
| ].every((e) => e.hasPrimitiveEquality(featureSet)); | |
| } | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (this != rightOperand) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| @override | |
| String toString() { | |
| var buffer = StringBuffer(); | |
| buffer.write('('); | |
| bool first = true; | |
| for (var value in positionalFields) { | |
| if (first) { | |
| first = false; | |
| } else { | |
| buffer.write(', '); | |
| } | |
| buffer.write(value); | |
| } | |
| var entries = namedFields.entries.toList(); | |
| if (entries.isNotEmpty) { | |
| entries.sort((first, second) => first.key.compareTo(second.key)); | |
| if (!first) { | |
| buffer.write(', '); | |
| first = true; | |
| } | |
| buffer.write('{'); | |
| for (var entry in entries) { | |
| if (first) { | |
| first = false; | |
| } else { | |
| buffer.write(', '); | |
| } | |
| buffer.write(entry.key); | |
| buffer.write(': '); | |
| buffer.write(entry.value); | |
| } | |
| buffer.write('}'); | |
| } | |
| buffer.write(')'); | |
| return buffer.toString(); | |
| } | |
| } | |
| /// The state of an object representing a set. | |
| class SetState extends InstanceState { | |
| final TypeImpl _elementType; | |
| /// The elements of the set. | |
| final Set<DartObjectImpl> elements; | |
| /// Whether the set contains an entry that has an unknown value. | |
| final bool _isUnknown; | |
| @override | |
| late final int hashCode = Object.hashAll(elements); | |
| /// Initializes a newly created state to represent a set with the given | |
| /// [elements]. | |
| SetState({ | |
| required TypeImpl elementType, | |
| required this.elements, | |
| bool isUnknown = false, | |
| }) : _elementType = elementType.extensionTypeErasure, | |
| _isUnknown = isUnknown; | |
| /// Creates a state that represents a list whose value is not known. | |
| factory SetState.unknown(TypeImpl elementType) => | |
| SetState(elementType: elementType, elements: {}, isUnknown: true); | |
| @override | |
| bool get isUnknown => _isUnknown; | |
| @override | |
| String get typeName => "Set"; | |
| @override | |
| bool operator ==(Object other) { | |
| if (other is SetState) { | |
| List<DartObjectImpl> currentElements = elements.toList(); | |
| List<DartObjectImpl> otherElements = other.elements.toList(); | |
| int count = currentElements.length; | |
| if (otherElements.length != count) { | |
| return false; | |
| } else if (count == 0) { | |
| return true; | |
| } | |
| for (int i = 0; i < count; i++) { | |
| if (currentElements[i] != otherElements[i]) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| @override | |
| StringState convertToString() => StringState.UNKNOWN_VALUE; | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (isUnknown || rightOperand.isUnknown) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is! SetState) { | |
| return BoolState.FALSE_STATE; | |
| } | |
| return BoolState.from( | |
| typeSystem.normalize(_elementType) == | |
| typeSystem.normalize(rightOperand._elementType) && | |
| this == rightOperand, | |
| ); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer buffer = StringBuffer(); | |
| buffer.write('{'); | |
| bool first = true; | |
| for (var element in elements) { | |
| if (first) { | |
| first = false; | |
| } else { | |
| buffer.write(', '); | |
| } | |
| buffer.write(element); | |
| } | |
| buffer.write('}'); | |
| return buffer.toString(); | |
| } | |
| } | |
| /// The state of an object representing a string. | |
| class StringState extends InstanceState { | |
| /// A state that can be used to represent a string whose value is not known. | |
| static StringState UNKNOWN_VALUE = StringState(null); | |
| /// The value of this instance. | |
| final String? value; | |
| /// Initialize a newly created state to represent the given [value]. | |
| StringState(this.value); | |
| @override | |
| int get hashCode => value == null ? 0 : value.hashCode; | |
| @override | |
| bool get isBoolNumStringOrNull => true; | |
| @override | |
| bool get isUnknown => value == null; | |
| @override | |
| String get typeName => "String"; | |
| @override | |
| bool operator ==(Object other) => | |
| other is StringState && (value == other.value); | |
| @override | |
| StringState concatenate(InstanceState rightOperand) { | |
| if (value == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is StringState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return UNKNOWN_VALUE; | |
| } | |
| return StringState("$value$rightValue"); | |
| } | |
| return super.concatenate(rightOperand); | |
| } | |
| @override | |
| StringState convertToString() => this; | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is StringState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value == rightValue); | |
| } | |
| return BoolState.FALSE_STATE; | |
| } | |
| @override | |
| IntState stringLength() { | |
| if (value == null) { | |
| return IntState.UNKNOWN_VALUE; | |
| } | |
| return IntState(value!.length); | |
| } | |
| @override | |
| String toString() => value == null ? "-unknown-" : "'$value'"; | |
| } | |
| /// The state of an object representing a symbol. | |
| class SymbolState extends InstanceState { | |
| /// The value of this instance. | |
| final String? value; | |
| /// Initialize a newly created state to represent the given [value]. | |
| SymbolState(this.value); | |
| @override | |
| int get hashCode => value == null ? 0 : value.hashCode; | |
| @override | |
| String get typeName => "Symbol"; | |
| @override | |
| bool operator ==(Object other) => | |
| other is SymbolState && (value == other.value); | |
| @override | |
| StringState convertToString() { | |
| if (value == null) { | |
| return StringState.UNKNOWN_VALUE; | |
| } | |
| return StringState(value); | |
| } | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| if (value == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is SymbolState) { | |
| var rightValue = rightOperand.value; | |
| if (rightValue == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(value == rightValue); | |
| } | |
| return BoolState.FALSE_STATE; | |
| } | |
| @override | |
| String toString() => value == null ? "-unknown-" : "#$value"; | |
| } | |
| /// The state of an object representing a type. | |
| class TypeState extends InstanceState { | |
| /// The element representing the type being modeled. | |
| final TypeImpl? type; | |
| /// From the point of view of constant evaluation, the value is [type]. | |
| /// But some clients want to know the explicit provided type. | |
| final TypeImpl? typeNotExtensionTypeErased; | |
| factory TypeState(TypeImpl? type) { | |
| return TypeState._( | |
| type: type?.extensionTypeErasure, | |
| typeNotExtensionTypeErased: type, | |
| ); | |
| } | |
| TypeState._({required this.type, required this.typeNotExtensionTypeErased}); | |
| @override | |
| int get hashCode => type?.hashCode ?? 0; | |
| @override | |
| String get typeName => "Type"; | |
| @override | |
| bool operator ==(Object other) => other is TypeState && (type == other.type); | |
| @override | |
| StringState convertToString() { | |
| if (type case var type?) { | |
| return StringState(type.getDisplayString()); | |
| } | |
| return StringState.UNKNOWN_VALUE; | |
| } | |
| @override | |
| BoolState equalEqual(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| return isIdentical(typeSystem, rightOperand); | |
| } | |
| @override | |
| bool hasPrimitiveEquality(FeatureSet featureSet) => true; | |
| @override | |
| BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) { | |
| var type = this.type; | |
| if (type == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| if (rightOperand is TypeState) { | |
| var rightType = rightOperand.type; | |
| if (rightType == null) { | |
| return BoolState.UNKNOWN_VALUE; | |
| } | |
| return BoolState.from(typeSystem.runtimeTypesEqual(type, rightType)); | |
| } | |
| return BoolState.FALSE_STATE; | |
| } | |
| @override | |
| String toString() { | |
| return type?.getDisplayString() ?? '-unknown-'; | |
| } | |
| } |