| Index: packages/analyzer/lib/src/dart/element/type.dart
|
| diff --git a/packages/analyzer/lib/src/dart/element/type.dart b/packages/analyzer/lib/src/dart/element/type.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4bc84b629fb4168d1bd8d04aba814c4ac388cc9c
|
| --- /dev/null
|
| +++ b/packages/analyzer/lib/src/dart/element/type.dart
|
| @@ -0,0 +1,2786 @@
|
| +// 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.
|
| +
|
| +library analyzer.src.dart.element.type;
|
| +
|
| +import 'dart:collection';
|
| +
|
| +import 'package:analyzer/dart/ast/token.dart';
|
| +import 'package:analyzer/dart/element/element.dart';
|
| +import 'package:analyzer/dart/element/type.dart';
|
| +import 'package:analyzer/src/dart/element/element.dart';
|
| +import 'package:analyzer/src/dart/element/member.dart';
|
| +import 'package:analyzer/src/generated/engine.dart'
|
| + show AnalysisContext, AnalysisEngine;
|
| +import 'package:analyzer/src/generated/type_system.dart';
|
| +import 'package:analyzer/src/generated/utilities_collection.dart';
|
| +import 'package:analyzer/src/generated/utilities_dart.dart';
|
| +
|
| +/**
|
| + * Type of callbacks used by [DeferredFunctionTypeImpl].
|
| + */
|
| +typedef FunctionTypedElement FunctionTypedElementComputer();
|
| +
|
| +/**
|
| + * Computer of type arguments which is used to delay computing of type
|
| + * arguments until they are requested, instead of at the [ParameterizedType]
|
| + * creation time.
|
| + */
|
| +typedef List<DartType> TypeArgumentsComputer();
|
| +
|
| +/**
|
| + * A [Type] that represents the type 'bottom'.
|
| + */
|
| +class BottomTypeImpl extends TypeImpl {
|
| + /**
|
| + * The unique instance of this class.
|
| + */
|
| + static final BottomTypeImpl instance = new BottomTypeImpl._();
|
| +
|
| + /**
|
| + * Prevent the creation of instances of this class.
|
| + */
|
| + BottomTypeImpl._() : super(null, "<bottom>");
|
| +
|
| + @override
|
| + int get hashCode => 0;
|
| +
|
| + @override
|
| + bool get isBottom => true;
|
| +
|
| + @override
|
| + bool operator ==(Object object) => identical(object, this);
|
| +
|
| + @override
|
| + bool isMoreSpecificThan(DartType type,
|
| + [bool withDynamic = false, Set<Element> visitedElements]) =>
|
| + true;
|
| +
|
| + @override
|
| + bool isSubtypeOf(DartType type) => true;
|
| +
|
| + @override
|
| + bool isSupertypeOf(DartType type) => false;
|
| +
|
| + @override
|
| + TypeImpl pruned(List<FunctionTypeAliasElement> prune) => this;
|
| +
|
| + @override
|
| + BottomTypeImpl substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) =>
|
| + this;
|
| +}
|
| +
|
| +/**
|
| + * The type created internally if a circular reference is ever detected in a
|
| + * function type.
|
| + */
|
| +class CircularFunctionTypeImpl extends DynamicTypeImpl
|
| + implements FunctionTypeImpl {
|
| + CircularFunctionTypeImpl() : super._circular();
|
| +
|
| + @override
|
| + List<ParameterElement> get baseParameters => ParameterElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + DartType get baseReturnType => DynamicTypeImpl.instance;
|
| +
|
| + @override
|
| + List<TypeParameterElement> get boundTypeParameters =>
|
| + TypeParameterElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + FunctionTypedElement get element => super.element;
|
| +
|
| + @override
|
| + bool get isInstantiated => false;
|
| +
|
| + @override
|
| + Map<String, DartType> get namedParameterTypes => <String, DartType>{};
|
| +
|
| + @override
|
| + List<FunctionTypeAliasElement> get newPrune =>
|
| + FunctionTypeAliasElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + List<String> get normalParameterNames => <String>[];
|
| +
|
| + @override
|
| + List<DartType> get normalParameterTypes => DartType.EMPTY_LIST;
|
| +
|
| + @override
|
| + List<String> get optionalParameterNames => <String>[];
|
| +
|
| + @override
|
| + List<DartType> get optionalParameterTypes => DartType.EMPTY_LIST;
|
| +
|
| + @override
|
| + List<ParameterElement> get parameters => ParameterElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + List<FunctionTypeAliasElement> get prunedTypedefs =>
|
| + FunctionTypeAliasElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + DartType get returnType => DynamicTypeImpl.instance;
|
| +
|
| + @override
|
| + List<DartType> get typeArguments => DartType.EMPTY_LIST;
|
| +
|
| + @override
|
| + List<TypeParameterElement> get typeFormals => TypeParameterElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + List<TypeParameterElement> get typeParameters =>
|
| + TypeParameterElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + bool get _isInstantiated => false;
|
| +
|
| + @override
|
| + List<DartType> get _typeArguments => DartType.EMPTY_LIST;
|
| +
|
| + @override
|
| + void set _typeArguments(List<DartType> arguments) {
|
| + throw new UnsupportedError('Cannot have type arguments');
|
| + }
|
| +
|
| + @override
|
| + List<TypeParameterElement> get _typeParameters =>
|
| + TypeParameterElement.EMPTY_LIST;
|
| +
|
| + @override
|
| + void set _typeParameters(List<TypeParameterElement> parameters) {
|
| + throw new UnsupportedError('Cannot have type parameters');
|
| + }
|
| +
|
| + @override
|
| + bool operator ==(Object object) => object is CircularFunctionTypeImpl;
|
| +
|
| + @override
|
| + void appendTo(StringBuffer buffer) {
|
| + buffer.write('...');
|
| + }
|
| +
|
| + @override
|
| + FunctionTypeImpl instantiate(List<DartType> argumentTypes) => this;
|
| +
|
| + @override
|
| + FunctionTypeImpl pruned(List<FunctionTypeAliasElement> prune) => this;
|
| +
|
| + @override
|
| + FunctionType substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) {
|
| + return this;
|
| + }
|
| +
|
| + @override
|
| + FunctionTypeImpl substitute3(List<DartType> argumentTypes) => this;
|
| +
|
| + @override
|
| + void _forEachParameterType(
|
| + ParameterKind kind, callback(String name, DartType type)) {
|
| + // There are no parameters.
|
| + }
|
| +
|
| + @override
|
| + void _freeVariablesInFunctionType(
|
| + FunctionType type, Set<TypeParameterType> free) {
|
| + // There are no free variables
|
| + }
|
| +
|
| + @override
|
| + void _freeVariablesInInterfaceType(
|
| + InterfaceType type, Set<TypeParameterType> free) {
|
| + // There are no free variables
|
| + }
|
| +
|
| + @override
|
| + void _freeVariablesInType(DartType type, Set<TypeParameterType> free) {
|
| + // There are no free variables
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Type created internally if a circular reference is ever detected. Behaves
|
| + * like `dynamic`, except that when converted to a string it is displayed as
|
| + * `...`.
|
| + */
|
| +class CircularTypeImpl extends DynamicTypeImpl {
|
| + CircularTypeImpl() : super._circular();
|
| +
|
| + @override
|
| + bool operator ==(Object object) => object is CircularTypeImpl;
|
| +
|
| + @override
|
| + void appendTo(StringBuffer buffer) {
|
| + buffer.write('...');
|
| + }
|
| +
|
| + @override
|
| + TypeImpl pruned(List<FunctionTypeAliasElement> prune) => this;
|
| +}
|
| +
|
| +/**
|
| + * The type of a function, method, constructor, getter, or setter that has been
|
| + * resynthesized from a summary. The actual underlying element won't be
|
| + * constructed until it's needed.
|
| + */
|
| +class DeferredFunctionTypeImpl extends FunctionTypeImpl {
|
| + /**
|
| + * Callback which should be invoked when the element associated with this
|
| + * function type is needed.
|
| + *
|
| + * Once the callback has been invoked, it is set to `null` to reduce GC
|
| + * pressure.
|
| + */
|
| + FunctionTypedElementComputer _computeElement;
|
| +
|
| + /**
|
| + * If [_computeElement] has been called, the value it returned. Otherwise
|
| + * `null`.
|
| + */
|
| + FunctionTypedElement _computedElement;
|
| +
|
| + DeferredFunctionTypeImpl(this._computeElement, String name,
|
| + List<DartType> typeArguments, bool isInstantiated)
|
| + : super._(null, name, null, typeArguments, isInstantiated);
|
| +
|
| + @override
|
| + FunctionTypedElement get element {
|
| + if (_computeElement != null) {
|
| + _computedElement = _computeElement();
|
| + _computeElement = null;
|
| + }
|
| + return _computedElement;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * The [Type] representing the type `dynamic`.
|
| + */
|
| +class DynamicTypeImpl extends TypeImpl {
|
| + /**
|
| + * The unique instance of this class.
|
| + */
|
| + static final DynamicTypeImpl instance = new DynamicTypeImpl._();
|
| +
|
| + /**
|
| + * Prevent the creation of instances of this class.
|
| + */
|
| + DynamicTypeImpl._()
|
| + : super(new DynamicElementImpl(), Keyword.DYNAMIC.syntax) {
|
| + (element as DynamicElementImpl).type = this;
|
| + }
|
| +
|
| + /**
|
| + * Constructor used by [CircularTypeImpl].
|
| + */
|
| + DynamicTypeImpl._circular() : super(instance.element, Keyword.DYNAMIC.syntax);
|
| +
|
| + @override
|
| + int get hashCode => 1;
|
| +
|
| + @override
|
| + bool get isDynamic => true;
|
| +
|
| + @override
|
| + bool operator ==(Object object) => identical(object, this);
|
| +
|
| + @override
|
| + bool isMoreSpecificThan(DartType type,
|
| + [bool withDynamic = false, Set<Element> visitedElements]) {
|
| + // T is S
|
| + if (identical(this, type)) {
|
| + return true;
|
| + }
|
| + // else
|
| + return withDynamic;
|
| + }
|
| +
|
| + @override
|
| + bool isSubtypeOf(DartType type) => true;
|
| +
|
| + @override
|
| + bool isSupertypeOf(DartType type) => true;
|
| +
|
| + @override
|
| + TypeImpl pruned(List<FunctionTypeAliasElement> prune) => this;
|
| +
|
| + @override
|
| + DartType substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) {
|
| + int length = parameterTypes.length;
|
| + for (int i = 0; i < length; i++) {
|
| + if (parameterTypes[i] == this) {
|
| + return argumentTypes[i];
|
| + }
|
| + }
|
| + return this;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * The type of a function, method, constructor, getter, or setter.
|
| + */
|
| +class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| + /**
|
| + * The list of [typeArguments].
|
| + */
|
| + List<DartType> _typeArguments;
|
| +
|
| + /**
|
| + * The list of [typeParameters], if it has been computed already. Otherwise
|
| + * `null`.
|
| + */
|
| + List<TypeParameterElement> _typeParameters;
|
| +
|
| + /**
|
| + * True if this type is the result of instantiating type parameters (and thus
|
| + * any type parameters bound by the typedef should be considered part of
|
| + * [typeParameters] rather than [typeFormals]).
|
| + */
|
| + final bool _isInstantiated;
|
| +
|
| + /**
|
| + * The set of typedefs which should not be expanded when exploring this type,
|
| + * to avoid creating infinite types in response to self-referential typedefs.
|
| + */
|
| + final List<FunctionTypeAliasElement> prunedTypedefs;
|
| +
|
| + /**
|
| + * Initialize a newly created function type to be declared by the given
|
| + * [element], and also initialize [typeArguments] to match the
|
| + * [typeParameters], which permits later substitution.
|
| + */
|
| + FunctionTypeImpl(ExecutableElement element,
|
| + [List<FunctionTypeAliasElement> prunedTypedefs])
|
| + : this._(element, null, prunedTypedefs, null, false);
|
| +
|
| + /**
|
| + * Initialize a newly created function type to be declared by the given
|
| + * [element], with the given [name] and [typeArguments].
|
| + */
|
| + FunctionTypeImpl.elementWithNameAndArgs(Element element, String name,
|
| + List<DartType> typeArguments, bool isInstantiated)
|
| + : this._(element, name, null, typeArguments, isInstantiated);
|
| +
|
| + /**
|
| + * Initialize a newly created function type to be declared by the given
|
| + * [element].
|
| + */
|
| + FunctionTypeImpl.forTypedef(FunctionTypeAliasElement element,
|
| + [List<FunctionTypeAliasElement> prunedTypedefs])
|
| + : this._(element, element?.name, prunedTypedefs, null, false);
|
| +
|
| + /**
|
| + * Private constructor.
|
| + */
|
| + FunctionTypeImpl._(TypeParameterizedElement element, String name,
|
| + this.prunedTypedefs, this._typeArguments, this._isInstantiated)
|
| + : super(element, name);
|
| +
|
| + /**
|
| + * Return the base parameter elements of this function element.
|
| + */
|
| + List<ParameterElement> get baseParameters => element.parameters;
|
| +
|
| + /**
|
| + * Return the return type defined by this function's element.
|
| + */
|
| + DartType get baseReturnType => element.returnType;
|
| +
|
| + @deprecated
|
| + @override
|
| + List<TypeParameterElement> get boundTypeParameters => typeFormals;
|
| +
|
| + @override
|
| + String get displayName {
|
| + String name = this.name;
|
| + if (name == null || name.length == 0) {
|
| + // Function types have an empty name when they are defined implicitly by
|
| + // either a closure or as part of a parameter declaration.
|
| + StringBuffer buffer = new StringBuffer();
|
| + appendTo(buffer);
|
| + name = buffer.toString();
|
| + }
|
| + return name;
|
| + }
|
| +
|
| + @override
|
| + FunctionTypedElement get element => super.element;
|
| +
|
| + @override
|
| + int get hashCode {
|
| + if (element == null) {
|
| + return 0;
|
| + }
|
| + // Reference the arrays of parameters
|
| + List<DartType> normalParameterTypes = this.normalParameterTypes;
|
| + List<DartType> optionalParameterTypes = this.optionalParameterTypes;
|
| + Iterable<DartType> namedParameterTypes = this.namedParameterTypes.values;
|
| + // Generate the hashCode
|
| + int code = returnType.hashCode;
|
| + for (int i = 0; i < normalParameterTypes.length; i++) {
|
| + code = (code << 1) + normalParameterTypes[i].hashCode;
|
| + }
|
| + for (int i = 0; i < optionalParameterTypes.length; i++) {
|
| + code = (code << 1) + optionalParameterTypes[i].hashCode;
|
| + }
|
| + for (DartType type in namedParameterTypes) {
|
| + code = (code << 1) + type.hashCode;
|
| + }
|
| + return code;
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if this type is the result of instantiating type parameters.
|
| + */
|
| + bool get isInstantiated => _isInstantiated;
|
| +
|
| + @override
|
| + Map<String, DartType> get namedParameterTypes {
|
| + Map<String, DartType> types = <String, DartType>{};
|
| + _forEachParameterType(ParameterKind.NAMED, (name, type) {
|
| + types[name] = type;
|
| + });
|
| + return types;
|
| + }
|
| +
|
| + /**
|
| + * Determine the new set of typedefs which should be pruned when expanding
|
| + * this function type.
|
| + */
|
| + List<FunctionTypeAliasElement> get newPrune {
|
| + Element element = this.element;
|
| + if (element is FunctionTypeAliasElement && !element.isSynthetic) {
|
| + // This typedef should be pruned, along with anything that was previously
|
| + // pruned.
|
| + if (prunedTypedefs == null) {
|
| + return <FunctionTypeAliasElement>[element];
|
| + } else {
|
| + return new List<FunctionTypeAliasElement>.from(prunedTypedefs)
|
| + ..add(element);
|
| + }
|
| + } else {
|
| + // This is not a typedef, so nothing additional needs to be pruned.
|
| + return prunedTypedefs;
|
| + }
|
| + }
|
| +
|
| + @override
|
| + List<String> get normalParameterNames {
|
| + return baseParameters
|
| + .where((parameter) => parameter.parameterKind == ParameterKind.REQUIRED)
|
| + .map((parameter) => parameter.name)
|
| + .toList();
|
| + }
|
| +
|
| + @override
|
| + List<DartType> get normalParameterTypes {
|
| + List<DartType> types = <DartType>[];
|
| + _forEachParameterType(ParameterKind.REQUIRED, (name, type) {
|
| + types.add(type);
|
| + });
|
| + return types;
|
| + }
|
| +
|
| + @override
|
| + List<String> get optionalParameterNames {
|
| + return baseParameters
|
| + .where(
|
| + (parameter) => parameter.parameterKind == ParameterKind.POSITIONAL)
|
| + .map((parameter) => parameter.name)
|
| + .toList();
|
| + }
|
| +
|
| + @override
|
| + List<DartType> get optionalParameterTypes {
|
| + List<DartType> types = <DartType>[];
|
| + _forEachParameterType(ParameterKind.POSITIONAL, (name, type) {
|
| + types.add(type);
|
| + });
|
| + return types;
|
| + }
|
| +
|
| + @override
|
| + List<ParameterElement> get parameters {
|
| + List<ParameterElement> baseParameters = this.baseParameters;
|
| + // no parameters, quick return
|
| + int parameterCount = baseParameters.length;
|
| + if (parameterCount == 0) {
|
| + return baseParameters;
|
| + }
|
| +
|
| + // create specialized parameters
|
| + var specializedParams = new List<ParameterElement>(parameterCount);
|
| +
|
| + var parameterTypes = TypeParameterTypeImpl.getTypes(typeParameters);
|
| + for (int i = 0; i < parameterCount; i++) {
|
| + var parameter = baseParameters[i];
|
| + if (parameter?.type == null) {
|
| + specializedParams[i] = parameter;
|
| + continue;
|
| + }
|
| +
|
| + // Check if parameter type depends on defining type type arguments, or
|
| + // if it needs to be pruned.
|
| +
|
| + if (parameter is FieldFormalParameterElement) {
|
| + // TODO(jmesserly): this seems like it won't handle pruning correctly.
|
| + specializedParams[i] = new FieldFormalParameterMember(parameter, this);
|
| + continue;
|
| + }
|
| +
|
| + var baseType = parameter.type as TypeImpl;
|
| + TypeImpl type;
|
| + if (typeArguments.isEmpty ||
|
| + typeArguments.length != typeParameters.length) {
|
| + type = baseType.pruned(newPrune);
|
| + } else {
|
| + type = baseType.substitute2(typeArguments, parameterTypes, newPrune);
|
| + }
|
| +
|
| + specializedParams[i] = identical(type, baseType)
|
| + ? parameter
|
| + : new ParameterMember(parameter, this, type);
|
| + }
|
| + return specializedParams;
|
| + }
|
| +
|
| + @override
|
| + DartType get returnType {
|
| + DartType baseReturnType = this.baseReturnType;
|
| + if (baseReturnType == null) {
|
| + // TODO(brianwilkerson) This is a patch. The return type should never be
|
| + // null and we need to understand why it is and fix it.
|
| + return DynamicTypeImpl.instance;
|
| + }
|
| + // If there are no arguments to substitute, or if the arguments size doesn't
|
| + // match the parameter size, return the base return type.
|
| + if (typeArguments.length == 0 ||
|
| + typeArguments.length != typeParameters.length) {
|
| + return (baseReturnType as TypeImpl).pruned(newPrune);
|
| + }
|
| + return (baseReturnType as TypeImpl).substitute2(typeArguments,
|
| + TypeParameterTypeImpl.getTypes(typeParameters), newPrune);
|
| + }
|
| +
|
| + /**
|
| + * A list containing the actual types of the type arguments.
|
| + */
|
| + List<DartType> get typeArguments {
|
| + if (_typeArguments == null) {
|
| + // TODO(jmesserly): reuse TypeParameterTypeImpl.getTypes once we can
|
| + // make it generic, which will allow it to return List<DartType> instead
|
| + // of List<TypeParameterType>.
|
| + if (typeParameters.isEmpty) {
|
| + _typeArguments = DartType.EMPTY_LIST;
|
| + } else {
|
| + _typeArguments = new List<DartType>.from(
|
| + typeParameters.map((t) => t.type),
|
| + growable: false);
|
| + }
|
| + }
|
| + return _typeArguments;
|
| + }
|
| +
|
| + @override
|
| + List<TypeParameterElement> get typeFormals {
|
| + if (_isInstantiated || element == null) {
|
| + return TypeParameterElement.EMPTY_LIST;
|
| + }
|
| + List<TypeParameterElement> baseTypeFormals = element.typeParameters;
|
| + int formalCount = baseTypeFormals.length;
|
| + if (formalCount == 0) {
|
| + return TypeParameterElement.EMPTY_LIST;
|
| + }
|
| +
|
| + // Create type formals with specialized bounds.
|
| + // For example `<U extends T>` where T comes from an outer scope.
|
| + List<TypeParameterElement> result =
|
| + new List<TypeParameterElement>(formalCount);
|
| +
|
| + for (int i = 0; i < formalCount; i++) {
|
| + result[i] = TypeParameterMember.from(baseTypeFormals[i], this);
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + @override
|
| + List<TypeParameterElement> get typeParameters {
|
| + if (_typeParameters == null) {
|
| + // Combine the generic type variables from all enclosing contexts, except
|
| + // for this generic function's type variables. Those variables are
|
| + // tracked in [boundTypeParameters].
|
| + _typeParameters = <TypeParameterElement>[];
|
| +
|
| + Element e = element;
|
| + while (e != null) {
|
| + // If a static method, skip the enclosing class type parameters.
|
| + if (e is MethodElement && e.isStatic) {
|
| + e = e.enclosingElement;
|
| + }
|
| + e = e.enclosingElement;
|
| + if (e is TypeParameterizedElement) {
|
| + _typeParameters.addAll(e.typeParameters);
|
| + }
|
| + }
|
| +
|
| + if (_isInstantiated) {
|
| + // Once the type has been instantiated, type parameters defined at the
|
| + // site of the declaration of the method are no longer considered part
|
| + // [boundTypeParameters]; they are part of [typeParameters].
|
| + List<TypeParameterElement> parametersToAdd = element?.typeParameters;
|
| + if (parametersToAdd != null) {
|
| + _typeParameters.addAll(parametersToAdd);
|
| + }
|
| + }
|
| + }
|
| + return _typeParameters;
|
| + }
|
| +
|
| + @override
|
| + bool operator ==(Object object) {
|
| + if (object is FunctionTypeImpl) {
|
| + if (typeFormals.length != object.typeFormals.length) {
|
| + return false;
|
| + }
|
| + // `<T>T -> T` should be equal to `<U>U -> U`
|
| + // To test this, we instantiate both types with the same (unique) type
|
| + // variables, and see if the result is equal.
|
| + if (typeFormals.isNotEmpty) {
|
| + List<DartType> freshVariables =
|
| + relateTypeFormals(this, object, (t, s) => t == s);
|
| + if (freshVariables == null) {
|
| + return false;
|
| + }
|
| + return instantiate(freshVariables) ==
|
| + object.instantiate(freshVariables);
|
| + }
|
| +
|
| + return returnType == object.returnType &&
|
| + TypeImpl.equalArrays(
|
| + normalParameterTypes, object.normalParameterTypes) &&
|
| + TypeImpl.equalArrays(
|
| + optionalParameterTypes, object.optionalParameterTypes) &&
|
| + _equals(namedParameterTypes, object.namedParameterTypes);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + @override
|
| + void appendTo(StringBuffer buffer) {
|
| + if (typeFormals.isNotEmpty) {
|
| + // To print a type with type variables, first make sure we have unique
|
| + // variable names to print.
|
| + Set<TypeParameterType> freeVariables = new HashSet<TypeParameterType>();
|
| + _freeVariablesInFunctionType(this, freeVariables);
|
| +
|
| + Set<String> namesToAvoid = new HashSet<String>();
|
| + for (DartType arg in freeVariables) {
|
| + if (arg is TypeParameterType) {
|
| + namesToAvoid.add(arg.displayName);
|
| + }
|
| + }
|
| +
|
| + List<DartType> instantiateTypeArgs = <DartType>[];
|
| + List<DartType> variables = <DartType>[];
|
| + buffer.write("<");
|
| + for (TypeParameterElement e in typeFormals) {
|
| + if (e != typeFormals[0]) {
|
| + buffer.write(",");
|
| + }
|
| + String name = e.name;
|
| + int counter = 0;
|
| + while (!namesToAvoid.add(name)) {
|
| + // Unicode subscript-zero is U+2080, zero is U+0030. Other digits
|
| + // are sequential from there. Thus +0x2050 will get us the subscript.
|
| + String subscript = new String.fromCharCodes(
|
| + counter.toString().codeUnits.map((n) => n + 0x2050));
|
| +
|
| + name = e.name + subscript;
|
| + counter++;
|
| + }
|
| + TypeParameterTypeImpl t =
|
| + new TypeParameterTypeImpl(new TypeParameterElementImpl(name, -1));
|
| + t.appendTo(buffer);
|
| + instantiateTypeArgs.add(t);
|
| + variables.add(e.type);
|
| + if (e.bound != null) {
|
| + buffer.write(" extends ");
|
| + TypeImpl renamed =
|
| + e.bound.substitute2(instantiateTypeArgs, variables);
|
| + renamed.appendTo(buffer);
|
| + }
|
| + }
|
| + buffer.write(">");
|
| +
|
| + // Instantiate it and print the resulting type. After instantiation, it
|
| + // will no longer have typeFormals, so we will continue below.
|
| + this.instantiate(instantiateTypeArgs).appendTo(buffer);
|
| + return;
|
| + }
|
| +
|
| + List<DartType> normalParameterTypes = this.normalParameterTypes;
|
| + List<DartType> optionalParameterTypes = this.optionalParameterTypes;
|
| + Map<String, DartType> namedParameterTypes = this.namedParameterTypes;
|
| + DartType returnType = this.returnType;
|
| +
|
| + bool needsComma = false;
|
| + void writeSeparator() {
|
| + if (needsComma) {
|
| + buffer.write(", ");
|
| + } else {
|
| + needsComma = true;
|
| + }
|
| + }
|
| +
|
| + void startOptionalParameters() {
|
| + if (needsComma) {
|
| + buffer.write(", ");
|
| + needsComma = false;
|
| + }
|
| + }
|
| +
|
| + buffer.write("(");
|
| + if (normalParameterTypes.isNotEmpty) {
|
| + for (DartType type in normalParameterTypes) {
|
| + writeSeparator();
|
| + (type as TypeImpl).appendTo(buffer);
|
| + }
|
| + }
|
| + if (optionalParameterTypes.isNotEmpty) {
|
| + startOptionalParameters();
|
| + buffer.write("[");
|
| + for (DartType type in optionalParameterTypes) {
|
| + writeSeparator();
|
| + (type as TypeImpl).appendTo(buffer);
|
| + }
|
| + buffer.write("]");
|
| + needsComma = true;
|
| + }
|
| + if (namedParameterTypes.isNotEmpty) {
|
| + startOptionalParameters();
|
| + buffer.write("{");
|
| + namedParameterTypes.forEach((String name, DartType type) {
|
| + writeSeparator();
|
| + buffer.write(name);
|
| + buffer.write(": ");
|
| + (type as TypeImpl).appendTo(buffer);
|
| + });
|
| + buffer.write("}");
|
| + needsComma = true;
|
| + }
|
| + buffer.write(")");
|
| + buffer.write(ElementImpl.RIGHT_ARROW);
|
| + if (returnType == null) {
|
| + buffer.write("null");
|
| + } else {
|
| + (returnType as TypeImpl).appendTo(buffer);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + FunctionTypeImpl instantiate(List<DartType> argumentTypes) {
|
| + if (argumentTypes.length != typeFormals.length) {
|
| + throw new ArgumentError(
|
| + "argumentTypes.length (${argumentTypes.length}) != "
|
| + "typeFormals.length (${typeFormals.length})");
|
| + }
|
| + if (argumentTypes.isEmpty) {
|
| + return this;
|
| + }
|
| +
|
| + // Given:
|
| + // {U/T} <S> T -> S
|
| + // Where {U/T} represents the typeArguments (U) and typeParameters (T) list,
|
| + // and <S> represents the typeFormals.
|
| + //
|
| + // Now instantiate([V]), and the result should be:
|
| + // {U/T, V/S} T -> S.
|
| + List<DartType> newTypeArgs = <DartType>[];
|
| + newTypeArgs.addAll(typeArguments);
|
| + newTypeArgs.addAll(argumentTypes);
|
| +
|
| + return new FunctionTypeImpl._(
|
| + element, name, prunedTypedefs, newTypeArgs, true);
|
| + }
|
| +
|
| + @override
|
| + bool isAssignableTo(DartType type) {
|
| + // A function type T may be assigned to a function type S, written T <=> S,
|
| + // iff T <: S.
|
| + return isSubtypeOf(type);
|
| + }
|
| +
|
| + @override
|
| + bool isMoreSpecificThan(DartType type,
|
| + [bool withDynamic = false, Set<Element> visitedElements]) {
|
| + // Note: visitedElements is only used for breaking recursion in the type
|
| + // hierarchy; we don't use it when recursing into the function type.
|
| + return relate(
|
| + this,
|
| + type,
|
| + (DartType t, DartType s, _, __) =>
|
| + (t as TypeImpl).isMoreSpecificThan(s, withDynamic),
|
| + new TypeSystemImpl().instantiateToBounds);
|
| + }
|
| +
|
| + @override
|
| + bool isSubtypeOf(DartType type) {
|
| + return relate(
|
| + this,
|
| + type,
|
| + (DartType t, DartType s, _, __) => t.isAssignableTo(s),
|
| + new TypeSystemImpl().instantiateToBounds);
|
| + }
|
| +
|
| + @override
|
| + FunctionTypeImpl pruned(List<FunctionTypeAliasElement> prune) {
|
| + if (prune == null) {
|
| + return this;
|
| + } else if (prune.contains(element)) {
|
| + // Circularity found. Prune the type declaration.
|
| + return new CircularFunctionTypeImpl();
|
| + } else {
|
| + // There should never be a reason to prune a type that has already been
|
| + // pruned, since pruning is only done when expanding a function type
|
| + // alias, and function type aliases are always expanded by starting with
|
| + // base types.
|
| + assert(this.prunedTypedefs == null);
|
| + List<DartType> typeArgs = typeArguments
|
| + .map((DartType t) => (t as TypeImpl).pruned(prune))
|
| + .toList(growable: false);
|
| + return new FunctionTypeImpl._(
|
| + element, name, prune, typeArgs, _isInstantiated);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + FunctionType substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) {
|
| + // Pruned types should only ever result from performing type variable
|
| + // substitution, and it doesn't make sense to substitute again after
|
| + // substituting once.
|
| + assert(this.prunedTypedefs == null);
|
| + if (argumentTypes.length != parameterTypes.length) {
|
| + throw new ArgumentError(
|
| + "argumentTypes.length (${argumentTypes.length}) != parameterTypes.length (${parameterTypes.length})");
|
| + }
|
| + Element element = this.element;
|
| + if (prune != null && prune.contains(element)) {
|
| + // Circularity found. Prune the type declaration.
|
| + return new CircularFunctionTypeImpl();
|
| + }
|
| + if (argumentTypes.length == 0) {
|
| + return this.pruned(prune);
|
| + }
|
| + List<DartType> typeArgs =
|
| + TypeImpl.substitute(typeArguments, argumentTypes, parameterTypes);
|
| + return new FunctionTypeImpl._(
|
| + element, name, prune, typeArgs, _isInstantiated);
|
| + }
|
| +
|
| + @override
|
| + FunctionTypeImpl substitute3(List<DartType> argumentTypes) =>
|
| + substitute2(argumentTypes, typeArguments);
|
| +
|
| + /**
|
| + * Invokes [callback] for each parameter of [kind] with the parameter's [name]
|
| + * and type after any type parameters have been applied.
|
| + */
|
| + void _forEachParameterType(
|
| + ParameterKind kind, callback(String name, DartType type)) {
|
| + List<ParameterElement> parameters = baseParameters;
|
| + if (parameters.isEmpty) {
|
| + return;
|
| + }
|
| +
|
| + List<DartType> typeParameters =
|
| + TypeParameterTypeImpl.getTypes(this.typeParameters);
|
| + int length = parameters.length;
|
| + for (int i = 0; i < length; i++) {
|
| + ParameterElement parameter = parameters[i];
|
| + if (parameter.parameterKind == kind) {
|
| + TypeImpl type = parameter.type ?? DynamicTypeImpl.instance;
|
| + if (typeArguments.length != 0 &&
|
| + typeArguments.length == typeParameters.length) {
|
| + type = type.substitute2(typeArguments, typeParameters, newPrune);
|
| + } else {
|
| + type = type.pruned(newPrune);
|
| + }
|
| +
|
| + callback(parameter.name, type);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _freeVariablesInFunctionType(
|
| + FunctionType type, Set<TypeParameterType> free) {
|
| + // Make some fresh variables to avoid capture.
|
| + List<DartType> typeArgs = DartType.EMPTY_LIST;
|
| + if (type.typeFormals.isNotEmpty) {
|
| + typeArgs = new List<DartType>.from(type.typeFormals.map((e) =>
|
| + new TypeParameterTypeImpl(new TypeParameterElementImpl(e.name, -1))));
|
| +
|
| + type = type.instantiate(typeArgs);
|
| + }
|
| +
|
| + for (ParameterElement p in type.parameters) {
|
| + _freeVariablesInType(p.type, free);
|
| + }
|
| + _freeVariablesInType(type.returnType, free);
|
| +
|
| + // Remove all of our bound variables.
|
| + free.removeAll(typeArgs);
|
| + }
|
| +
|
| + void _freeVariablesInInterfaceType(
|
| + InterfaceType type, Set<TypeParameterType> free) {
|
| + for (DartType typeArg in type.typeArguments) {
|
| + _freeVariablesInType(typeArg, free);
|
| + }
|
| + }
|
| +
|
| + void _freeVariablesInType(DartType type, Set<TypeParameterType> free) {
|
| + if (type is TypeParameterType) {
|
| + free.add(type);
|
| + } else if (type is FunctionType) {
|
| + _freeVariablesInFunctionType(type, free);
|
| + } else if (type is InterfaceType) {
|
| + _freeVariablesInInterfaceType(type, free);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Given a generic function type [g] and an instantiated function type [f],
|
| + * find a list of type arguments TArgs such that `g<TArgs> == f`,
|
| + * and return TArgs.
|
| + *
|
| + * This function must be called with type [f] that was instantiated from [g].
|
| + */
|
| + static Iterable<DartType> recoverTypeArguments(
|
| + FunctionType g, FunctionType f) {
|
| + // TODO(jmesserly): perhaps a better design here would be: instead of
|
| + // recording staticInvokeType on InvocationExpression, we could record the
|
| + // instantiated type arguments, that way we wouldn't need to recover them.
|
| + //
|
| + // For now though, this is a pretty quick operation.
|
| + assert(identical(g.element, f.element));
|
| + assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty);
|
| + assert(g.typeFormals.length + g.typeArguments.length ==
|
| + f.typeArguments.length);
|
| +
|
| + // Instantiation in Analyzer works like this:
|
| + // Given:
|
| + // {U/T} <S> T -> S
|
| + // Where {U/T} represents the typeArguments (U) and typeParameters (T) list,
|
| + // and <S> represents the typeFormals.
|
| + //
|
| + // Now instantiate([V]), and the result should be:
|
| + // {U/T, V/S} T -> S.
|
| + //
|
| + // Therefore, we can recover the typeArguments from our instantiated
|
| + // function.
|
| + return f.typeArguments.skip(g.typeArguments.length);
|
| + }
|
| +
|
| + /**
|
| + * Compares two function types [t] and [s] to see if their corresponding
|
| + * parameter types match [parameterRelation] and their return types match
|
| + * [returnRelation].
|
| + *
|
| + * Used for the various relations on function types which have the same
|
| + * structural rules for handling optional parameters and arity, but use their
|
| + * own relation for comparing corresponding parameters or return types.
|
| + *
|
| + * If [returnRelation] is omitted, uses [parameterRelation] for both.
|
| + */
|
| + static bool relate(
|
| + FunctionType t,
|
| + DartType other,
|
| + bool parameterRelation(
|
| + DartType t, DartType s, bool tIsCovariant, bool sIsCovariant),
|
| + DartType instantiateToBounds(DartType t),
|
| + {bool returnRelation(DartType t, DartType s)}) {
|
| + returnRelation ??= (t, s) => parameterRelation(t, s, false, false);
|
| +
|
| + // Trivial base cases.
|
| + if (other == null) {
|
| + return false;
|
| + } else if (identical(t, other) ||
|
| + other.isDynamic ||
|
| + other.isDartCoreFunction ||
|
| + other.isObject) {
|
| + return true;
|
| + } else if (other is! FunctionType) {
|
| + return false;
|
| + }
|
| +
|
| + // This type cast is safe, because we checked it above.
|
| + FunctionType s = other as FunctionType;
|
| + if (t.typeFormals.isNotEmpty) {
|
| + if (s.typeFormals.isEmpty) {
|
| + t = instantiateToBounds(t);
|
| + } else {
|
| + List<DartType> freshVariables = relateTypeFormals(t, s, returnRelation);
|
| + if (freshVariables == null) {
|
| + return false;
|
| + }
|
| + t = t.instantiate(freshVariables);
|
| + s = s.instantiate(freshVariables);
|
| + }
|
| + } else if (s.typeFormals.isNotEmpty) {
|
| + return false;
|
| + }
|
| +
|
| + // Test the return types.
|
| + DartType sRetType = s.returnType;
|
| + if (!sRetType.isVoid && !returnRelation(t.returnType, sRetType)) {
|
| + return false;
|
| + }
|
| +
|
| + // Test the parameter types.
|
| +
|
| + // TODO(jmesserly): this could be implemented with less allocation if we
|
| + // wanted, by taking advantage of the fact that positional arguments must
|
| + // appear before named ones.
|
| + var tRequired = <ParameterElement>[];
|
| + var tOptional = <ParameterElement>[];
|
| + var tNamed = <String, ParameterElement>{};
|
| + for (var p in t.parameters) {
|
| + var kind = p.parameterKind;
|
| + if (kind == ParameterKind.REQUIRED) {
|
| + tRequired.add(p);
|
| + } else if (kind == ParameterKind.POSITIONAL) {
|
| + tOptional.add(p);
|
| + } else {
|
| + assert(kind == ParameterKind.NAMED);
|
| + tNamed[p.name] = p;
|
| + }
|
| + }
|
| +
|
| + var sRequired = <ParameterElement>[];
|
| + var sOptional = <ParameterElement>[];
|
| + var sNamed = <String, ParameterElement>{};
|
| + for (var p in s.parameters) {
|
| + var kind = p.parameterKind;
|
| + if (kind == ParameterKind.REQUIRED) {
|
| + sRequired.add(p);
|
| + } else if (kind == ParameterKind.POSITIONAL) {
|
| + sOptional.add(p);
|
| + } else {
|
| + assert(kind == ParameterKind.NAMED);
|
| + sNamed[p.name] = p;
|
| + }
|
| + }
|
| +
|
| + // If one function has positional and the other has named parameters,
|
| + // they don't relate.
|
| + if (sOptional.isNotEmpty && tNamed.isNotEmpty ||
|
| + tOptional.isNotEmpty && sNamed.isNotEmpty) {
|
| + return false;
|
| + }
|
| +
|
| + // If the passed function includes more named parameters than we do, we
|
| + // don't relate.
|
| + if (tNamed.length < sNamed.length) {
|
| + return false;
|
| + }
|
| +
|
| + // For each named parameter in s, make sure we have a corresponding one
|
| + // that relates.
|
| + for (String key in sNamed.keys) {
|
| + var tParam = tNamed[key];
|
| + if (tParam == null) {
|
| + return false;
|
| + }
|
| + var sParam = sNamed[key];
|
| + if (!parameterRelation(
|
| + tParam.type, sParam.type, tParam.isCovariant, sParam.isCovariant)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // Make sure all of the positional parameters (both required and optional)
|
| + // relate to each other.
|
| + var tPositional = tRequired;
|
| + var sPositional = sRequired;
|
| +
|
| + if (tOptional.isNotEmpty) {
|
| + tPositional = tPositional.toList()..addAll(tOptional);
|
| + }
|
| +
|
| + if (sOptional.isNotEmpty) {
|
| + sPositional = sPositional.toList()..addAll(sOptional);
|
| + }
|
| +
|
| + // Check that s has enough required parameters.
|
| + if (sRequired.length < tRequired.length) {
|
| + return false;
|
| + }
|
| +
|
| + // Check that s does not include more positional parameters than we do.
|
| + if (tPositional.length < sPositional.length) {
|
| + return false;
|
| + }
|
| +
|
| + for (int i = 0; i < sPositional.length; i++) {
|
| + var tParam = tPositional[i];
|
| + var sParam = sPositional[i];
|
| + if (!parameterRelation(
|
| + tParam.type, sParam.type, tParam.isCovariant, sParam.isCovariant)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + /**
|
| + * Given two functions [f1] and [f2] where f1 and f2 are known to be
|
| + * generic function types (both have type formals), this checks that they
|
| + * have the same number of formals, and that those formals have bounds
|
| + * (e.g. `<T extends LowerBound>`) that satisfy [relation].
|
| + *
|
| + * The return value will be a new list of fresh type variables, that can be
|
| + * used to instantiate both function types, allowing further comparison.
|
| + * For example, given `<T>T -> T` and `<U>U -> U` we can instantiate them with
|
| + * `F` to get `F -> F` and `F -> F`, which we can see are equal.
|
| + */
|
| + static List<DartType> relateTypeFormals(
|
| + FunctionType f1, FunctionType f2, bool relation(DartType t, DartType s)) {
|
| + List<TypeParameterElement> params1 = f1.typeFormals;
|
| + List<TypeParameterElement> params2 = f2.typeFormals;
|
| + int count = params1.length;
|
| + if (params2.length != count) {
|
| + return null;
|
| + }
|
| + // We build up a substitution matching up the type parameters
|
| + // from the two types, {variablesFresh/variables1} and
|
| + // {variablesFresh/variables2}
|
| + List<DartType> variables1 = <DartType>[];
|
| + List<DartType> variables2 = <DartType>[];
|
| + List<DartType> variablesFresh = <DartType>[];
|
| + for (int i = 0; i < count; i++) {
|
| + TypeParameterElement p1 = params1[i];
|
| + TypeParameterElement p2 = params2[i];
|
| + TypeParameterElementImpl pFresh =
|
| + new TypeParameterElementImpl.synthetic(p2.name);
|
| +
|
| + DartType variable1 = p1.type;
|
| + DartType variable2 = p2.type;
|
| + DartType variableFresh = new TypeParameterTypeImpl(pFresh);
|
| +
|
| + variables1.add(variable1);
|
| + variables2.add(variable2);
|
| + variablesFresh.add(variableFresh);
|
| + DartType bound1 = p1.bound ?? DynamicTypeImpl.instance;
|
| + DartType bound2 = p2.bound ?? DynamicTypeImpl.instance;
|
| + bound1 = bound1.substitute2(variablesFresh, variables1);
|
| + bound2 = bound2.substitute2(variablesFresh, variables2);
|
| + pFresh.bound = bound2;
|
| + if (!relation(bound2, bound1)) {
|
| + return null;
|
| + }
|
| + }
|
| + return variablesFresh;
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if all of the name/type pairs in the first map ([firstTypes])
|
| + * are equal to the corresponding name/type pairs in the second map
|
| + * ([secondTypes]). The maps are expected to iterate over their entries in the
|
| + * same order in which those entries were added to the map.
|
| + */
|
| + static bool _equals(
|
| + Map<String, DartType> firstTypes, Map<String, DartType> secondTypes) {
|
| + if (secondTypes.length != firstTypes.length) {
|
| + return false;
|
| + }
|
| + Iterator<String> firstKeys = firstTypes.keys.iterator;
|
| + Iterator<String> secondKeys = secondTypes.keys.iterator;
|
| + while (firstKeys.moveNext() && secondKeys.moveNext()) {
|
| + String firstKey = firstKeys.current;
|
| + String secondKey = secondKeys.current;
|
| + TypeImpl firstType = firstTypes[firstKey];
|
| + TypeImpl secondType = secondTypes[secondKey];
|
| + if (firstKey != secondKey || firstType != secondType) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * A concrete implementation of an [InterfaceType].
|
| + */
|
| +class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
| + /**
|
| + * A list containing the actual types of the type arguments.
|
| + */
|
| + List<DartType> _typeArguments = DartType.EMPTY_LIST;
|
| +
|
| + /**
|
| + * If not `null` and [_typeArguments] is `null`, the actual type arguments
|
| + * should be computed (once) using this function.
|
| + */
|
| + TypeArgumentsComputer _typeArgumentsComputer;
|
| +
|
| + /**
|
| + * The set of typedefs which should not be expanded when exploring this type,
|
| + * to avoid creating infinite types in response to self-referential typedefs.
|
| + */
|
| + final List<FunctionTypeAliasElement> prunedTypedefs;
|
| +
|
| + /**
|
| + * The version of [element] for which members are cached.
|
| + */
|
| + int _versionOfCachedMembers = null;
|
| +
|
| + /**
|
| + * Cached [ConstructorElement]s - members or raw elements.
|
| + */
|
| + List<ConstructorElement> _constructors;
|
| +
|
| + /**
|
| + * Cached [PropertyAccessorElement]s - members or raw elements.
|
| + */
|
| + List<PropertyAccessorElement> _accessors;
|
| +
|
| + /**
|
| + * Cached [MethodElement]s - members or raw elements.
|
| + */
|
| + List<MethodElement> _methods;
|
| +
|
| + /**
|
| + * Initialize a newly created type to be declared by the given [element].
|
| + */
|
| + InterfaceTypeImpl(ClassElement element, [this.prunedTypedefs])
|
| + : super(element, element.displayName);
|
| +
|
| + /**
|
| + * Initialize a newly created type to be declared by the given [element],
|
| + * with the given [name] and [typeArguments].
|
| + */
|
| + InterfaceTypeImpl.elementWithNameAndArgs(
|
| + ClassElement element, String name, this._typeArgumentsComputer)
|
| + : prunedTypedefs = null,
|
| + super(element, name) {
|
| + _typeArguments = null;
|
| + }
|
| +
|
| + /**
|
| + * Initialize a newly created type to have the given [name]. This constructor
|
| + * should only be used in cases where there is no declaration of the type.
|
| + */
|
| + InterfaceTypeImpl.named(String name)
|
| + : prunedTypedefs = null,
|
| + super(null, name);
|
| +
|
| + /**
|
| + * Private constructor.
|
| + */
|
| + InterfaceTypeImpl._(Element element, String name, this.prunedTypedefs)
|
| + : super(element, name);
|
| +
|
| + @override
|
| + List<PropertyAccessorElement> get accessors {
|
| + _flushCachedMembersIfStale();
|
| + if (_accessors == null) {
|
| + List<PropertyAccessorElement> accessors = element.accessors;
|
| + List<PropertyAccessorElement> members =
|
| + new List<PropertyAccessorElement>(accessors.length);
|
| + for (int i = 0; i < accessors.length; i++) {
|
| + members[i] = PropertyAccessorMember.from(accessors[i], this);
|
| + }
|
| + _accessors = members;
|
| + }
|
| + return _accessors;
|
| + }
|
| +
|
| + @override
|
| + List<ConstructorElement> get constructors {
|
| + _flushCachedMembersIfStale();
|
| + if (_constructors == null) {
|
| + List<ConstructorElement> constructors = element.constructors;
|
| + List<ConstructorElement> members =
|
| + new List<ConstructorElement>(constructors.length);
|
| + for (int i = 0; i < constructors.length; i++) {
|
| + members[i] = ConstructorMember.from(constructors[i], this);
|
| + }
|
| + _constructors = members;
|
| + }
|
| + return _constructors;
|
| + }
|
| +
|
| + @override
|
| + String get displayName {
|
| + String name = this.name;
|
| + List<DartType> typeArguments = this.typeArguments;
|
| + bool allDynamic = true;
|
| + for (DartType type in typeArguments) {
|
| + if (type != null && !type.isDynamic) {
|
| + allDynamic = false;
|
| + break;
|
| + }
|
| + }
|
| + // If there is at least one non-dynamic type, then list them out
|
| + if (!allDynamic) {
|
| + StringBuffer buffer = new StringBuffer();
|
| + buffer.write(name);
|
| + buffer.write("<");
|
| + for (int i = 0; i < typeArguments.length; i++) {
|
| + if (i != 0) {
|
| + buffer.write(", ");
|
| + }
|
| + DartType typeArg = typeArguments[i];
|
| + buffer.write(typeArg.displayName);
|
| + }
|
| + buffer.write(">");
|
| + name = buffer.toString();
|
| + }
|
| + return name;
|
| + }
|
| +
|
| + @override
|
| + ClassElement get element => super.element as ClassElement;
|
| +
|
| + @override
|
| + int get hashCode {
|
| + ClassElement element = this.element;
|
| + if (element == null) {
|
| + return 0;
|
| + }
|
| + return element.hashCode;
|
| + }
|
| +
|
| + @override
|
| + List<InterfaceType> get interfaces {
|
| + ClassElement classElement = element;
|
| + List<InterfaceType> interfaces = classElement.interfaces;
|
| + List<TypeParameterElement> typeParameters = classElement.typeParameters;
|
| + List<DartType> parameterTypes = classElement.type.typeArguments;
|
| + if (typeParameters.length == 0) {
|
| + return interfaces;
|
| + }
|
| + int count = interfaces.length;
|
| + List<InterfaceType> typedInterfaces = new List<InterfaceType>(count);
|
| + for (int i = 0; i < count; i++) {
|
| + typedInterfaces[i] =
|
| + interfaces[i].substitute2(typeArguments, parameterTypes);
|
| + }
|
| + return typedInterfaces;
|
| + }
|
| +
|
| + @override
|
| + bool get isDartAsyncFuture {
|
| + ClassElement element = this.element;
|
| + if (element == null) {
|
| + return false;
|
| + }
|
| + return element.name == "Future" && element.library.isDartAsync;
|
| + }
|
| +
|
| + @override
|
| + bool get isDartCoreFunction {
|
| + ClassElement element = this.element;
|
| + if (element == null) {
|
| + return false;
|
| + }
|
| + return element.name == "Function" && element.library.isDartCore;
|
| + }
|
| +
|
| + @override
|
| + bool get isObject => element.supertype == null;
|
| +
|
| + @override
|
| + List<MethodElement> get methods {
|
| + _flushCachedMembersIfStale();
|
| + if (_methods == null) {
|
| + List<MethodElement> methods = element.methods;
|
| + List<MethodElement> members = new List<MethodElement>(methods.length);
|
| + for (int i = 0; i < methods.length; i++) {
|
| + members[i] = MethodMember.from(methods[i], this);
|
| + }
|
| + _methods = members;
|
| + }
|
| + return _methods;
|
| + }
|
| +
|
| + @override
|
| + List<InterfaceType> get mixins {
|
| + ClassElement classElement = element;
|
| + List<InterfaceType> mixins = classElement.mixins;
|
| + List<TypeParameterElement> typeParameters = classElement.typeParameters;
|
| + List<DartType> parameterTypes = classElement.type.typeArguments;
|
| + if (typeParameters.length == 0) {
|
| + return mixins;
|
| + }
|
| + int count = mixins.length;
|
| + List<InterfaceType> typedMixins = new List<InterfaceType>(count);
|
| + for (int i = 0; i < count; i++) {
|
| + typedMixins[i] = mixins[i].substitute2(typeArguments, parameterTypes);
|
| + }
|
| + return typedMixins;
|
| + }
|
| +
|
| + @override
|
| + InterfaceType get superclass {
|
| + ClassElement classElement = element;
|
| + InterfaceType supertype = classElement.supertype;
|
| + if (supertype == null) {
|
| + return null;
|
| + }
|
| + List<DartType> typeParameters = classElement.type.typeArguments;
|
| + if (typeArguments.length == 0 ||
|
| + typeArguments.length != typeParameters.length) {
|
| + return supertype;
|
| + }
|
| + return supertype.substitute2(typeArguments, typeParameters);
|
| + }
|
| +
|
| + @override
|
| + List<DartType> get typeArguments {
|
| + if (_typeArguments == null) {
|
| + _typeArguments = _typeArgumentsComputer();
|
| + _typeArgumentsComputer = null;
|
| + }
|
| + return _typeArguments;
|
| + }
|
| +
|
| + /**
|
| + * Set [typeArguments].
|
| + */
|
| + void set typeArguments(List<DartType> typeArguments) {
|
| + _typeArguments = typeArguments;
|
| + _typeArgumentsComputer = null;
|
| + }
|
| +
|
| + @override
|
| + List<TypeParameterElement> get typeParameters => element.typeParameters;
|
| +
|
| + @override
|
| + bool operator ==(Object object) {
|
| + if (identical(object, this)) {
|
| + return true;
|
| + }
|
| + if (object is InterfaceTypeImpl) {
|
| + return (element == object.element) &&
|
| + TypeImpl.equalArrays(typeArguments, object.typeArguments);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + @override
|
| + void appendTo(StringBuffer buffer) {
|
| + buffer.write(name);
|
| + int argumentCount = typeArguments.length;
|
| + if (argumentCount > 0) {
|
| + buffer.write("<");
|
| + for (int i = 0; i < argumentCount; i++) {
|
| + if (i > 0) {
|
| + buffer.write(", ");
|
| + }
|
| + (typeArguments[i] as TypeImpl).appendTo(buffer);
|
| + }
|
| + buffer.write(">");
|
| + }
|
| + }
|
| +
|
| + @override
|
| + DartType flattenFutures(TypeSystem typeSystem) {
|
| + // Implement the case: "If T = Future<S> then flatten(T) = flatten(S)."
|
| + if (isDartAsyncFuture && typeArguments.isNotEmpty) {
|
| + return typeArguments[0].flattenFutures(typeSystem);
|
| + }
|
| +
|
| + // Implement the case: "Otherwise if T <: Future then let S be a type
|
| + // such that T << Future<S> and for all R, if T << Future<R> then S << R.
|
| + // Then flatten(T) = S."
|
| + //
|
| + // In other words, given the set of all types R such that T << Future<R>,
|
| + // let S be the most specific of those types, if any such S exists.
|
| + //
|
| + // Since we only care about the most specific type, it is sufficient to
|
| + // look at the types appearing as a parameter to Future in the type
|
| + // hierarchy of T. We don't need to consider the supertypes of those
|
| + // types, since they are by definition less specific.
|
| + List<DartType> candidateTypes =
|
| + _searchTypeHierarchyForFutureTypeParameters();
|
| + DartType flattenResult = findMostSpecificType(candidateTypes, typeSystem);
|
| + if (flattenResult != null) {
|
| + return flattenResult;
|
| + }
|
| +
|
| + // Implement the case: "In any other circumstance, flatten(T) = T."
|
| + return this;
|
| + }
|
| +
|
| + @override
|
| + PropertyAccessorElement getGetter(String getterName) =>
|
| + PropertyAccessorMember.from(element.getGetter(getterName), this);
|
| +
|
| + @override
|
| + MethodElement getMethod(String methodName) =>
|
| + MethodMember.from(element.getMethod(methodName), this);
|
| +
|
| + @override
|
| + PropertyAccessorElement getSetter(String setterName) =>
|
| + PropertyAccessorMember.from(element.getSetter(setterName), this);
|
| +
|
| + @override
|
| + InterfaceTypeImpl instantiate(List<DartType> argumentTypes) =>
|
| + substitute2(argumentTypes, typeArguments);
|
| +
|
| + @override
|
| + bool isDirectSupertypeOf(InterfaceType type) {
|
| + InterfaceType i = this;
|
| + InterfaceType j = type;
|
| + ClassElement jElement = j.element;
|
| + InterfaceType supertype = jElement.supertype;
|
| + //
|
| + // If J has no direct supertype then it is Object, and Object has no direct
|
| + // supertypes.
|
| + //
|
| + if (supertype == null) {
|
| + return false;
|
| + }
|
| + //
|
| + // I is listed in the extends clause of J.
|
| + //
|
| + List<DartType> jArgs = j.typeArguments;
|
| + List<DartType> jVars = jElement.type.typeArguments;
|
| + supertype = supertype.substitute2(jArgs, jVars);
|
| + if (supertype == i) {
|
| + return true;
|
| + }
|
| + //
|
| + // I is listed in the implements clause of J.
|
| + //
|
| + for (InterfaceType interfaceType in jElement.interfaces) {
|
| + interfaceType = interfaceType.substitute2(jArgs, jVars);
|
| + if (interfaceType == i) {
|
| + return true;
|
| + }
|
| + }
|
| + //
|
| + // I is listed in the with clause of J.
|
| + //
|
| + for (InterfaceType mixinType in jElement.mixins) {
|
| + mixinType = mixinType.substitute2(jArgs, jVars);
|
| + if (mixinType == i) {
|
| + return true;
|
| + }
|
| + }
|
| + //
|
| + // J is a mixin application of the mixin of I.
|
| + //
|
| + // TODO(brianwilkerson) Determine whether this needs to be implemented or
|
| + // whether it is covered by the case above.
|
| + return false;
|
| + }
|
| +
|
| + @override
|
| + bool isMoreSpecificThan(DartType type,
|
| + [bool withDynamic = false, Set<Element> visitedElements]) {
|
| + //
|
| + // S is dynamic.
|
| + // The test to determine whether S is dynamic is done here because dynamic
|
| + // is not an instance of InterfaceType.
|
| + //
|
| + if (type.isDynamic) {
|
| + return true;
|
| + }
|
| + //
|
| + // A type T is more specific than a type S, written T << S,
|
| + // if one of the following conditions is met:
|
| + //
|
| + // Reflexivity: T is S.
|
| + //
|
| + if (this == type) {
|
| + return true;
|
| + }
|
| + if (type is InterfaceType) {
|
| + //
|
| + // T is bottom. (This case is handled by the class BottomTypeImpl.)
|
| + //
|
| + // Direct supertype: S is a direct supertype of T.
|
| + //
|
| + if (type.isDirectSupertypeOf(this)) {
|
| + return true;
|
| + }
|
| + //
|
| + // Covariance: T is of the form I<T1, ..., Tn> and S is of the form
|
| + // I<S1, ..., Sn> and Ti << Si, 1 <= i <= n.
|
| + //
|
| + ClassElement tElement = this.element;
|
| + ClassElement sElement = type.element;
|
| + if (tElement == sElement) {
|
| + List<DartType> tArguments = typeArguments;
|
| + List<DartType> sArguments = type.typeArguments;
|
| + if (tArguments.length != sArguments.length) {
|
| + return false;
|
| + }
|
| + for (int i = 0; i < tArguments.length; i++) {
|
| + if (!(tArguments[i] as TypeImpl)
|
| + .isMoreSpecificThan(sArguments[i], withDynamic)) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| + }
|
| + //
|
| + // Transitivity: T << U and U << S.
|
| + //
|
| + // First check for infinite loops
|
| + if (element == null) {
|
| + return false;
|
| + }
|
| + if (visitedElements == null) {
|
| + visitedElements = new HashSet<ClassElement>();
|
| + } else if (visitedElements.contains(element)) {
|
| + return false;
|
| + }
|
| + visitedElements.add(element);
|
| + try {
|
| + // Iterate over all of the types U that are more specific than T because
|
| + // they are direct supertypes of T and return true if any of them are more
|
| + // specific than S.
|
| + InterfaceTypeImpl supertype = superclass;
|
| + if (supertype != null &&
|
| + supertype.isMoreSpecificThan(type, withDynamic, visitedElements)) {
|
| + return true;
|
| + }
|
| + for (InterfaceType interfaceType in interfaces) {
|
| + if ((interfaceType as InterfaceTypeImpl)
|
| + .isMoreSpecificThan(type, withDynamic, visitedElements)) {
|
| + return true;
|
| + }
|
| + }
|
| + for (InterfaceType mixinType in mixins) {
|
| + if ((mixinType as InterfaceTypeImpl)
|
| + .isMoreSpecificThan(type, withDynamic, visitedElements)) {
|
| + return true;
|
| + }
|
| + }
|
| + // If a type I includes an instance method named `call`, and the type of
|
| + // `call` is the function type F, then I is considered to be more specific
|
| + // than F.
|
| + MethodElement callMethod = getMethod('call');
|
| + if (callMethod != null && !callMethod.isStatic) {
|
| + FunctionTypeImpl callType = callMethod.type;
|
| + if (callType.isMoreSpecificThan(type, withDynamic, visitedElements)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + } finally {
|
| + visitedElements.remove(element);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + ConstructorElement lookUpConstructor(
|
| + String constructorName, LibraryElement library) {
|
| + // prepare base ConstructorElement
|
| + ConstructorElement constructorElement;
|
| + if (constructorName == null) {
|
| + constructorElement = element.unnamedConstructor;
|
| + } else {
|
| + constructorElement = element.getNamedConstructor(constructorName);
|
| + }
|
| + // not found or not accessible
|
| + if (constructorElement == null ||
|
| + !constructorElement.isAccessibleIn(library)) {
|
| + return null;
|
| + }
|
| + // return member
|
| + return ConstructorMember.from(constructorElement, this);
|
| + }
|
| +
|
| + @override
|
| + PropertyAccessorElement lookUpGetter(
|
| + String getterName, LibraryElement library) {
|
| + PropertyAccessorElement element = getGetter(getterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + return lookUpGetterInSuperclass(getterName, library);
|
| + }
|
| +
|
| + @override
|
| + PropertyAccessorElement lookUpGetterInSuperclass(
|
| + String getterName, LibraryElement library) {
|
| + for (InterfaceType mixin in mixins.reversed) {
|
| + PropertyAccessorElement element = mixin.getGetter(getterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + }
|
| + HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
|
| + InterfaceType supertype = superclass;
|
| + ClassElement supertypeElement = supertype?.element;
|
| + while (supertype != null && !visitedClasses.contains(supertypeElement)) {
|
| + visitedClasses.add(supertypeElement);
|
| + PropertyAccessorElement element = supertype.getGetter(getterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + for (InterfaceType mixin in supertype.mixins.reversed) {
|
| + element = mixin.getGetter(getterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + }
|
| + supertype = supertype.superclass;
|
| + supertypeElement = supertype?.element;
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + @override
|
| + PropertyAccessorElement lookUpInheritedGetter(String name,
|
| + {LibraryElement library, bool thisType: true}) {
|
| + PropertyAccessorElement result;
|
| + if (thisType) {
|
| + result = lookUpGetter(name, library);
|
| + } else {
|
| + result = lookUpGetterInSuperclass(name, library);
|
| + }
|
| + if (result != null) {
|
| + return result;
|
| + }
|
| + return _lookUpMemberInInterfaces(this, false, library,
|
| + new HashSet<ClassElement>(), (InterfaceType t) => t.getGetter(name));
|
| + }
|
| +
|
| + @override
|
| + ExecutableElement lookUpInheritedGetterOrMethod(String name,
|
| + {LibraryElement library}) {
|
| + ExecutableElement result =
|
| + lookUpGetter(name, library) ?? lookUpMethod(name, library);
|
| +
|
| + if (result != null) {
|
| + return result;
|
| + }
|
| + return _lookUpMemberInInterfaces(
|
| + this,
|
| + false,
|
| + library,
|
| + new HashSet<ClassElement>(),
|
| + (InterfaceType t) => t.getGetter(name) ?? t.getMethod(name));
|
| + }
|
| +
|
| + @override
|
| + MethodElement lookUpInheritedMethod(String name,
|
| + {LibraryElement library, bool thisType: true}) {
|
| + MethodElement result;
|
| + if (thisType) {
|
| + result = lookUpMethod(name, library);
|
| + } else {
|
| + result = lookUpMethodInSuperclass(name, library);
|
| + }
|
| + if (result != null) {
|
| + return result;
|
| + }
|
| + return _lookUpMemberInInterfaces(this, false, library,
|
| + new HashSet<ClassElement>(), (InterfaceType t) => t.getMethod(name));
|
| + }
|
| +
|
| + @override
|
| + PropertyAccessorElement lookUpInheritedSetter(String name,
|
| + {LibraryElement library, bool thisType: true}) {
|
| + PropertyAccessorElement result;
|
| + if (thisType) {
|
| + result = lookUpSetter(name, library);
|
| + } else {
|
| + result = lookUpSetterInSuperclass(name, library);
|
| + }
|
| + if (result != null) {
|
| + return result;
|
| + }
|
| + return _lookUpMemberInInterfaces(this, false, library,
|
| + new HashSet<ClassElement>(), (t) => t.getSetter(name));
|
| + }
|
| +
|
| + @override
|
| + MethodElement lookUpMethod(String methodName, LibraryElement library) {
|
| + MethodElement element = getMethod(methodName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + return lookUpMethodInSuperclass(methodName, library);
|
| + }
|
| +
|
| + @override
|
| + MethodElement lookUpMethodInSuperclass(
|
| + String methodName, LibraryElement library) {
|
| + for (InterfaceType mixin in mixins.reversed) {
|
| + MethodElement element = mixin.getMethod(methodName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + }
|
| + HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
|
| + InterfaceType supertype = superclass;
|
| + ClassElement supertypeElement = supertype?.element;
|
| + while (supertype != null && !visitedClasses.contains(supertypeElement)) {
|
| + visitedClasses.add(supertypeElement);
|
| + MethodElement element = supertype.getMethod(methodName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + for (InterfaceType mixin in supertype.mixins.reversed) {
|
| + element = mixin.getMethod(methodName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + }
|
| + supertype = supertype.superclass;
|
| + supertypeElement = supertype?.element;
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + @override
|
| + PropertyAccessorElement lookUpSetter(
|
| + String setterName, LibraryElement library) {
|
| + PropertyAccessorElement element = getSetter(setterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + return lookUpSetterInSuperclass(setterName, library);
|
| + }
|
| +
|
| + @override
|
| + PropertyAccessorElement lookUpSetterInSuperclass(
|
| + String setterName, LibraryElement library) {
|
| + for (InterfaceType mixin in mixins.reversed) {
|
| + PropertyAccessorElement element = mixin.getSetter(setterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + }
|
| + HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
|
| + InterfaceType supertype = superclass;
|
| + ClassElement supertypeElement = supertype?.element;
|
| + while (supertype != null && !visitedClasses.contains(supertypeElement)) {
|
| + visitedClasses.add(supertypeElement);
|
| + PropertyAccessorElement element = supertype.getSetter(setterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + for (InterfaceType mixin in supertype.mixins.reversed) {
|
| + element = mixin.getSetter(setterName);
|
| + if (element != null && element.isAccessibleIn(library)) {
|
| + return element;
|
| + }
|
| + }
|
| + supertype = supertype.superclass;
|
| + supertypeElement = supertype?.element;
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + @override
|
| + InterfaceTypeImpl pruned(List<FunctionTypeAliasElement> prune) {
|
| + if (prune == null) {
|
| + return this;
|
| + } else {
|
| + // There should never be a reason to prune a type that has already been
|
| + // pruned, since pruning is only done when expanding a function type
|
| + // alias, and function type aliases are always expanded by starting with
|
| + // base types.
|
| + assert(this.prunedTypedefs == null);
|
| + InterfaceTypeImpl result = new InterfaceTypeImpl._(element, name, prune);
|
| + result.typeArguments = typeArguments
|
| + .map((DartType t) => (t as TypeImpl).pruned(prune))
|
| + .toList();
|
| + return result;
|
| + }
|
| + }
|
| +
|
| + @override
|
| + InterfaceTypeImpl substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) {
|
| + if (argumentTypes.length != parameterTypes.length) {
|
| + throw new ArgumentError(
|
| + "argumentTypes.length (${argumentTypes.length}) != parameterTypes.length (${parameterTypes.length})");
|
| + }
|
| + if (argumentTypes.length == 0 || typeArguments.length == 0) {
|
| + return this.pruned(prune);
|
| + }
|
| + List<DartType> newTypeArguments = TypeImpl.substitute(
|
| + typeArguments, argumentTypes, parameterTypes, prune);
|
| + if (listsEqual(newTypeArguments, typeArguments)) {
|
| + return this;
|
| + }
|
| +
|
| + if (isDartAsyncFuture && newTypeArguments.isNotEmpty) {
|
| + //
|
| + // In strong mode interpret Future< T > as Future< flatten(T) >
|
| + //
|
| + // For example, Future<Future<T>> will flatten to Future<T>.
|
| + //
|
| + // In the Dart 3rd edition spec, this flatten operation is used for
|
| + // `async` and `await`. In strong mode, we extend it to all Future<T>
|
| + // instantiations. This allows typing of Future-related operations
|
| + // in dart:async in a way that matches their runtime behavior and provides
|
| + // precise return types for users of these APIs.
|
| + //
|
| + // For example:
|
| + //
|
| + // abstract class Future<T> {
|
| + // Future<S> then<S>(S onValue(T value), ...);
|
| + // }
|
| + //
|
| + // Given a call where S <: Future<R> for some R, we will need to flatten
|
| + // the return type so it is Future< flatten(S) >, yielding Future<R>.
|
| + //
|
| + if (element.library.context.analysisOptions.strongMode) {
|
| + TypeImpl t = newTypeArguments[0];
|
| + newTypeArguments[0] = t.flattenFutures(new StrongTypeSystemImpl());
|
| + }
|
| + }
|
| +
|
| + InterfaceTypeImpl newType = new InterfaceTypeImpl(element, prune);
|
| + newType.typeArguments = newTypeArguments;
|
| + return newType;
|
| + }
|
| +
|
| + @deprecated
|
| + @override
|
| + InterfaceTypeImpl substitute4(List<DartType> argumentTypes) =>
|
| + instantiate(argumentTypes);
|
| +
|
| + /**
|
| + * Flush cache members if the version of [element] for which members are
|
| + * cached and the current version of the [element].
|
| + */
|
| + void _flushCachedMembersIfStale() {
|
| + ClassElement element = this.element;
|
| + if (element is ClassElementImpl) {
|
| + int currentVersion = element.version;
|
| + if (_versionOfCachedMembers != currentVersion) {
|
| + _constructors = null;
|
| + _accessors = null;
|
| + _methods = null;
|
| + }
|
| + _versionOfCachedMembers = currentVersion;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Starting from this type, search its class hierarchy for types of the form
|
| + * Future<R>, and return a list of the resulting R's.
|
| + */
|
| + List<DartType> _searchTypeHierarchyForFutureTypeParameters() {
|
| + List<DartType> result = <DartType>[];
|
| + HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
|
| + void recurse(InterfaceTypeImpl type) {
|
| + if (type.isDartAsyncFuture && type.typeArguments.isNotEmpty) {
|
| + result.add(type.typeArguments[0]);
|
| + }
|
| + if (visitedClasses.add(type.element)) {
|
| + if (type.superclass != null) {
|
| + recurse(type.superclass);
|
| + }
|
| + for (InterfaceType interface in type.interfaces) {
|
| + recurse(interface);
|
| + }
|
| + visitedClasses.remove(type.element);
|
| + }
|
| + }
|
| +
|
| + recurse(this);
|
| + return result;
|
| + }
|
| +
|
| + /**
|
| + * Compute the least upper bound of types [i] and [j], both of which are
|
| + * known to be interface types.
|
| + *
|
| + * In the event that the algorithm fails (which might occur due to a bug in
|
| + * the analyzer), `null` is returned.
|
| + */
|
| + static InterfaceType computeLeastUpperBound(
|
| + InterfaceType i, InterfaceType j) {
|
| + // compute set of supertypes
|
| + Set<InterfaceType> si = computeSuperinterfaceSet(i);
|
| + Set<InterfaceType> sj = computeSuperinterfaceSet(j);
|
| + // union si with i and sj with j
|
| + si.add(i);
|
| + sj.add(j);
|
| + // compute intersection, reference as set 's'
|
| + List<InterfaceType> s = _intersection(si, sj);
|
| + return computeTypeAtMaxUniqueDepth(s);
|
| + }
|
| +
|
| + /**
|
| + * Return the length of the longest inheritance path from the given [type] to
|
| + * Object.
|
| + *
|
| + * See [computeLeastUpperBound].
|
| + */
|
| + static int computeLongestInheritancePathToObject(InterfaceType type) =>
|
| + _computeLongestInheritancePathToObject(
|
| + type, 0, new HashSet<ClassElement>());
|
| +
|
| + /**
|
| + * Returns the set of all superinterfaces of the given [type].
|
| + *
|
| + * See [computeLeastUpperBound].
|
| + */
|
| + static Set<InterfaceType> computeSuperinterfaceSet(InterfaceType type) =>
|
| + _computeSuperinterfaceSet(type, new HashSet<InterfaceType>());
|
| +
|
| + /**
|
| + * Return the type from the [types] list that has the longest inheritence path
|
| + * to Object of unique length.
|
| + */
|
| + static InterfaceType computeTypeAtMaxUniqueDepth(List<InterfaceType> types) {
|
| + // for each element in Set s, compute the largest inheritance path to Object
|
| + List<int> depths = new List<int>.filled(types.length, 0);
|
| + int maxDepth = 0;
|
| + for (int n = 0; n < types.length; n++) {
|
| + depths[n] = computeLongestInheritancePathToObject(types[n]);
|
| + if (depths[n] > maxDepth) {
|
| + maxDepth = depths[n];
|
| + }
|
| + }
|
| + // ensure that the currently computed maxDepth is unique,
|
| + // otherwise, decrement and test for uniqueness again
|
| + for (; maxDepth >= 0; maxDepth--) {
|
| + int indexOfLeastUpperBound = -1;
|
| + int numberOfTypesAtMaxDepth = 0;
|
| + for (int m = 0; m < depths.length; m++) {
|
| + if (depths[m] == maxDepth) {
|
| + numberOfTypesAtMaxDepth++;
|
| + indexOfLeastUpperBound = m;
|
| + }
|
| + }
|
| + if (numberOfTypesAtMaxDepth == 1) {
|
| + return types[indexOfLeastUpperBound];
|
| + }
|
| + }
|
| + // Should be impossible--there should always be exactly one type with the
|
| + // maximum depth.
|
| + assert(false);
|
| + return null;
|
| + }
|
| +
|
| + /**
|
| + * If there is a single type which is at least as specific as all of the
|
| + * types in [types], return it. Otherwise return `null`.
|
| + */
|
| + static DartType findMostSpecificType(
|
| + List<DartType> types, TypeSystem typeSystem) {
|
| + // The << relation ("more specific than") is a partial ordering on types,
|
| + // so to find the most specific type of a set, we keep a bucket of the most
|
| + // specific types seen so far such that no type in the bucket is more
|
| + // specific than any other type in the bucket.
|
| + List<DartType> bucket = <DartType>[];
|
| +
|
| + // Then we consider each type in turn.
|
| + for (DartType type in types) {
|
| + // If any existing type in the bucket is more specific than this type,
|
| + // then we can ignore this type.
|
| + if (bucket.any((DartType t) => typeSystem.isMoreSpecificThan(t, type))) {
|
| + continue;
|
| + }
|
| + // Otherwise, we need to add this type to the bucket and remove any types
|
| + // that are less specific than it.
|
| + bool added = false;
|
| + int i = 0;
|
| + while (i < bucket.length) {
|
| + if (typeSystem.isMoreSpecificThan(type, bucket[i])) {
|
| + if (added) {
|
| + if (i < bucket.length - 1) {
|
| + bucket[i] = bucket.removeLast();
|
| + } else {
|
| + bucket.removeLast();
|
| + }
|
| + } else {
|
| + bucket[i] = type;
|
| + i++;
|
| + added = true;
|
| + }
|
| + } else {
|
| + i++;
|
| + }
|
| + }
|
| + if (!added) {
|
| + bucket.add(type);
|
| + }
|
| + }
|
| +
|
| + // Now that we are finished, if there is exactly one type left in the
|
| + // bucket, it is the most specific type.
|
| + if (bucket.length == 1) {
|
| + return bucket[0];
|
| + }
|
| +
|
| + // Otherwise, there is no single type that is more specific than the
|
| + // others.
|
| + return null;
|
| + }
|
| +
|
| + /**
|
| + * Returns a "smart" version of the "least upper bound" of the given types.
|
| + *
|
| + * If these types have the same element and differ only in terms of the type
|
| + * arguments, attempts to find a compatible set of type arguments.
|
| + *
|
| + * Otherwise, calls [DartType.getLeastUpperBound].
|
| + */
|
| + static InterfaceType getSmartLeastUpperBound(
|
| + InterfaceType first, InterfaceType second) {
|
| + // TODO(paulberry): this needs to be deprecated and replaced with a method
|
| + // in [TypeSystem], since it relies on the deprecated functionality of
|
| + // [DartType.getLeastUpperBound].
|
| + if (first.element == second.element) {
|
| + return _leastUpperBound(first, second);
|
| + }
|
| + AnalysisContext context = first.element.context;
|
| + return context.typeSystem
|
| + .getLeastUpperBound(context.typeProvider, first, second);
|
| + }
|
| +
|
| + /**
|
| + * Return the length of the longest inheritance path from a subtype of the
|
| + * given [type] to Object, where the given [depth] is the length of the
|
| + * longest path from the subtype to this type. The set of [visitedTypes] is
|
| + * used to prevent infinite recursion in the case of a cyclic type structure.
|
| + *
|
| + * See [computeLongestInheritancePathToObject], and [computeLeastUpperBound].
|
| + */
|
| + static int _computeLongestInheritancePathToObject(
|
| + InterfaceType type, int depth, HashSet<ClassElement> visitedTypes) {
|
| + ClassElement classElement = type.element;
|
| + // Object case
|
| + if (classElement.supertype == null || visitedTypes.contains(classElement)) {
|
| + return depth;
|
| + }
|
| + int longestPath = 1;
|
| + try {
|
| + visitedTypes.add(classElement);
|
| + List<InterfaceType> superinterfaces = classElement.interfaces;
|
| + int pathLength;
|
| + if (superinterfaces.length > 0) {
|
| + // loop through each of the superinterfaces recursively calling this
|
| + // method and keeping track of the longest path to return
|
| + for (InterfaceType superinterface in superinterfaces) {
|
| + pathLength = _computeLongestInheritancePathToObject(
|
| + superinterface, depth + 1, visitedTypes);
|
| + if (pathLength > longestPath) {
|
| + longestPath = pathLength;
|
| + }
|
| + }
|
| + }
|
| + // finally, perform this same check on the super type
|
| + // TODO(brianwilkerson) Does this also need to add in the number of mixin
|
| + // classes?
|
| + InterfaceType supertype = classElement.supertype;
|
| + pathLength = _computeLongestInheritancePathToObject(
|
| + supertype, depth + 1, visitedTypes);
|
| + if (pathLength > longestPath) {
|
| + longestPath = pathLength;
|
| + }
|
| + } finally {
|
| + visitedTypes.remove(classElement);
|
| + }
|
| + return longestPath;
|
| + }
|
| +
|
| + /**
|
| + * Add all of the superinterfaces of the given [type] to the given [set].
|
| + * Return the [set] as a convenience.
|
| + *
|
| + * See [computeSuperinterfaceSet], and [computeLeastUpperBound].
|
| + */
|
| + static Set<InterfaceType> _computeSuperinterfaceSet(
|
| + InterfaceType type, HashSet<InterfaceType> set) {
|
| + Element element = type.element;
|
| + if (element != null) {
|
| + List<InterfaceType> superinterfaces = type.interfaces;
|
| + for (InterfaceType superinterface in superinterfaces) {
|
| + if (set.add(superinterface)) {
|
| + _computeSuperinterfaceSet(superinterface, set);
|
| + }
|
| + }
|
| + InterfaceType supertype = type.superclass;
|
| + if (supertype != null) {
|
| + if (set.add(supertype)) {
|
| + _computeSuperinterfaceSet(supertype, set);
|
| + }
|
| + }
|
| + }
|
| + return set;
|
| + }
|
| +
|
| + /**
|
| + * Return the intersection of the [first] and [second] sets of types, where
|
| + * intersection is based on the equality of the types themselves.
|
| + */
|
| + static List<InterfaceType> _intersection(
|
| + Set<InterfaceType> first, Set<InterfaceType> second) {
|
| + Set<InterfaceType> result = new HashSet<InterfaceType>.from(first);
|
| + result.retainAll(second);
|
| + return new List.from(result);
|
| + }
|
| +
|
| + /**
|
| + * Return the "least upper bound" of the given types under the assumption that
|
| + * the types have the same element and differ only in terms of the type
|
| + * arguments.
|
| + *
|
| + * The resulting type is composed by comparing the corresponding type
|
| + * arguments, keeping those that are the same, and using 'dynamic' for those
|
| + * that are different.
|
| + */
|
| + static InterfaceType _leastUpperBound(
|
| + InterfaceType firstType, InterfaceType secondType) {
|
| + ClassElement firstElement = firstType.element;
|
| + ClassElement secondElement = secondType.element;
|
| + if (firstElement != secondElement) {
|
| + throw new ArgumentError('The same elements expected, but '
|
| + '$firstElement and $secondElement are given.');
|
| + }
|
| + if (firstType == secondType) {
|
| + return firstType;
|
| + }
|
| + List<DartType> firstArguments = firstType.typeArguments;
|
| + List<DartType> secondArguments = secondType.typeArguments;
|
| + int argumentCount = firstArguments.length;
|
| + if (argumentCount == 0) {
|
| + return firstType;
|
| + }
|
| + List<DartType> lubArguments = new List<DartType>(argumentCount);
|
| + for (int i = 0; i < argumentCount; i++) {
|
| + //
|
| + // Ideally we would take the least upper bound of the two argument types,
|
| + // but this can cause an infinite recursion (such as when finding the
|
| + // least upper bound of String and num).
|
| + //
|
| + if (firstArguments[i] == secondArguments[i]) {
|
| + lubArguments[i] = firstArguments[i];
|
| + }
|
| + if (lubArguments[i] == null) {
|
| + lubArguments[i] = DynamicTypeImpl.instance;
|
| + }
|
| + }
|
| + InterfaceTypeImpl lub = new InterfaceTypeImpl(firstElement);
|
| + lub.typeArguments = lubArguments;
|
| + return lub;
|
| + }
|
| +
|
| + /**
|
| + * Look up the getter with the given [name] in the interfaces
|
| + * implemented by the given [targetType], either directly or indirectly.
|
| + * Return the element representing the getter that was found, or `null` if
|
| + * there is no getter with the given name. The flag [includeTargetType] should
|
| + * be `true` if the search should include the target type. The
|
| + * [visitedInterfaces] is a set containing all of the interfaces that have
|
| + * been examined, used to prevent infinite recursion and to optimize the
|
| + * search.
|
| + */
|
| + static ExecutableElement _lookUpMemberInInterfaces(
|
| + InterfaceType targetType,
|
| + bool includeTargetType,
|
| + LibraryElement library,
|
| + HashSet<ClassElement> visitedInterfaces,
|
| + ExecutableElement getMember(InterfaceType type)) {
|
| + // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the
|
| + // specification (titled "Inheritance and Overriding" under "Interfaces")
|
| + // describes a much more complex scheme for finding the inherited member.
|
| + // We need to follow that scheme. The code below should cover the 80% case.
|
| + ClassElement targetClass = targetType.element;
|
| + if (!visitedInterfaces.add(targetClass)) {
|
| + return null;
|
| + }
|
| + if (includeTargetType) {
|
| + ExecutableElement member = getMember(targetType);
|
| + if (member != null && member.isAccessibleIn(library)) {
|
| + return member;
|
| + }
|
| + }
|
| + for (InterfaceType interfaceType in targetType.interfaces) {
|
| + ExecutableElement member = _lookUpMemberInInterfaces(
|
| + interfaceType, true, library, visitedInterfaces, getMember);
|
| + if (member != null) {
|
| + return member;
|
| + }
|
| + }
|
| + for (InterfaceType mixinType in targetType.mixins.reversed) {
|
| + ExecutableElement member = _lookUpMemberInInterfaces(
|
| + mixinType, true, library, visitedInterfaces, getMember);
|
| + if (member != null) {
|
| + return member;
|
| + }
|
| + }
|
| + InterfaceType superclass = targetType.superclass;
|
| + if (superclass == null) {
|
| + return null;
|
| + }
|
| + return _lookUpMemberInInterfaces(
|
| + superclass, true, library, visitedInterfaces, getMember);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * The abstract class `TypeImpl` implements the behavior common to objects
|
| + * representing the declared type of elements in the element model.
|
| + */
|
| +abstract class TypeImpl implements DartType {
|
| + /**
|
| + * The element representing the declaration of this type, or `null` if the
|
| + * type has not, or cannot, be associated with an element.
|
| + */
|
| + final Element _element;
|
| +
|
| + /**
|
| + * The name of this type, or `null` if the type does not have a name.
|
| + */
|
| + final String name;
|
| +
|
| + /**
|
| + * Initialize a newly created type to be declared by the given [element] and
|
| + * to have the given [name].
|
| + */
|
| + TypeImpl(this._element, this.name);
|
| +
|
| + @override
|
| + String get displayName => name;
|
| +
|
| + @override
|
| + Element get element => _element;
|
| +
|
| + @override
|
| + bool get isBottom => false;
|
| +
|
| + @override
|
| + bool get isDartAsyncFuture => false;
|
| +
|
| + @override
|
| + bool get isDartCoreFunction => false;
|
| +
|
| + @override
|
| + bool get isDynamic => false;
|
| +
|
| + @override
|
| + bool get isObject => false;
|
| +
|
| + @override
|
| + bool get isUndefined => false;
|
| +
|
| + @override
|
| + bool get isVoid => false;
|
| +
|
| + /**
|
| + * Append a textual representation of this type to the given [buffer]. The set
|
| + * of [visitedTypes] is used to prevent infinite recursion.
|
| + */
|
| + void appendTo(StringBuffer buffer) {
|
| + if (name == null) {
|
| + buffer.write("<unnamed type>");
|
| + } else {
|
| + buffer.write(name);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + DartType flattenFutures(TypeSystem typeSystem) => this;
|
| +
|
| + /**
|
| + * Return `true` if this type is assignable to the given [type] (written in
|
| + * the spec as "T <=> S", where T=[this] and S=[type]).
|
| + *
|
| + * The sets [thisExpansions] and [typeExpansions], if given, are the sets of
|
| + * function type aliases that have been expanded so far in the process of
|
| + * reaching [this] and [type], respectively. These are used to avoid
|
| + * infinite regress when analyzing invalid code; since the language spec
|
| + * forbids a typedef from referring to itself directly or indirectly, we can
|
| + * use these as sets of function type aliases that don't need to be expanded.
|
| + */
|
| + @override
|
| + bool isAssignableTo(DartType type) {
|
| + // An interface type T may be assigned to a type S, written T <=> S, iff
|
| + // either T <: S or S <: T.
|
| + return isSubtypeOf(type) || type.isSubtypeOf(this);
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if this type is more specific than the given [type] (written
|
| + * in the spec as "T << S", where T=[this] and S=[type]).
|
| + *
|
| + * If [withDynamic] is `true`, then "dynamic" should be considered as a
|
| + * subtype of any type (as though "dynamic" had been replaced with bottom).
|
| + *
|
| + * The set [visitedElements], if given, is the set of classes and type
|
| + * parameters that have been visited so far while examining the class
|
| + * hierarchy of [this]. This is used to avoid infinite regress when
|
| + * analyzing invalid code; since the language spec forbids loops in the class
|
| + * hierarchy, we can use this as a set of classes that don't need to be
|
| + * examined when walking the class hierarchy.
|
| + */
|
| + @override
|
| + bool isMoreSpecificThan(DartType type,
|
| + [bool withDynamic = false, Set<Element> visitedElements]);
|
| +
|
| + /**
|
| + * Return `true` if this type is a subtype of the given [type] (written in
|
| + * the spec as "T <: S", where T=[this] and S=[type]).
|
| + *
|
| + * The sets [thisExpansions] and [typeExpansions], if given, are the sets of
|
| + * function type aliases that have been expanded so far in the process of
|
| + * reaching [this] and [type], respectively. These are used to avoid
|
| + * infinite regress when analyzing invalid code; since the language spec
|
| + * forbids a typedef from referring to itself directly or indirectly, we can
|
| + * use these as sets of function type aliases that don't need to be expanded.
|
| + */
|
| + @override
|
| + bool isSubtypeOf(DartType type) {
|
| + // For non-function types, T <: S iff [_|_/dynamic]T << S.
|
| + return isMoreSpecificThan(type, true);
|
| + }
|
| +
|
| + @override
|
| + bool isSupertypeOf(DartType type) => type.isSubtypeOf(this);
|
| +
|
| + /**
|
| + * Create a new [TypeImpl] that is identical to [this] except that when
|
| + * visiting type parameters, function parameter types, and function return
|
| + * types, function types listed in [prune] will not be expanded. This is
|
| + * used to avoid creating infinite types in the presence of circular
|
| + * typedefs.
|
| + *
|
| + * If [prune] is null, then [this] is returned unchanged.
|
| + *
|
| + * Only legal to call on a [TypeImpl] that is not already subject to pruning.
|
| + */
|
| + TypeImpl pruned(List<FunctionTypeAliasElement> prune);
|
| +
|
| + @override
|
| + DartType resolveToBound(DartType objectType) => this;
|
| +
|
| + /**
|
| + * Return the type resulting from substituting the given [argumentTypes] for
|
| + * the given [parameterTypes] in this type.
|
| + *
|
| + * In all classes derived from [TypeImpl], a new optional argument
|
| + * [prune] is added. If specified, it is a list of function typdefs
|
| + * which should not be expanded. This is used to avoid creating infinite
|
| + * types in response to self-referential typedefs.
|
| + */
|
| + @override
|
| + DartType substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]);
|
| +
|
| + @override
|
| + String toString() {
|
| + StringBuffer buffer = new StringBuffer();
|
| + appendTo(buffer);
|
| + return buffer.toString();
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if corresponding elements of the [first] and [second] lists
|
| + * of type arguments are all equal.
|
| + */
|
| + static bool equalArrays(List<DartType> first, List<DartType> second) {
|
| + if (first.length != second.length) {
|
| + return false;
|
| + }
|
| + for (int i = 0; i < first.length; i++) {
|
| + if (first[i] == null) {
|
| + AnalysisEngine.instance.logger
|
| + .logInformation('Found null type argument in TypeImpl.equalArrays');
|
| + return second[i] == null;
|
| + } else if (second[i] == null) {
|
| + AnalysisEngine.instance.logger
|
| + .logInformation('Found null type argument in TypeImpl.equalArrays');
|
| + return false;
|
| + }
|
| + if (first[i] != second[i]) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + /**
|
| + * Return a list containing the results of using the given [argumentTypes] and
|
| + * [parameterTypes] to perform a substitution on all of the given [types].
|
| + *
|
| + * If [prune] is specified, it is a list of function typdefs which should not
|
| + * be expanded. This is used to avoid creating infinite types in response to
|
| + * self-referential typedefs.
|
| + */
|
| + static List<DartType> substitute(List<DartType> types,
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) {
|
| + int length = types.length;
|
| + if (length == 0) {
|
| + return types;
|
| + }
|
| + List<DartType> newTypes = new List<DartType>(length);
|
| + for (int i = 0; i < length; i++) {
|
| + newTypes[i] = (types[i] as TypeImpl)
|
| + .substitute2(argumentTypes, parameterTypes, prune);
|
| + }
|
| + return newTypes;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * A concrete implementation of a [TypeParameterType].
|
| + */
|
| +class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
|
| + /**
|
| + * Initialize a newly created type parameter type to be declared by the given
|
| + * [element] and to have the given name.
|
| + */
|
| + TypeParameterTypeImpl(TypeParameterElement element)
|
| + : super(element, element.name);
|
| +
|
| + @override
|
| + TypeParameterElement get element => super.element as TypeParameterElement;
|
| +
|
| + @override
|
| + int get hashCode => element.hashCode;
|
| +
|
| + @override
|
| + bool operator ==(Object object) =>
|
| + object is TypeParameterTypeImpl && (element == object.element);
|
| +
|
| + @override
|
| + bool isMoreSpecificThan(DartType s,
|
| + [bool withDynamic = false, Set<Element> visitedElements]) {
|
| + //
|
| + // A type T is more specific than a type S, written T << S,
|
| + // if one of the following conditions is met:
|
| + //
|
| + // Reflexivity: T is S.
|
| + //
|
| + if (this == s) {
|
| + return true;
|
| + }
|
| + // S is dynamic.
|
| + //
|
| + if (s.isDynamic) {
|
| + return true;
|
| + }
|
| + //
|
| + // T is a type parameter and S is the upper bound of T.
|
| + //
|
| + TypeImpl bound = element.bound;
|
| + if (s == bound) {
|
| + return true;
|
| + }
|
| + //
|
| + // T is a type parameter and S is Object.
|
| + //
|
| + if (s.isObject) {
|
| + return true;
|
| + }
|
| + // We need upper bound to continue.
|
| + if (bound == null) {
|
| + return false;
|
| + }
|
| + //
|
| + // Transitivity: T << U and U << S.
|
| + //
|
| + // First check for infinite loops
|
| + if (element == null) {
|
| + return false;
|
| + }
|
| + if (visitedElements == null) {
|
| + visitedElements = new HashSet<Element>();
|
| + } else if (visitedElements.contains(element)) {
|
| + return false;
|
| + }
|
| + visitedElements.add(element);
|
| + try {
|
| + return bound.isMoreSpecificThan(s, withDynamic, visitedElements);
|
| + } finally {
|
| + visitedElements.remove(element);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + bool isSubtypeOf(DartType type) => isMoreSpecificThan(type, true);
|
| +
|
| + @override
|
| + TypeImpl pruned(List<FunctionTypeAliasElement> prune) => this;
|
| +
|
| + @override
|
| + DartType resolveToBound(DartType objectType) {
|
| + if (element.bound == null) {
|
| + return objectType;
|
| + }
|
| +
|
| + return element.bound.resolveToBound(objectType);
|
| + }
|
| +
|
| + @override
|
| + DartType substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) {
|
| + int length = parameterTypes.length;
|
| + for (int i = 0; i < length; i++) {
|
| + if (parameterTypes[i] == this) {
|
| + return argumentTypes[i];
|
| + }
|
| + }
|
| + return this;
|
| + }
|
| +
|
| + /**
|
| + * Return a list containing the type parameter types defined by the given
|
| + * array of type parameter elements ([typeParameters]).
|
| + */
|
| + static List<TypeParameterType> getTypes(
|
| + List<TypeParameterElement> typeParameters) {
|
| + int count = typeParameters.length;
|
| + if (count == 0) {
|
| + return TypeParameterType.EMPTY_LIST;
|
| + }
|
| + List<TypeParameterType> types = new List<TypeParameterType>(count);
|
| + for (int i = 0; i < count; i++) {
|
| + types[i] = typeParameters[i].type;
|
| + }
|
| + return types;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * The unique instance of the class `UndefinedTypeImpl` implements the type of
|
| + * type names that couldn't be resolved.
|
| + *
|
| + * This class behaves like DynamicTypeImpl in almost every respect, to reduce
|
| + * cascading errors.
|
| + */
|
| +class UndefinedTypeImpl extends TypeImpl {
|
| + /**
|
| + * The unique instance of this class.
|
| + */
|
| + static final UndefinedTypeImpl instance = new UndefinedTypeImpl._();
|
| +
|
| + /**
|
| + * Prevent the creation of instances of this class.
|
| + */
|
| + UndefinedTypeImpl._()
|
| + : super(DynamicElementImpl.instance, Keyword.DYNAMIC.syntax);
|
| +
|
| + @override
|
| + int get hashCode => 1;
|
| +
|
| + @override
|
| + bool get isDynamic => true;
|
| +
|
| + @override
|
| + bool get isUndefined => true;
|
| +
|
| + @override
|
| + bool operator ==(Object object) => identical(object, this);
|
| +
|
| + @override
|
| + bool isMoreSpecificThan(DartType type,
|
| + [bool withDynamic = false, Set<Element> visitedElements]) {
|
| + // T is S
|
| + if (identical(this, type)) {
|
| + return true;
|
| + }
|
| + // else
|
| + return withDynamic;
|
| + }
|
| +
|
| + @override
|
| + bool isSubtypeOf(DartType type) => true;
|
| +
|
| + @override
|
| + bool isSupertypeOf(DartType type) => true;
|
| +
|
| + @override
|
| + TypeImpl pruned(List<FunctionTypeAliasElement> prune) => this;
|
| +
|
| + @override
|
| + DartType substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) {
|
| + int length = parameterTypes.length;
|
| + for (int i = 0; i < length; i++) {
|
| + if (parameterTypes[i] == this) {
|
| + return argumentTypes[i];
|
| + }
|
| + }
|
| + return this;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * The type `void`.
|
| + */
|
| +abstract class VoidType implements DartType {
|
| + @override
|
| + VoidType substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes);
|
| +}
|
| +
|
| +/**
|
| + * A concrete implementation of a [VoidType].
|
| + */
|
| +class VoidTypeImpl extends TypeImpl implements VoidType {
|
| + /**
|
| + * The unique instance of this class.
|
| + */
|
| + static final VoidTypeImpl instance = new VoidTypeImpl._();
|
| +
|
| + /**
|
| + * Prevent the creation of instances of this class.
|
| + */
|
| + VoidTypeImpl._() : super(null, Keyword.VOID.syntax);
|
| +
|
| + @override
|
| + int get hashCode => 2;
|
| +
|
| + @override
|
| + bool get isVoid => true;
|
| +
|
| + @override
|
| + bool operator ==(Object object) => identical(object, this);
|
| +
|
| + @override
|
| + bool isMoreSpecificThan(DartType type,
|
| + [bool withDynamic = false, Set<Element> visitedElements]) =>
|
| + isSubtypeOf(type);
|
| +
|
| + @override
|
| + bool isSubtypeOf(DartType type) {
|
| + // The only subtype relations that pertain to void are therefore:
|
| + // void <: void (by reflexivity)
|
| + // bottom <: void (as bottom is a subtype of all types).
|
| + // void <: dynamic (as dynamic is a supertype of all types)
|
| + return identical(type, this) || type.isDynamic;
|
| + }
|
| +
|
| + @override
|
| + TypeImpl pruned(List<FunctionTypeAliasElement> prune) => this;
|
| +
|
| + @override
|
| + VoidTypeImpl substitute2(
|
| + List<DartType> argumentTypes, List<DartType> parameterTypes,
|
| + [List<FunctionTypeAliasElement> prune]) =>
|
| + this;
|
| +}
|
|
|