| // 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.element; |
| |
| import 'dart:collection'; |
| import 'dart:math' show min; |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/constant/value.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/dart/element/visitor.dart'; |
| import 'package:analyzer/src/dart/ast/utilities.dart'; |
| import 'package:analyzer/src/dart/constant/value.dart'; |
| import 'package:analyzer/src/dart/element/handle.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/error/codes.dart' show CompileTimeErrorCode; |
| import 'package:analyzer/src/generated/constant.dart' show EvaluationResultImpl; |
| import 'package:analyzer/src/generated/engine.dart' |
| show AnalysisContext, AnalysisEngine; |
| import 'package:analyzer/src/generated/java_engine.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/sdk.dart' show DartSdk; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/generated/testing/ast_test_factory.dart'; |
| import 'package:analyzer/src/generated/utilities_collection.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| import 'package:analyzer/src/generated/utilities_general.dart'; |
| import 'package:analyzer/src/summary/idl.dart'; |
| import 'package:analyzer/src/task/dart.dart'; |
| |
| /** |
| * Assert that the given [object] is null, which in the places where this |
| * function is called means that the element is not resynthesized. |
| */ |
| void _assertNotResynthesized(Object object) { |
| // TODO(scheglov) I comment this check for now. |
| // When we make a decision about switch to the new analysis driver, |
| // we will need to rework the analysis code to don't call the setters |
| // or restore / inline it. |
| // assert(object == null); |
| } |
| |
| /** |
| * A concrete implementation of a [ClassElement]. |
| */ |
| abstract class AbstractClassElementImpl extends ElementImpl |
| implements ClassElement { |
| /** |
| * A list containing all of the accessors (getters and setters) contained in |
| * this class. |
| */ |
| List<PropertyAccessorElement> _accessors; |
| |
| /** |
| * A list containing all of the fields contained in this class. |
| */ |
| List<FieldElement> _fields; |
| |
| /** |
| * Initialize a newly created class element to have the given [name] at the |
| * given [offset] in the file that contains the declaration of this element. |
| */ |
| AbstractClassElementImpl(String name, int offset) : super(name, offset); |
| |
| /** |
| * Initialize a newly created class element to have the given [name]. |
| */ |
| AbstractClassElementImpl.forNode(Identifier name) : super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| AbstractClassElementImpl.forSerialized( |
| CompilationUnitElementImpl enclosingUnit) |
| : super.forSerialized(enclosingUnit); |
| |
| @override |
| List<PropertyAccessorElement> get accessors { |
| return _accessors ?? const <PropertyAccessorElement>[]; |
| } |
| |
| /** |
| * Set the accessors contained in this class to the given [accessors]. |
| */ |
| void set accessors(List<PropertyAccessorElement> accessors) { |
| for (PropertyAccessorElement accessor in accessors) { |
| (accessor as PropertyAccessorElementImpl).enclosingElement = this; |
| } |
| this._accessors = accessors; |
| } |
| |
| @override |
| String get displayName => name; |
| |
| @override |
| List<FieldElement> get fields => _fields ?? const <FieldElement>[]; |
| |
| /** |
| * Set the fields contained in this class to the given [fields]. |
| */ |
| void set fields(List<FieldElement> fields) { |
| for (FieldElement field in fields) { |
| (field as FieldElementImpl).enclosingElement = this; |
| } |
| this._fields = fields; |
| } |
| |
| @override |
| bool get isEnum; |
| |
| @override |
| ElementKind get kind => ElementKind.CLASS; |
| |
| @override |
| /*=T*/ accept/*<T>*/(ElementVisitor<dynamic/*=T*/ > visitor) => |
| visitor.visitClassElement(this); |
| |
| @override |
| NamedCompilationUnitMember computeNode() { |
| if (isEnum) { |
| return getNodeMatching((node) => node is EnumDeclaration); |
| } else { |
| return getNodeMatching( |
| (node) => node is ClassDeclaration || node is ClassTypeAlias); |
| } |
| } |
| |
| @override |
| ElementImpl getChild(String identifier) { |
| // |
| // The casts in this method are safe because the set methods would have |
| // thrown a CCE if any of the elements in the arrays were not of the |
| // expected types. |
| // |
| for (PropertyAccessorElement accessor in accessors) { |
| PropertyAccessorElementImpl accessorImpl = accessor; |
| if (accessorImpl.identifier == identifier) { |
| return accessorImpl; |
| } |
| } |
| for (FieldElement field in fields) { |
| FieldElementImpl fieldImpl = field; |
| if (fieldImpl.identifier == identifier) { |
| return fieldImpl; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| FieldElement getField(String name) { |
| for (FieldElement fieldElement in fields) { |
| if (name == fieldElement.name) { |
| return fieldElement; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| PropertyAccessorElement getGetter(String getterName) { |
| int length = accessors.length; |
| for (int i = 0; i < length; i++) { |
| PropertyAccessorElement accessor = accessors[i]; |
| if (accessor.isGetter && accessor.name == getterName) { |
| return accessor; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| PropertyAccessorElement getSetter(String setterName) { |
| // TODO (jwren) revisit- should we append '=' here or require clients to |
| // include it? |
| // Do we need the check for isSetter below? |
| if (!StringUtilities.endsWithChar(setterName, 0x3D)) { |
| setterName += '='; |
| } |
| for (PropertyAccessorElement accessor in accessors) { |
| if (accessor.isSetter && accessor.name == setterName) { |
| return accessor; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| MethodElement lookUpConcreteMethod( |
| String methodName, LibraryElement library) => |
| _first(_implementationsOfMethod(methodName).where( |
| (MethodElement method) => |
| !method.isAbstract && method.isAccessibleIn(library))); |
| |
| @override |
| PropertyAccessorElement lookUpGetter( |
| String getterName, LibraryElement library) => |
| _first(_implementationsOfGetter(getterName).where( |
| (PropertyAccessorElement getter) => getter.isAccessibleIn(library))); |
| |
| @override |
| PropertyAccessorElement lookUpInheritedConcreteGetter( |
| String getterName, LibraryElement library) => |
| _first(_implementationsOfGetter(getterName).where( |
| (PropertyAccessorElement getter) => |
| !getter.isAbstract && |
| getter.isAccessibleIn(library) && |
| getter.enclosingElement != this)); |
| |
| @override |
| MethodElement lookUpInheritedConcreteMethod( |
| String methodName, LibraryElement library) => |
| _first(_implementationsOfMethod(methodName).where( |
| (MethodElement method) => |
| !method.isAbstract && |
| method.isAccessibleIn(library) && |
| method.enclosingElement != this)); |
| |
| @override |
| PropertyAccessorElement lookUpInheritedConcreteSetter( |
| String setterName, LibraryElement library) => |
| _first(_implementationsOfSetter(setterName).where( |
| (PropertyAccessorElement setter) => |
| !setter.isAbstract && |
| setter.isAccessibleIn(library) && |
| setter.enclosingElement != this)); |
| |
| @override |
| MethodElement lookUpInheritedMethod( |
| String methodName, LibraryElement library) => |
| _first(_implementationsOfMethod(methodName).where( |
| (MethodElement method) => |
| method.isAccessibleIn(library) && |
| method.enclosingElement != this)); |
| |
| @override |
| MethodElement lookUpMethod(String methodName, LibraryElement library) => |
| _first(_implementationsOfMethod(methodName) |
| .where((MethodElement method) => method.isAccessibleIn(library))); |
| |
| @override |
| PropertyAccessorElement lookUpSetter( |
| String setterName, LibraryElement library) => |
| _first(_implementationsOfSetter(setterName).where( |
| (PropertyAccessorElement setter) => setter.isAccessibleIn(library))); |
| |
| @override |
| void visitChildren(ElementVisitor visitor) { |
| super.visitChildren(visitor); |
| safelyVisitChildren(accessors, visitor); |
| safelyVisitChildren(fields, visitor); |
| } |
| |
| /** |
| * Return the first element from the given [iterable], or `null` if the |
| * iterable is empty. |
| */ |
| Object/*=E*/ _first/*<E>*/(Iterable/*<E>*/ iterable) { |
| if (iterable.isEmpty) { |
| return null; |
| } |
| return iterable.first; |
| } |
| |
| /** |
| * Return an iterable containing all of the implementations of a getter with |
| * the given [getterName] that are defined in this class any any superclass of |
| * this class (but not in interfaces). |
| * |
| * The getters that are returned are not filtered in any way. In particular, |
| * they can include getters that are not visible in some context. Clients must |
| * perform any necessary filtering. |
| * |
| * The getters are returned based on the depth of their defining class; if |
| * this class contains a definition of the getter it will occur first, if |
| * Object contains a definition of the getter it will occur last. |
| */ |
| Iterable<PropertyAccessorElement> _implementationsOfGetter( |
| String getterName) sync* { |
| ClassElement classElement = this; |
| HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
| while (classElement != null && visitedClasses.add(classElement)) { |
| PropertyAccessorElement getter = classElement.getGetter(getterName); |
| if (getter != null) { |
| yield getter; |
| } |
| for (InterfaceType mixin in classElement.mixins.reversed) { |
| getter = mixin.element?.getGetter(getterName); |
| if (getter != null) { |
| yield getter; |
| } |
| } |
| classElement = classElement.supertype?.element; |
| } |
| } |
| |
| /** |
| * Return an iterable containing all of the implementations of a method with |
| * the given [methodName] that are defined in this class any any superclass of |
| * this class (but not in interfaces). |
| * |
| * The methods that are returned are not filtered in any way. In particular, |
| * they can include methods that are not visible in some context. Clients must |
| * perform any necessary filtering. |
| * |
| * The methods are returned based on the depth of their defining class; if |
| * this class contains a definition of the method it will occur first, if |
| * Object contains a definition of the method it will occur last. |
| */ |
| Iterable<MethodElement> _implementationsOfMethod(String methodName) sync* { |
| ClassElement classElement = this; |
| HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
| while (classElement != null && visitedClasses.add(classElement)) { |
| MethodElement method = classElement.getMethod(methodName); |
| if (method != null) { |
| yield method; |
| } |
| for (InterfaceType mixin in classElement.mixins.reversed) { |
| method = mixin.element?.getMethod(methodName); |
| if (method != null) { |
| yield method; |
| } |
| } |
| classElement = classElement.supertype?.element; |
| } |
| } |
| |
| /** |
| * Return an iterable containing all of the implementations of a setter with |
| * the given [setterName] that are defined in this class any any superclass of |
| * this class (but not in interfaces). |
| * |
| * The setters that are returned are not filtered in any way. In particular, |
| * they can include setters that are not visible in some context. Clients must |
| * perform any necessary filtering. |
| * |
| * The setters are returned based on the depth of their defining class; if |
| * this class contains a definition of the setter it will occur first, if |
| * Object contains a definition of the setter it will occur last. |
| */ |
| Iterable<PropertyAccessorElement> _implementationsOfSetter( |
| String setterName) sync* { |
| ClassElement classElement = this; |
| HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
| while (classElement != null && visitedClasses.add(classElement)) { |
| PropertyAccessorElement setter = classElement.getSetter(setterName); |
| if (setter != null) { |
| yield setter; |
| } |
| for (InterfaceType mixin in classElement.mixins.reversed) { |
| setter = mixin.element?.getSetter(setterName); |
| if (setter != null) { |
| yield setter; |
| } |
| } |
| classElement = classElement.supertype?.element; |
| } |
| } |
| |
| /** |
| * Return the [AbstractClassElementImpl] of the given [classElement]. May |
| * throw an exception if the [AbstractClassElementImpl] cannot be provided |
| * (should not happen though). |
| */ |
| static AbstractClassElementImpl getImpl(ClassElement classElement) { |
| if (classElement is ClassElementHandle) { |
| return getImpl(classElement.actualElement); |
| } |
| return classElement as AbstractClassElementImpl; |
| } |
| } |
| |
| /** |
| * For AST nodes that could be in both the getter and setter contexts |
| * ([IndexExpression]s and [SimpleIdentifier]s), the additional resolved |
| * elements are stored in the AST node, in an [AuxiliaryElements]. Because |
| * resolved elements are either statically resolved or resolved using propagated |
| * type information, this class is a wrapper for a pair of [ExecutableElement]s, |
| * not just a single [ExecutableElement]. |
| */ |
| class AuxiliaryElements { |
| /** |
| * The element based on propagated type information, or `null` if the AST |
| * structure has not been resolved or if the node could not be resolved. |
| */ |
| final ExecutableElement propagatedElement; |
| |
| /** |
| * The element based on static type information, or `null` if the AST |
| * structure has not been resolved or if the node could not be resolved. |
| */ |
| final ExecutableElement staticElement; |
| |
| /** |
| * Initialize a newly created pair to have both the [staticElement] and the |
| * [propagatedElement]. |
| */ |
| AuxiliaryElements(this.staticElement, this.propagatedElement); |
| } |
| |
| /** |
| * An [AbstractClassElementImpl] which is a class. |
| */ |
| class ClassElementImpl extends AbstractClassElementImpl |
| with TypeParameterizedElementMixin { |
| /** |
| * The unlinked representation of the class in the summary. |
| */ |
| final UnlinkedClass _unlinkedClass; |
| |
| /** |
| * The superclass of the class, or `null` for [Object]. |
| */ |
| InterfaceType _supertype; |
| |
| /** |
| * The type defined by the class. |
| */ |
| InterfaceType _type; |
| |
| /** |
| * A list containing all of the mixins that are applied to the class being |
| * extended in order to derive the superclass of this class. |
| */ |
| List<InterfaceType> _mixins; |
| |
| /** |
| * A list containing all of the interfaces that are implemented by this class. |
| */ |
| List<InterfaceType> _interfaces; |
| |
| /** |
| * For classes which are not mixin applications, a list containing all of the |
| * constructors contained in this class, or `null` if the list of |
| * constructors has not yet been built. |
| * |
| * For classes which are mixin applications, the list of constructors is |
| * computed on the fly by the [constructors] getter, and this field is |
| * `null`. |
| */ |
| List<ConstructorElement> _constructors; |
| |
| /** |
| * A list containing all of the methods contained in this class. |
| */ |
| List<MethodElement> _methods; |
| |
| /** |
| * A flag indicating whether the types associated with the instance members of |
| * this class have been inferred. |
| */ |
| bool _hasBeenInferred = false; |
| |
| /** |
| * The version of this element. The version is changed when the element is |
| * incrementally updated, so that its lists of constructors, accessors and |
| * methods might be different. |
| */ |
| int version = 0; |
| |
| /** |
| * Initialize a newly created class element to have the given [name] at the |
| * given [offset] in the file that contains the declaration of this element. |
| */ |
| ClassElementImpl(String name, int offset) |
| : _unlinkedClass = null, |
| super(name, offset); |
| |
| /** |
| * Initialize a newly created class element to have the given [name]. |
| */ |
| ClassElementImpl.forNode(Identifier name) |
| : _unlinkedClass = null, |
| super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| ClassElementImpl.forSerialized( |
| this._unlinkedClass, CompilationUnitElementImpl enclosingUnit) |
| : super.forSerialized(enclosingUnit); |
| |
| /** |
| * Set whether this class is abstract. |
| */ |
| void set abstract(bool isAbstract) { |
| _assertNotResynthesized(_unlinkedClass); |
| setModifier(Modifier.ABSTRACT, isAbstract); |
| } |
| |
| @override |
| List<PropertyAccessorElement> get accessors { |
| if (_unlinkedClass != null && _accessors == null) { |
| _resynthesizeFieldsAndPropertyAccessors(); |
| } |
| return _accessors ?? const <PropertyAccessorElement>[]; |
| } |
| |
| @override |
| void set accessors(List<PropertyAccessorElement> accessors) { |
| _assertNotResynthesized(_unlinkedClass); |
| super.accessors = accessors; |
| } |
| |
| @override |
| List<InterfaceType> get allSupertypes { |
| List<InterfaceType> list = new List<InterfaceType>(); |
| _collectAllSupertypes(list); |
| return list; |
| } |
| |
| @override |
| int get codeLength { |
| if (_unlinkedClass != null) { |
| return _unlinkedClass.codeRange?.length; |
| } |
| return super.codeLength; |
| } |
| |
| @override |
| int get codeOffset { |
| if (_unlinkedClass != null) { |
| return _unlinkedClass.codeRange?.offset; |
| } |
| return super.codeOffset; |
| } |
| |
| @override |
| List<ConstructorElement> get constructors { |
| if (isMixinApplication) { |
| return _computeMixinAppConstructors(); |
| } |
| if (_unlinkedClass != null && _constructors == null) { |
| _constructors = _unlinkedClass.executables |
| .where((e) => e.kind == UnlinkedExecutableKind.constructor) |
| .map((e) => new ConstructorElementImpl.forSerialized(e, this)) |
| .toList(growable: false); |
| // Ensure at least implicit default constructor. |
| if (_constructors.isEmpty) { |
| ConstructorElementImpl constructor = new ConstructorElementImpl('', -1); |
| constructor.isSynthetic = true; |
| constructor.enclosingElement = this; |
| _constructors = <ConstructorElement>[constructor]; |
| } |
| } |
| assert(_constructors != null); |
| return _constructors ?? const <ConstructorElement>[]; |
| } |
| |
| /** |
| * Set the constructors contained in this class to the given [constructors]. |
| * |
| * Should only be used for class elements that are not mixin applications. |
| */ |
| void set constructors(List<ConstructorElement> constructors) { |
| _assertNotResynthesized(_unlinkedClass); |
| assert(!isMixinApplication); |
| for (ConstructorElement constructor in constructors) { |
| (constructor as ConstructorElementImpl).enclosingElement = this; |
| } |
| this._constructors = constructors; |
| } |
| |
| @override |
| String get documentationComment { |
| if (_unlinkedClass != null) { |
| return _unlinkedClass?.documentationComment?.text; |
| } |
| return super.documentationComment; |
| } |
| |
| /** |
| * Return `true` if [CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS] should |
| * be reported for this class. |
| */ |
| bool get doesMixinLackConstructors { |
| if (!isMixinApplication && mixins.isEmpty) { |
| // This class is not a mixin application and it doesn't have a "with" |
| // clause, so CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS is |
| // inapplicable. |
| return false; |
| } |
| if (supertype == null) { |
| // Should never happen, since Object is the only class that has no |
| // supertype, and it should have been caught by the test above. |
| assert(false); |
| return false; |
| } |
| // Find the nearest class in the supertype chain that is not a mixin |
| // application. |
| ClassElement nearestNonMixinClass = supertype.element; |
| if (nearestNonMixinClass.isMixinApplication) { |
| // Use a list to keep track of the classes we've seen, so that we won't |
| // go into an infinite loop in the event of a non-trivial loop in the |
| // class hierarchy. |
| List<ClassElement> classesSeen = <ClassElement>[this]; |
| while (nearestNonMixinClass.isMixinApplication) { |
| if (classesSeen.contains(nearestNonMixinClass)) { |
| // Loop in the class hierarchy (which is reported elsewhere). Don't |
| // confuse the user with further errors. |
| return false; |
| } |
| classesSeen.add(nearestNonMixinClass); |
| if (nearestNonMixinClass.supertype == null) { |
| // Should never happen, since Object is the only class that has no |
| // supertype, and it is not a mixin application. |
| assert(false); |
| return false; |
| } |
| nearestNonMixinClass = nearestNonMixinClass.supertype.element; |
| } |
| } |
| return !nearestNonMixinClass.constructors.any(isSuperConstructorAccessible); |
| } |
| |
| @override |
| TypeParameterizedElementMixin get enclosingTypeParameterContext => null; |
| |
| @override |
| List<FieldElement> get fields { |
| if (_unlinkedClass != null && _fields == null) { |
| _resynthesizeFieldsAndPropertyAccessors(); |
| } |
| return _fields ?? const <FieldElement>[]; |
| } |
| |
| @override |
| void set fields(List<FieldElement> fields) { |
| _assertNotResynthesized(_unlinkedClass); |
| super.fields = fields; |
| } |
| |
| bool get hasBeenInferred { |
| if (_unlinkedClass != null) { |
| return context.analysisOptions.strongMode; |
| } |
| return _hasBeenInferred; |
| } |
| |
| void set hasBeenInferred(bool hasBeenInferred) { |
| _assertNotResynthesized(_unlinkedClass); |
| _hasBeenInferred = hasBeenInferred; |
| } |
| |
| @override |
| bool get hasNonFinalField { |
| List<ClassElement> classesToVisit = new List<ClassElement>(); |
| HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
| classesToVisit.add(this); |
| while (!classesToVisit.isEmpty) { |
| ClassElement currentElement = classesToVisit.removeAt(0); |
| if (visitedClasses.add(currentElement)) { |
| // check fields |
| for (FieldElement field in currentElement.fields) { |
| if (!field.isFinal && |
| !field.isConst && |
| !field.isStatic && |
| !field.isSynthetic) { |
| return true; |
| } |
| } |
| // check mixins |
| for (InterfaceType mixinType in currentElement.mixins) { |
| ClassElement mixinElement = mixinType.element; |
| classesToVisit.add(mixinElement); |
| } |
| // check super |
| InterfaceType supertype = currentElement.supertype; |
| if (supertype != null) { |
| ClassElement superElement = supertype.element; |
| if (superElement != null) { |
| classesToVisit.add(superElement); |
| } |
| } |
| } |
| } |
| // not found |
| return false; |
| } |
| |
| /** |
| * Return `true` if the class has a `noSuchMethod()` method distinct from the |
| * one declared in class `Object`, as per the Dart Language Specification |
| * (section 10.4). |
| */ |
| bool get hasNoSuchMethod { |
| MethodElement method = |
| lookUpMethod(FunctionElement.NO_SUCH_METHOD_METHOD_NAME, library); |
| ClassElement definingClass = method?.enclosingElement; |
| return definingClass != null && !definingClass.type.isObject; |
| } |
| |
| @override |
| bool get hasReferenceToSuper => hasModifier(Modifier.REFERENCES_SUPER); |
| |
| /** |
| * Set whether this class references 'super'. |
| */ |
| void set hasReferenceToSuper(bool isReferencedSuper) { |
| setModifier(Modifier.REFERENCES_SUPER, isReferencedSuper); |
| } |
| |
| @override |
| bool get hasStaticMember { |
| for (MethodElement method in methods) { |
| if (method.isStatic) { |
| return true; |
| } |
| } |
| for (PropertyAccessorElement accessor in accessors) { |
| if (accessor.isStatic) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| List<InterfaceType> get interfaces { |
| if (_unlinkedClass != null && _interfaces == null) { |
| ResynthesizerContext context = enclosingUnit.resynthesizerContext; |
| _interfaces = _unlinkedClass.interfaces |
| .map((EntityRef t) => context.resolveTypeRef(t, this)) |
| .where(_isClassInterfaceType) |
| .toList(growable: false); |
| } |
| return _interfaces ?? const <InterfaceType>[]; |
| } |
| |
| void set interfaces(List<InterfaceType> interfaces) { |
| _assertNotResynthesized(_unlinkedClass); |
| _interfaces = interfaces; |
| } |
| |
| @override |
| bool get isAbstract { |
| if (_unlinkedClass != null) { |
| return _unlinkedClass.isAbstract; |
| } |
| return hasModifier(Modifier.ABSTRACT); |
| } |
| |
| @override |
| bool get isEnum => false; |
| |
| @override |
| bool get isMixinApplication { |
| if (_unlinkedClass != null) { |
| return _unlinkedClass.isMixinApplication; |
| } |
| return hasModifier(Modifier.MIXIN_APPLICATION); |
| } |
| |
| @override |
| bool get isOrInheritsProxy => |
| _safeIsOrInheritsProxy(this, new HashSet<ClassElement>()); |
| |
| @override |
| bool get isProxy { |
| for (ElementAnnotation annotation in metadata) { |
| if (annotation.isProxy) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool get isValidMixin { |
| if (!context.analysisOptions.enableSuperMixins) { |
| if (hasReferenceToSuper) { |
| return false; |
| } |
| if (!supertype.isObject) { |
| return false; |
| } |
| } |
| for (ConstructorElement constructor in constructors) { |
| if (!constructor.isSynthetic && !constructor.isFactory) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @override |
| List<ElementAnnotation> get metadata { |
| if (_unlinkedClass != null) { |
| return _metadata ??= |
| _buildAnnotations(enclosingUnit, _unlinkedClass.annotations); |
| } |
| return super.metadata; |
| } |
| |
| @override |
| List<MethodElement> get methods { |
| if (_unlinkedClass != null) { |
| _methods ??= _unlinkedClass.executables |
| .where((e) => e.kind == UnlinkedExecutableKind.functionOrMethod) |
| .map((e) => new MethodElementImpl.forSerialized(e, this)) |
| .toList(growable: false); |
| } |
| return _methods ?? const <MethodElement>[]; |
| } |
| |
| /** |
| * Set the methods contained in this class to the given [methods]. |
| */ |
| void set methods(List<MethodElement> methods) { |
| _assertNotResynthesized(_unlinkedClass); |
| for (MethodElement method in methods) { |
| (method as MethodElementImpl).enclosingElement = this; |
| } |
| _methods = methods; |
| } |
| |
| /** |
| * Set whether this class is a mixin application. |
| */ |
| void set mixinApplication(bool isMixinApplication) { |
| _assertNotResynthesized(_unlinkedClass); |
| setModifier(Modifier.MIXIN_APPLICATION, isMixinApplication); |
| } |
| |
| @override |
| List<InterfaceType> get mixins { |
| if (_unlinkedClass != null && _mixins == null) { |
| ResynthesizerContext context = enclosingUnit.resynthesizerContext; |
| _mixins = _unlinkedClass.mixins |
| .map((EntityRef t) => context.resolveTypeRef(t, this)) |
| .where(_isClassInterfaceType) |
| .toList(growable: false); |
| } |
| return _mixins ?? const <InterfaceType>[]; |
| } |
| |
| void set mixins(List<InterfaceType> mixins) { |
| _assertNotResynthesized(_unlinkedClass); |
| _mixins = mixins; |
| } |
| |
| @override |
| String get name { |
| if (_unlinkedClass != null) { |
| return _unlinkedClass.name; |
| } |
| return super.name; |
| } |
| |
| @override |
| int get nameOffset { |
| int offset = super.nameOffset; |
| if (offset == 0 && _unlinkedClass != null) { |
| return _unlinkedClass.nameOffset; |
| } |
| return offset; |
| } |
| |
| @override |
| InterfaceType get supertype { |
| if (_unlinkedClass != null && _supertype == null) { |
| if (_unlinkedClass.supertype != null) { |
| DartType type = enclosingUnit.resynthesizerContext |
| .resolveTypeRef(_unlinkedClass.supertype, this); |
| if (_isClassInterfaceType(type)) { |
| _supertype = type; |
| } else { |
| _supertype = context.typeProvider.objectType; |
| } |
| } else if (_unlinkedClass.hasNoSupertype) { |
| return null; |
| } else { |
| _supertype = context.typeProvider.objectType; |
| } |
| } |
| return _supertype; |
| } |
| |
| void set supertype(InterfaceType supertype) { |
| _assertNotResynthesized(_unlinkedClass); |
| _supertype = supertype; |
| } |
| |
| @override |
| InterfaceType get type { |
| if (_type == null) { |
| InterfaceTypeImpl type = new InterfaceTypeImpl(this); |
| type.typeArguments = typeParameterTypes; |
| _type = type; |
| } |
| return _type; |
| } |
| |
| /** |
| * Set the type parameters defined for this class to the given |
| * [typeParameters]. |
| */ |
| void set typeParameters(List<TypeParameterElement> typeParameters) { |
| _assertNotResynthesized(_unlinkedClass); |
| for (TypeParameterElement typeParameter in typeParameters) { |
| (typeParameter as TypeParameterElementImpl).enclosingElement = this; |
| } |
| this._typeParameterElements = typeParameters; |
| } |
| |
| @override |
| List<UnlinkedTypeParam> get unlinkedTypeParams => |
| _unlinkedClass?.typeParameters; |
| |
| @override |
| ConstructorElement get unnamedConstructor { |
| for (ConstructorElement element in constructors) { |
| String name = element.displayName; |
| if (name == null || name.isEmpty) { |
| return element; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| void appendTo(StringBuffer buffer) { |
| if (isAbstract) { |
| buffer.write('abstract '); |
| } |
| buffer.write('class '); |
| String name = displayName; |
| if (name == null) { |
| buffer.write("{unnamed class}"); |
| } else { |
| buffer.write(name); |
| } |
| int variableCount = typeParameters.length; |
| if (variableCount > 0) { |
| buffer.write("<"); |
| for (int i = 0; i < variableCount; i++) { |
| if (i > 0) { |
| buffer.write(", "); |
| } |
| (typeParameters[i] as TypeParameterElementImpl).appendTo(buffer); |
| } |
| buffer.write(">"); |
| } |
| if (supertype != null && !supertype.isObject) { |
| buffer.write(' extends '); |
| buffer.write(supertype.displayName); |
| } |
| if (mixins.isNotEmpty) { |
| buffer.write(' with '); |
| buffer.write(mixins.map((t) => t.displayName).join(', ')); |
| } |
| if (interfaces.isNotEmpty) { |
| buffer.write(' implements '); |
| buffer.write(interfaces.map((t) => t.displayName).join(', ')); |
| } |
| } |
| |
| @override |
| ElementImpl getChild(String identifier) { |
| ElementImpl child = super.getChild(identifier); |
| if (child != null) { |
| return child; |
| } |
| // |
| // The casts in this method are safe because the set methods would have |
| // thrown a CCE if any of the elements in the arrays were not of the |
| // expected types. |
| // |
| for (ConstructorElement constructor in _constructors) { |
| ConstructorElementImpl constructorImpl = constructor; |
| if (constructorImpl.identifier == identifier) { |
| return constructorImpl; |
| } |
| } |
| for (MethodElement method in methods) { |
| MethodElementImpl methodImpl = method; |
| if (methodImpl.identifier == identifier) { |
| return methodImpl; |
| } |
| } |
| for (TypeParameterElement typeParameter in typeParameters) { |
| TypeParameterElementImpl typeParameterImpl = typeParameter; |
| if (typeParameterImpl.identifier == identifier) { |
| return typeParameterImpl; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| MethodElement getMethod(String methodName) { |
| int length = methods.length; |
| for (int i = 0; i < length; i++) { |
| MethodElement method = methods[i]; |
| if (method.name == methodName) { |
| return method; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| ConstructorElement getNamedConstructor(String name) { |
| for (ConstructorElement element in constructors) { |
| String elementName = element.name; |
| if (elementName != null && elementName == name) { |
| return element; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| bool isSuperConstructorAccessible(ConstructorElement constructor) { |
| // If this class has no mixins, then all superclass constructors are |
| // accessible. |
| if (mixins.isEmpty) { |
| return true; |
| } |
| // Otherwise only constructors that lack optional parameters are |
| // accessible (see dartbug.com/19576). |
| for (ParameterElement parameter in constructor.parameters) { |
| if (parameter.parameterKind != ParameterKind.REQUIRED) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @override |
| void visitChildren(ElementVisitor visitor) { |
| super.visitChildren(visitor); |
| safelyVisitChildren(constructors, visitor); |
| safelyVisitChildren(methods, visitor); |
| safelyVisitChildren(typeParameters, visitor); |
| } |
| |
| void _collectAllSupertypes(List<InterfaceType> supertypes) { |
| List<InterfaceType> typesToVisit = new List<InterfaceType>(); |
| List<ClassElement> visitedClasses = new List<ClassElement>(); |
| typesToVisit.add(this.type); |
| while (!typesToVisit.isEmpty) { |
| InterfaceType currentType = typesToVisit.removeAt(0); |
| ClassElement currentElement = currentType.element; |
| if (!visitedClasses.contains(currentElement)) { |
| visitedClasses.add(currentElement); |
| if (!identical(currentType, this.type)) { |
| supertypes.add(currentType); |
| } |
| InterfaceType supertype = currentType.superclass; |
| if (supertype != null) { |
| typesToVisit.add(supertype); |
| } |
| for (InterfaceType type in currentElement.interfaces) { |
| typesToVisit.add(type); |
| } |
| for (InterfaceType type in currentElement.mixins) { |
| ClassElement element = type.element; |
| if (!visitedClasses.contains(element)) { |
| supertypes.add(type); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Compute a list of constructors for this class, which is a mixin |
| * application. If specified, [visitedClasses] is a list of the other mixin |
| * application classes which have been visited on the way to reaching this |
| * one (this is used to detect circularities). |
| */ |
| List<ConstructorElement> _computeMixinAppConstructors( |
| [List<ClassElementImpl> visitedClasses = null]) { |
| // First get the list of constructors of the superclass which need to be |
| // forwarded to this class. |
| Iterable<ConstructorElement> constructorsToForward; |
| if (supertype == null) { |
| // Shouldn't ever happen, since the only class with no supertype is |
| // Object, and it isn't a mixin application. But for safety's sake just |
| // assume an empty list. |
| assert(false); |
| constructorsToForward = <ConstructorElement>[]; |
| } else if (!supertype.element.isMixinApplication) { |
| List<ConstructorElement> superclassConstructors = |
| supertype.element.constructors; |
| // Filter out any constructors with optional parameters (see |
| // dartbug.com/15101). |
| constructorsToForward = |
| superclassConstructors.where(isSuperConstructorAccessible); |
| } else { |
| if (visitedClasses == null) { |
| visitedClasses = <ClassElementImpl>[this]; |
| } else { |
| if (visitedClasses.contains(this)) { |
| // Loop in the class hierarchy. Don't try to forward any |
| // constructors. |
| return <ConstructorElement>[]; |
| } |
| visitedClasses.add(this); |
| } |
| try { |
| ClassElementImpl superElement = AbstractClassElementImpl |
| .getImpl(supertype.element) as ClassElementImpl; |
| constructorsToForward = |
| superElement._computeMixinAppConstructors(visitedClasses); |
| } finally { |
| visitedClasses.removeLast(); |
| } |
| } |
| |
| // Figure out the type parameter substitution we need to perform in order |
| // to produce constructors for this class. We want to be robust in the |
| // face of errors, so drop any extra type arguments and fill in any missing |
| // ones with `dynamic`. |
| List<DartType> parameterTypes = |
| TypeParameterTypeImpl.getTypes(supertype.typeParameters); |
| List<DartType> argumentTypes = new List<DartType>.filled( |
| parameterTypes.length, DynamicTypeImpl.instance); |
| for (int i = 0; i < supertype.typeArguments.length; i++) { |
| if (i >= argumentTypes.length) { |
| break; |
| } |
| argumentTypes[i] = supertype.typeArguments[i]; |
| } |
| |
| // Now create an implicit constructor for every constructor found above, |
| // substituting type parameters as appropriate. |
| return constructorsToForward |
| .map((ConstructorElement superclassConstructor) { |
| ConstructorElementImpl implicitConstructor = |
| new ConstructorElementImpl(superclassConstructor.name, -1); |
| implicitConstructor.isSynthetic = true; |
| implicitConstructor.redirectedConstructor = superclassConstructor; |
| List<ParameterElement> superParameters = superclassConstructor.parameters; |
| int count = superParameters.length; |
| if (count > 0) { |
| List<ParameterElement> implicitParameters = |
| new List<ParameterElement>(count); |
| for (int i = 0; i < count; i++) { |
| ParameterElement superParameter = superParameters[i]; |
| ParameterElementImpl implicitParameter = |
| new ParameterElementImpl(superParameter.name, -1); |
| implicitParameter.isConst = superParameter.isConst; |
| implicitParameter.isFinal = superParameter.isFinal; |
| implicitParameter.parameterKind = superParameter.parameterKind; |
| implicitParameter.isSynthetic = true; |
| implicitParameter.type = |
| superParameter.type.substitute2(argumentTypes, parameterTypes); |
| implicitParameters[i] = implicitParameter; |
| } |
| implicitConstructor.parameters = implicitParameters; |
| } |
| implicitConstructor.enclosingElement = this; |
| return implicitConstructor; |
| }).toList(growable: false); |
| } |
| |
| /** |
| * Resynthesize explicit fields and property accessors and fill [_fields] and |
| * [_accessors] with explicit and implicit elements. |
| */ |
| void _resynthesizeFieldsAndPropertyAccessors() { |
| assert(_fields == null); |
| assert(_accessors == null); |
| // Build explicit fields and implicit property accessors. |
| var explicitFields = <FieldElement>[]; |
| var implicitAccessors = <PropertyAccessorElement>[]; |
| for (UnlinkedVariable v in _unlinkedClass.fields) { |
| FieldElementImpl field = |
| new FieldElementImpl.forSerializedFactory(v, this); |
| explicitFields.add(field); |
| implicitAccessors.add( |
| new PropertyAccessorElementImpl_ImplicitGetter(field) |
| ..enclosingElement = this); |
| if (!field.isConst && !field.isFinal) { |
| implicitAccessors.add( |
| new PropertyAccessorElementImpl_ImplicitSetter(field) |
| ..enclosingElement = this); |
| } |
| } |
| // Build explicit property accessors and implicit fields. |
| var explicitAccessors = <PropertyAccessorElement>[]; |
| var implicitFields = <String, FieldElementImpl>{}; |
| for (UnlinkedExecutable e in _unlinkedClass.executables) { |
| if (e.kind == UnlinkedExecutableKind.getter || |
| e.kind == UnlinkedExecutableKind.setter) { |
| PropertyAccessorElementImpl accessor = |
| new PropertyAccessorElementImpl.forSerialized(e, this); |
| explicitAccessors.add(accessor); |
| // Create or update the implicit field. |
| String fieldName = accessor.displayName; |
| FieldElementImpl field = implicitFields[fieldName]; |
| if (field == null) { |
| field = new FieldElementImpl(fieldName, -1); |
| implicitFields[fieldName] = field; |
| field.enclosingElement = this; |
| field.isSynthetic = true; |
| field.isFinal = e.kind == UnlinkedExecutableKind.getter; |
| field.isStatic = e.isStatic; |
| } else { |
| field.isFinal = false; |
| } |
| accessor.variable = field; |
| if (e.kind == UnlinkedExecutableKind.getter) { |
| field.getter = accessor; |
| } else { |
| field.setter = accessor; |
| } |
| } |
| } |
| // Combine explicit and implicit fields and property accessors. |
| _fields = <FieldElement>[] |
| ..addAll(explicitFields) |
| ..addAll(implicitFields.values); |
| _accessors = <PropertyAccessorElement>[] |
| ..addAll(explicitAccessors) |
| ..addAll(implicitAccessors); |
| } |
| |
| bool _safeIsOrInheritsProxy( |
| ClassElement element, HashSet<ClassElement> visited) { |
| if (visited.contains(element)) { |
| return false; |
| } |
| visited.add(element); |
| if (element.isProxy) { |
| return true; |
| } else if (element.supertype != null && |
| _safeIsOrInheritsProxy(element.supertype.element, visited)) { |
| return true; |
| } |
| List<InterfaceType> supertypes = element.interfaces; |
| for (int i = 0; i < supertypes.length; i++) { |
| if (_safeIsOrInheritsProxy(supertypes[i].element, visited)) { |
| return true; |
| } |
| } |
| supertypes = element.mixins; |
| for (int i = 0; i < supertypes.length; i++) { |
| if (_safeIsOrInheritsProxy(supertypes[i].element, visited)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Return `true` if the given [type] is a class [InterfaceType]. |
| */ |
| static bool _isClassInterfaceType(DartType type) { |
| return type is InterfaceType && !type.element.isEnum; |
| } |
| } |
| |
| /** |
| * A concrete implementation of a [CompilationUnitElement]. |
| */ |
| class CompilationUnitElementImpl extends UriReferencedElementImpl |
| implements CompilationUnitElement { |
| /** |
| * The context in which this unit is resynthesized, or `null` if the |
| * element is not resynthesized a summary. |
| */ |
| final ResynthesizerContext resynthesizerContext; |
| |
| /** |
| * The unlinked representation of the unit in the summary. |
| */ |
| final UnlinkedUnit _unlinkedUnit; |
| |
| /** |
| * The unlinked representation of the part in the summary. |
| */ |
| final UnlinkedPart _unlinkedPart; |
| |
| /** |
| * The source that corresponds to this compilation unit. |
| */ |
| @override |
| Source source; |
| |
| @override |
| LineInfo lineInfo; |
| |
| /** |
| * The source of the library containing this compilation unit. |
| * |
| * This is the same as the source of the containing [LibraryElement], |
| * except that it does not require the containing [LibraryElement] to be |
| * computed. |
| */ |
| Source librarySource; |
| |
| /** |
| * A table mapping the offset of a directive to the annotations associated |
| * with that directive, or `null` if none of the annotations in the |
| * compilation unit have annotations. |
| */ |
| Map<int, List<ElementAnnotation>> annotationMap = null; |
| |
| /** |
| * A list containing all of the top-level accessors (getters and setters) |
| * contained in this compilation unit. |
| */ |
| List<PropertyAccessorElement> _accessors; |
| |
| /** |
| * A list containing all of the enums contained in this compilation unit. |
| */ |
| List<ClassElement> _enums; |
| |
| /** |
| * A list containing all of the top-level functions contained in this |
| * compilation unit. |
| */ |
| List<FunctionElement> _functions; |
| |
| /** |
| * A list containing all of the function type aliases contained in this |
| * compilation unit. |
| */ |
| List<FunctionTypeAliasElement> _typeAliases; |
| |
| /** |
| * A list containing all of the types contained in this compilation unit. |
| */ |
| List<ClassElement> _types; |
| |
| /** |
| * A list containing all of the variables contained in this compilation unit. |
| */ |
| List<TopLevelVariableElement> _variables; |
| |
| /** |
| * A map from offsets to elements of this unit at these offsets. |
| */ |
| final Map<int, Element> _offsetToElementMap = new HashMap<int, Element>(); |
| |
| /** |
| * Resynthesized explicit top-level property accessors. |
| */ |
| UnitExplicitTopLevelAccessors _explicitTopLevelAccessors; |
| |
| /** |
| * Resynthesized explicit top-level variables. |
| */ |
| UnitExplicitTopLevelVariables _explicitTopLevelVariables; |
| |
| /** |
| * Description of top-level variable replacements that should be applied |
| * to implicit top-level variables because of re-linking top-level property |
| * accessors between different unit of the same library. |
| */ |
| Map<TopLevelVariableElement, TopLevelVariableElement> |
| _topLevelVariableReplaceMap; |
| |
| /** |
| * Initialize a newly created compilation unit element to have the given |
| * [name]. |
| */ |
| CompilationUnitElementImpl(String name) |
| : resynthesizerContext = null, |
| _unlinkedUnit = null, |
| _unlinkedPart = null, |
| super(name, -1); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| CompilationUnitElementImpl.forSerialized( |
| LibraryElementImpl enclosingLibrary, |
| this.resynthesizerContext, |
| this._unlinkedUnit, |
| this._unlinkedPart, |
| String name) |
| : super.forSerialized(null) { |
| _enclosingElement = enclosingLibrary; |
| _name = name; |
| _nameOffset = -1; |
| } |
| |
| @override |
| List<PropertyAccessorElement> get accessors { |
| if (_unlinkedUnit != null) { |
| if (_accessors == null) { |
| _explicitTopLevelAccessors ??= |
| resynthesizerContext.buildTopLevelAccessors(); |
| _explicitTopLevelVariables ??= |
| resynthesizerContext.buildTopLevelVariables(); |
| List<PropertyAccessorElementImpl> accessors = |
| <PropertyAccessorElementImpl>[]; |
| accessors.addAll(_explicitTopLevelAccessors.accessors); |
| accessors.addAll(_explicitTopLevelVariables.implicitAccessors); |
| _accessors = accessors; |
| } |
| } |
| return _accessors ?? PropertyAccessorElement.EMPTY_LIST; |
| } |
| |
| /** |
| * Set the top-level accessors (getters and setters) contained in this |
| * compilation unit to the given [accessors]. |
| */ |
| void set accessors(List<PropertyAccessorElement> accessors) { |
| for (PropertyAccessorElement accessor in accessors) { |
| (accessor as PropertyAccessorElementImpl).enclosingElement = this; |
| } |
| this._accessors = accessors; |
| } |
| |
| @override |
| int get codeLength { |
| if (_unlinkedUnit != null) { |
| return _unlinkedUnit.codeRange?.length; |
| } |
| return super.codeLength; |
| } |
| |
| @override |
| int get codeOffset { |
| if (_unlinkedUnit != null) { |
| return _unlinkedUnit.codeRange?.offset; |
| } |
| return super.codeOffset; |
| } |
| |
| @override |
| LibraryElement get enclosingElement => |
| super.enclosingElement as LibraryElement; |
| |
| @override |
| CompilationUnitElementImpl get enclosingUnit { |
| return this; |
| } |
| |
| @override |
| List<ClassElement> get enums { |
| if (_unlinkedUnit != null) { |
| _enums ??= _unlinkedUnit.enums |
| .map((e) => new EnumElementImpl.forSerialized(e, this)) |
| .toList(growable: false); |
| } |
| return _enums ?? const <ClassElement>[]; |
| } |
| |
| /** |
| * Set the enums contained in this compilation unit to the given [enums]. |
| */ |
| void set enums(List<ClassElement> enums) { |
| _assertNotResynthesized(_unlinkedUnit); |
| for (ClassElement enumDeclaration in enums) { |
| (enumDeclaration as EnumElementImpl).enclosingElement = this; |
| } |
| this._enums = enums; |
| } |
| |
| @override |
| List<FunctionElement> get functions { |
| if (_unlinkedUnit != null) { |
| _functions ??= _unlinkedUnit.executables |
| .where((e) => e.kind == UnlinkedExecutableKind.functionOrMethod) |
| .map((e) => new FunctionElementImpl.forSerialized(e, this)) |
| .toList(growable: false); |
| } |
| return _functions ?? const <FunctionElement>[]; |
| } |
| |
| /** |
| * Set the top-level functions contained in this compilation unit to the given |
| * [functions]. |
| */ |
| void set functions(List<FunctionElement> functions) { |
| for (FunctionElement function in functions) { |
| (function as FunctionElementImpl).enclosingElement = this; |
| } |
| this._functions = functions; |
| } |
| |
| @override |
| List<FunctionTypeAliasElement> get functionTypeAliases { |
| if (_unlinkedUnit != null) { |
| _typeAliases ??= _unlinkedUnit.typedefs.map((t) { |
| if (t.style == TypedefStyle.functionType) { |
| return new FunctionTypeAliasElementImpl.forSerialized(t, this); |
| } else if (t.style == TypedefStyle.genericFunctionType) { |
| return new GenericTypeAliasElementImpl.forSerialized(t, this); |
| } |
| }).toList(growable: false); |
| } |
| return _typeAliases ?? const <FunctionTypeAliasElement>[]; |
| } |
| |
| @override |
| int get hashCode => source.hashCode; |
| |
| @override |
| bool get hasLoadLibraryFunction { |
| List<FunctionElement> functions = this.functions; |
| for (int i = 0; i < functions.length; i++) { |
| if (functions[i].name == FunctionElement.LOAD_LIBRARY_NAME) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| String get identifier => source.encoding; |
| |
| @override |
| ElementKind get kind => ElementKind.COMPILATION_UNIT; |
| |
| @override |
| List<ElementAnnotation> get metadata { |
| if (_unlinkedPart != null) { |
| return _metadata ??= _buildAnnotations( |
| library.definingCompilationUnit as CompilationUnitElementImpl, |
| _unlinkedPart.annotations); |
| } |
| return super.metadata; |
| } |
| |
| @override |
| List<TopLevelVariableElement> get topLevelVariables { |
| if (_unlinkedUnit != null) { |
| if (_variables == null) { |
| _explicitTopLevelAccessors ??= |
| resynthesizerContext.buildTopLevelAccessors(); |
| _explicitTopLevelVariables ??= |
| resynthesizerContext.buildTopLevelVariables(); |
| List<TopLevelVariableElementImpl> variables = |
| <TopLevelVariableElementImpl>[]; |
| variables.addAll(_explicitTopLevelVariables.variables); |
| variables.addAll(_explicitTopLevelAccessors.implicitVariables); |
| // Ensure that getters and setters in different units use |
| // the same top-level variables. |
| (enclosingElement as LibraryElementImpl) |
| .resynthesizerContext |
| .patchTopLevelAccessors(); |
| _variables = variables; |
| _topLevelVariableReplaceMap?.forEach((from, to) { |
| int index = _variables.indexOf(from); |
| _variables[index] = to; |
| }); |
| _topLevelVariableReplaceMap = null; |
| } |
| } |
| return _variables ?? TopLevelVariableElement.EMPTY_LIST; |
| } |
| |
| /** |
| * Set the top-level variables contained in this compilation unit to the given |
| * [variables]. |
| */ |
| void set topLevelVariables(List<TopLevelVariableElement> variables) { |
| assert(!isResynthesized); |
| for (TopLevelVariableElement field in variables) { |
| (field as TopLevelVariableElementImpl).enclosingElement = this; |
| } |
| this._variables = variables; |
| } |
| |
| /** |
| * Set the function type aliases contained in this compilation unit to the |
| * given [typeAliases]. |
| */ |
| void set typeAliases(List<FunctionTypeAliasElement> typeAliases) { |
| _assertNotResynthesized(_unlinkedUnit); |
| for (FunctionTypeAliasElement typeAlias in typeAliases) { |
| (typeAlias as ElementImpl).enclosingElement = this; |
| } |
| this._typeAliases = typeAliases; |
| } |
| |
| @override |
| TypeParameterizedElementMixin get typeParameterContext => null; |
| |
| @override |
| List<ClassElement> get types { |
| if (_unlinkedUnit != null) { |
| _types ??= _unlinkedUnit.classes |
| .map((c) => new ClassElementImpl.forSerialized(c, this)) |
| .toList(growable: false); |
| } |
| return _types ?? const <ClassElement>[]; |
| } |
| |
| /** |
| * Set the types contained in this compilation unit to the given [types]. |
| */ |
| void set types(List<ClassElement> types) { |
| _assertNotResynthesized(_unlinkedUnit); |
| for (ClassElement type in types) { |
| // Another implementation of ClassElement is _DeferredClassElement, |
| // which is used to resynthesize classes lazily. We cannot cast it |
| // to ClassElementImpl, and it already can provide correct values of the |
| // 'enclosingElement' property. |
| if (type is ClassElementImpl) { |
| type.enclosingElement = this; |
| } |
| } |
| this._types = types; |
| } |
| |
| @override |
| bool operator ==(Object object) => |
| object is CompilationUnitElementImpl && source == object.source; |
| |
| @override |
| /*=T*/ accept/*<T>*/(ElementVisitor<dynamic/*=T*/ > visitor) => |
| visitor.visitCompilationUnitElement(this); |
| |
| /** |
| * This method is invoked after this unit was incrementally resolved. |
| */ |
| void afterIncrementalResolution() { |
| _offsetToElementMap.clear(); |
| } |
| |
| @override |
| void appendTo(StringBuffer buffer) { |
| if (source == null) { |
| buffer.write("{compilation unit}"); |
| } else { |
| buffer.write(source.fullName); |
| } |
| } |
| |
| @override |
| CompilationUnit computeNode() => unit; |
| |
| /** |
| * Return the annotations associated with the directive at the given [offset], |
| * or an empty list if the directive has no annotations or if there is no |
| * directive at the given offset. |
| */ |
| List<ElementAnnotation> getAnnotations(int offset) { |
| if (annotationMap == null) { |
| return const <ElementAnnotation>[]; |
| } |
| return annotationMap[offset] ?? const <ElementAnnotation>[]; |
| } |
| |
| @override |
| ElementImpl getChild(String identifier) { |
| // |
| // The casts in this method are safe because the set methods would have |
| // thrown a CCE if any of the elements in the arrays were not of the |
| // expected types. |
| // |
| for (PropertyAccessorElement accessor in accessors) { |
| PropertyAccessorElementImpl accessorImpl = accessor; |
| if (accessorImpl.identifier == identifier) { |
| return accessorImpl; |
| } |
| } |
| for (TopLevelVariableElement variable in topLevelVariables) { |
| TopLevelVariableElementImpl variableImpl = variable; |
| if (variableImpl.identifier == identifier) { |
| return variableImpl; |
| } |
| } |
| for (FunctionElement function in functions) { |
| FunctionElementImpl functionImpl = function; |
| if (functionImpl.identifier == identifier) { |
| return functionImpl; |
| } |
| } |
| for (FunctionTypeAliasElement typeAlias in functionTypeAliases) { |
| FunctionTypeAliasElementImpl typeAliasImpl = typeAlias; |
| if (typeAliasImpl.identifier == identifier) { |
| return typeAliasImpl; |
| } |
| } |
| for (ClassElement type in types) { |
| ClassElementImpl typeImpl = type; |
| if (typeImpl.name == identifier) { |
| return typeImpl; |
| } |
| } |
| for (ClassElement type in _enums) { |
| EnumElementImpl typeImpl = type; |
| if (typeImpl.identifier == identifier) { |
| return typeImpl; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| Element getElementAt(int offset) { |
| if (_offsetToElementMap.isEmpty) { |
| accept(new _BuildOffsetToElementMap(_offsetToElementMap)); |
| } |
| return _offsetToElementMap[offset]; |
| } |
| |
| @override |
| ClassElement getEnum(String enumName) { |
| for (ClassElement enumDeclaration in _enums) { |
| if (enumDeclaration.name == enumName) { |
| return enumDeclaration; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| ClassElement getType(String className) { |
| for (ClassElement type in types) { |
| if (type.name == className) { |
| return type; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Replace the given [from] top-level variable with [to] in this compilation unit. |
| */ |
| void replaceTopLevelVariable( |
| TopLevelVariableElement from, TopLevelVariableElement to) { |
| if (_unlinkedUnit != null) { |
| // Getters and setter in different units should be patched to use the |
| // same variables before these variables were asked and returned. |
| assert(_variables == null); |
| _topLevelVariableReplaceMap ??= |
| <TopLevelVariableElement, TopLevelVariableElement>{}; |
| _topLevelVariableReplaceMap[from] = to; |
| } else { |
| int index = _variables.indexOf(from); |
| _variables[index] = to; |
| } |
| } |
| |
| /** |
| * Set the annotations associated with the directive at the given [offset] to |
| * the given list of [annotations]. |
| */ |
| void setAnnotations(int offset, List<ElementAnnotation> annotations) { |
| annotationMap ??= new HashMap<int, List<ElementAnnotation>>(); |
| annotationMap[offset] = annotations; |
| } |
| |
| @override |
| void visitChildren(ElementVisitor visitor) { |
| super.visitChildren(visitor); |
| safelyVisitChildren(accessors, visitor); |
| safelyVisitChildren(enums, visitor); |
| safelyVisitChildren(functions, visitor); |
| safelyVisitChildren(functionTypeAliases, visitor); |
| safelyVisitChildren(types, visitor); |
| safelyVisitChildren(topLevelVariables, visitor); |
| } |
| } |
| |
| /** |
| * A [FieldElement] for a 'const' or 'final' field that has an initializer. |
| * |
| * TODO(paulberry): we should rename this class to reflect the fact that it's |
| * used for both const and final fields. However, we shouldn't do so until |
| * we've created an API for reading the values of constants; until that API is |
| * available, clients are likely to read constant values by casting to |
| * ConstFieldElementImpl, so it would be a breaking change to rename this |
| * class. |
| */ |
| class ConstFieldElementImpl extends FieldElementImpl with ConstVariableElement { |
| /** |
| * Initialize a newly created synthetic field element to have the given |
| * [name] and [offset]. |
| */ |
| ConstFieldElementImpl(String name, int offset) : super(name, offset); |
| |
| /** |
| * Initialize a newly created field element to have the given [name]. |
| */ |
| ConstFieldElementImpl.forNode(Identifier name) : super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| ConstFieldElementImpl.forSerialized( |
| UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement) |
| : super.forSerialized(unlinkedVariable, enclosingElement); |
| } |
| |
| /** |
| * A field element representing an enum constant. |
| */ |
| class ConstFieldElementImpl_EnumValue extends ConstFieldElementImpl_ofEnum { |
| final UnlinkedEnumValue _unlinkedEnumValue; |
| final int _index; |
| |
| ConstFieldElementImpl_EnumValue( |
| EnumElementImpl enumElement, this._unlinkedEnumValue, this._index) |
| : super(enumElement); |
| |
| @override |
| String get documentationComment { |
| if (_unlinkedEnumValue != null) { |
| return _unlinkedEnumValue?.documentationComment?.text; |
| } |
| return super.documentationComment; |
| } |
| |
| @override |
| EvaluationResultImpl get evaluationResult { |
| if (_evaluationResult == null) { |
| Map<String, DartObjectImpl> fieldMap = <String, DartObjectImpl>{ |
| name: new DartObjectImpl( |
| context.typeProvider.intType, new IntState(_index)) |
| }; |
| DartObjectImpl value = |
| new DartObjectImpl(type, new GenericState(fieldMap)); |
| _evaluationResult = new EvaluationResultImpl(value); |
| } |
| return _evaluationResult; |
| } |
| |
| @override |
| String get name { |
| if (_unlinkedEnumValue != null) { |
| return _unlinkedEnumValue.name; |
| } |
| return super.name; |
| } |
| |
| @override |
| int get nameOffset { |
| int offset = super.nameOffset; |
| if (offset == -1 && _unlinkedEnumValue != null) { |
| return _unlinkedEnumValue.nameOffset; |
| } |
| return offset; |
| } |
| |
| @override |
| InterfaceType get type => _enum.type; |
| } |
| |
| /** |
| * The synthetic `values` field of an enum. |
| */ |
| class ConstFieldElementImpl_EnumValues extends ConstFieldElementImpl_ofEnum { |
| ConstFieldElementImpl_EnumValues(EnumElementImpl enumElement) |
| : super(enumElement) { |
| isSynthetic = true; |
| } |
| |
| @override |
| EvaluationResultImpl get evaluationResult { |
| if (_evaluationResult == null) { |
| List<DartObjectImpl> constantValues = <DartObjectImpl>[]; |
| for (FieldElement field in _enum.fields) { |
| if (field is ConstFieldElementImpl_EnumValue) { |
| constantValues.add(field.evaluationResult.value); |
| } |
| } |
| _evaluationResult = new EvaluationResultImpl( |
| new DartObjectImpl(type, new ListState(constantValues))); |
| } |
| return _evaluationResult; |
| } |
| |
| @override |
| String get name => 'values'; |
| |
| @override |
| InterfaceType get type { |
| if (_type == null) { |
| InterfaceType listType = context.typeProvider.listType; |
| return _type = listType.instantiate(<DartType>[_enum.type]); |
| } |
| return _type; |
| } |
| } |
| |
| /** |
| * An abstract constant field of an enum. |
| */ |
| abstract class ConstFieldElementImpl_ofEnum extends ConstFieldElementImpl { |
| final EnumElementImpl _enum; |
| |
| ConstFieldElementImpl_ofEnum(this._enum) : super(null, -1) { |
| enclosingElement = _enum; |
| } |
| |
| @override |
| void set evaluationResult(_) { |
| assert(false); |
| } |
| |
| @override |
| bool get isConst => true; |
| |
| @override |
| void set isConst(bool isConst) { |
| assert(false); |
| } |
| |
| @override |
| void set isFinal(bool isFinal) { |
| assert(false); |
| } |
| |
| @override |
| bool get isStatic => true; |
| |
| @override |
| void set isStatic(bool isStatic) { |
| assert(false); |
| } |
| |
| void set type(DartType type) { |
| assert(false); |
| } |
| } |
| |
| /** |
| * A [LocalVariableElement] for a local 'const' variable that has an |
| * initializer. |
| */ |
| class ConstLocalVariableElementImpl extends LocalVariableElementImpl |
| with ConstVariableElement { |
| /** |
| * Initialize a newly created local variable element to have the given [name] |
| * and [offset]. |
| */ |
| ConstLocalVariableElementImpl(String name, int offset) : super(name, offset); |
| |
| /** |
| * Initialize a newly created local variable element to have the given [name]. |
| */ |
| ConstLocalVariableElementImpl.forNode(Identifier name) : super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| ConstLocalVariableElementImpl.forSerialized(UnlinkedVariable unlinkedVariable, |
| ExecutableElementImpl enclosingExecutable) |
| : super.forSerialized(unlinkedVariable, enclosingExecutable); |
| } |
| |
| /** |
| * A concrete implementation of a [ConstructorElement]. |
| */ |
| class ConstructorElementImpl extends ExecutableElementImpl |
| implements ConstructorElement { |
| /** |
| * The constructor to which this constructor is redirecting. |
| */ |
| ConstructorElement _redirectedConstructor; |
| |
| /** |
| * The initializers for this constructor (used for evaluating constant |
| * instance creation expressions). |
| */ |
| List<ConstructorInitializer> _constantInitializers; |
| |
| /** |
| * The offset of the `.` before this constructor name or `null` if not named. |
| */ |
| int _periodOffset; |
| |
| /** |
| * Return the offset of the character immediately following the last character |
| * of this constructor's name, or `null` if not named. |
| */ |
| int _nameEnd; |
| |
| /** |
| * True if this constructor has been found by constant evaluation to be free |
| * of redirect cycles, and is thus safe to evaluate. |
| */ |
| bool _isCycleFree = false; |
| |
| /** |
| * Initialize a newly created constructor element to have the given [name] and |
| * [offset]. |
| */ |
| ConstructorElementImpl(String name, int offset) : super(name, offset); |
| |
| /** |
| * Initialize a newly created constructor element to have the given [name]. |
| */ |
| ConstructorElementImpl.forNode(Identifier name) : super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| ConstructorElementImpl.forSerialized( |
| UnlinkedExecutable serializedExecutable, ClassElementImpl enclosingClass) |
| : super.forSerialized(serializedExecutable, enclosingClass); |
| |
| /** |
| * Return the constant initializers for this element, which will be empty if |
| * there are no initializers, or `null` if there was an error in the source. |
| */ |
| List<ConstructorInitializer> get constantInitializers { |
| if (serializedExecutable != null && _constantInitializers == null) { |
| _constantInitializers ??= serializedExecutable.constantInitializers |
| .map((i) => _buildConstructorInitializer(i)) |
| .toList(growable: false); |
| } |
| return _constantInitializers; |
| } |
| |
| void set constantInitializers( |
| List<ConstructorInitializer> constantInitializers) { |
| _assertNotResynthesized(serializedExecutable); |
| _constantInitializers = constantInitializers; |
| } |
| |
| @override |
| ClassElementImpl get enclosingElement => |
| super.enclosingElement as ClassElementImpl; |
| |
| @override |
| TypeParameterizedElementMixin get enclosingTypeParameterContext => |
| super.enclosingElement as ClassElementImpl; |
| |
| /** |
| * Set whether this constructor represents a factory method. |
| */ |
| void set factory(bool isFactory) { |
| _assertNotResynthesized(serializedExecutable); |
| setModifier(Modifier.FACTORY, isFactory); |
| } |
| |
| @override |
| bool get isConst { |
| if (serializedExecutable != null) { |
| return serializedExecutable.isConst; |
| } |
| return hasModifier(Modifier.CONST); |
| } |
| |
| /** |
| * Set whether this constructor represents a 'const' constructor. |
| */ |
| void set isConst(bool isConst) { |
| _assertNotResynthesized(serializedExecutable); |
| setModifier(Modifier.CONST, isConst); |
| } |
| |
| bool get isCycleFree { |
| if (serializedExecutable != null) { |
| return serializedExecutable.isConst && |
| !enclosingUnit.resynthesizerContext |
| .isInConstCycle(serializedExecutable.constCycleSlot); |
| } |
| return _isCycleFree; |
| } |
| |
| void set isCycleFree(bool isCycleFree) { |
| // This property is updated in ConstantEvaluationEngine even for |
| // resynthesized constructors, so we don't have the usual assert here. |
| _isCycleFree = isCycleFree; |
| } |
| |
| @override |
| bool get isDefaultConstructor { |
| // unnamed |
| String name = this.name; |
| if (name != null && name.length != 0) { |
| return false; |
| } |
| // no required parameters |
| for (ParameterElement parameter in parameters) { |
| if (parameter.parameterKind == ParameterKind.REQUIRED) { |
| return false; |
| } |
| } |
| // OK, can be used as default constructor |
| return true; |
| } |
| |
| @override |
| bool get isFactory { |
| if (serializedExecutable != null) { |
| return serializedExecutable.isFactory; |
| } |
| return hasModifier(Modifier.FACTORY); |
| } |
| |
| @override |
| bool get isStatic => false; |
| |
| @override |
| ElementKind get kind => ElementKind.CONSTRUCTOR; |
| |
| @override |
| int get nameEnd { |
| if (serializedExecutable != null) { |
| if (serializedExecutable.name.isNotEmpty) { |
| return serializedExecutable.nameEnd; |
| } else { |
| return serializedExecutable.nameOffset + enclosingElement.name.length; |
| } |
| } |
| return _nameEnd; |
| } |
| |
| void set nameEnd(int nameEnd) { |
| _assertNotResynthesized(serializedExecutable); |
| _nameEnd = nameEnd; |
| } |
| |
| @override |
| int get periodOffset { |
| if (serializedExecutable != null) { |
| if (serializedExecutable.name.isNotEmpty) { |
| return serializedExecutable.periodOffset; |
| } |
| } |
| return _periodOffset; |
| } |
| |
| void set periodOffset(int periodOffset) { |
| _assertNotResynthesized(serializedExecutable); |
| _periodOffset = periodOffset; |
| } |
| |
| @override |
| ConstructorElement get redirectedConstructor { |
| if (serializedExecutable != null && _redirectedConstructor == null) { |
| if (serializedExecutable.isRedirectedConstructor) { |
| if (serializedExecutable.isFactory) { |
| _redirectedConstructor = enclosingUnit.resynthesizerContext |
| .resolveConstructorRef( |
| enclosingElement, serializedExecutable.redirectedConstructor); |
| } else { |
| _redirectedConstructor = enclosingElement.getNamedConstructor( |
| serializedExecutable.redirectedConstructorName); |
| } |
| } else { |
| return null; |
| } |
| } |
| return _redirectedConstructor; |
| } |
| |
| void set redirectedConstructor(ConstructorElement redirectedConstructor) { |
| _assertNotResynthesized(serializedExecutable); |
| _redirectedConstructor = redirectedConstructor; |
| } |
| |
| @override |
| DartType get returnType => enclosingElement.type; |
| |
| void set returnType(DartType returnType) { |
| assert(false); |
| } |
| |
| @override |
| FunctionType get type { |
| return _type ??= new FunctionTypeImpl(this); |
| } |
| |
| void set type(FunctionType type) { |
| assert(false); |
| } |
| |
| @override |
| /*=T*/ accept/*<T>*/(ElementVisitor<dynamic/*=T*/ > visitor) => |
| visitor.visitConstructorElement(this); |
| |
| @override |
| void appendTo(StringBuffer buffer) { |
| if (enclosingElement == null) { |
| String message; |
| String name = displayName; |
| if (name != null && !name.isEmpty) { |
| message = |
| 'Found constructor element named $name with no enclosing element'; |
| } else { |
| message = 'Found unnamed constructor element with no enclosing element'; |
| } |
| AnalysisEngine.instance.logger.logError(message); |
| buffer.write('<unknown class>'); |
| } else { |
| buffer.write(enclosingElement.displayName); |
| } |
| String name = displayName; |
| if (name != null && !name.isEmpty) { |
| buffer.write("."); |
| buffer.write(name); |
| } |
| super.appendTo(buffer); |
| } |
| |
| @override |
| ConstructorDeclaration computeNode() => |
| getNodeMatching((node) => node is ConstructorDeclaration); |
| |
| /** |
| * Resynthesize the AST for the given serialized constructor initializer. |
| */ |
| ConstructorInitializer _buildConstructorInitializer( |
| UnlinkedConstructorInitializer serialized) { |
| UnlinkedConstructorInitializerKind kind = serialized.kind; |
| String name = serialized.name; |
| List<Expression> arguments = <Expression>[]; |
| { |
| int numArguments = serialized.arguments.length; |
| int numNames = serialized.argumentNames.length; |
| for (int i = 0; i < numArguments; i++) { |
| Expression expression = enclosingUnit.resynthesizerContext |
| .buildExpression(this, serialized.arguments[i]); |
| int nameIndex = numNames + i - numArguments; |
| if (nameIndex >= 0) { |
| expression = AstTestFactory.namedExpression2( |
| serialized.argumentNames[nameIndex], expression); |
| } |
| arguments.add(expression); |
| } |
| } |
| switch (kind) { |
| case UnlinkedConstructorInitializerKind.field: |
| ConstructorFieldInitializer initializer = |
| AstTestFactory.constructorFieldInitializer( |
| false, |
| name, |
| enclosingUnit.resynthesizerContext |
| .buildExpression(this, serialized.expression)); |
| initializer.fieldName.staticElement = enclosingElement.getField(name); |
| return initializer; |
| case UnlinkedConstructorInitializerKind.assertInvocation: |
| return AstTestFactory.assertInitializer( |
| arguments[0], arguments.length > 1 ? arguments[1] : null); |
| case UnlinkedConstructorInitializerKind.superInvocation: |
| SuperConstructorInvocation initializer = |
| AstTestFactory.superConstructorInvocation2( |
| name.isNotEmpty ? name : null, arguments); |
| ClassElement superElement = enclosingElement.supertype.element; |
| ConstructorElement element = name.isEmpty |
| ? superElement.unnamedConstructor |
| : superElement.getNamedConstructor(name); |
| initializer.staticElement = element; |
| initializer.constructorName?.staticElement = element; |
| return initializer; |
| case UnlinkedConstructorInitializerKind.thisInvocation: |
| RedirectingConstructorInvocation initializer = |
| AstTestFactory.redirectingConstructorInvocation2( |
| name.isNotEmpty ? name : null, arguments); |
| ConstructorElement element = name.isEmpty |
| ? enclosingElement.unnamedConstructor |
| : enclosingElement.getNamedConstructor(name); |
| initializer.staticElement = element; |
| initializer.constructorName?.staticElement = element; |
| return initializer; |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * A [TopLevelVariableElement] for a top-level 'const' variable that has an |
| * initializer. |
| */ |
| class ConstTopLevelVariableElementImpl extends TopLevelVariableElementImpl |
| with ConstVariableElement { |
| /** |
| * Initialize a newly created synthetic top-level variable element to have the |
| * given [name] and [offset]. |
| */ |
| ConstTopLevelVariableElementImpl(String name, int offset) |
| : super(name, offset); |
| |
| /** |
| * Initialize a newly created top-level variable element to have the given |
| * [name]. |
| */ |
| ConstTopLevelVariableElementImpl.forNode(Identifier name) |
| : super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| ConstTopLevelVariableElementImpl.forSerialized( |
| UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement) |
| : super.forSerialized(unlinkedVariable, enclosingElement); |
| } |
| |
| /** |
| * Mixin used by elements that represent constant variables and have |
| * initializers. |
| * |
| * Note that in correct Dart code, all constant variables must have |
| * initializers. However, analyzer also needs to handle incorrect Dart code, |
| * in which case there might be some constant variables that lack initializers. |
| * This interface is only used for constant variables that have initializers. |
| * |
| * This class is not intended to be part of the public API for analyzer. |
| */ |
| abstract class ConstVariableElement |
| implements ElementImpl, ConstantEvaluationTarget { |
| /** |
| * If this element represents a constant variable, and it has an initializer, |
| * a copy of the initializer for the constant. Otherwise `null`. |
| * |
| * Note that in correct Dart code, all constant variables must have |
| * initializers. However, analyzer also needs to handle incorrect Dart code, |
| * in which case there might be some constant variables that lack |
| * initializers. |
| */ |
| Expression _constantInitializer; |
| |
| EvaluationResultImpl _evaluationResult; |
| |
| Expression get constantInitializer { |
| if (_constantInitializer == null && _unlinkedConst != null) { |
| _constantInitializer = enclosingUnit.resynthesizerContext |
| .buildExpression(this, _unlinkedConst); |
| } |
| return _constantInitializer; |
| } |
| |
| void set constantInitializer(Expression constantInitializer) { |
| _assertNotResynthesized(_unlinkedConst); |
| _constantInitializer = constantInitializer; |
| } |
| |
| EvaluationResultImpl get evaluationResult => _evaluationResult; |
| |
| void set evaluationResult(EvaluationResultImpl evaluationResult) { |
| _evaluationResult = evaluationResult; |
| } |
| |
| /** |
| * If this element is resynthesized from the summary, return the unlinked |
| * initializer, otherwise return `null`. |
| */ |
| UnlinkedExpr get _unlinkedConst; |
| |
| /** |
| * Return a representation of the value of this variable, forcing the value |
| * to be computed if it had not previously been computed, or `null` if either |
| * this variable was not declared with the 'const' modifier or if the value of |
| * this variable could not be computed because of errors. |
| */ |
| DartObject computeConstantValue() { |
| if (evaluationResult == null) { |
| context?.computeResult(this, CONSTANT_VALUE); |
| } |
| return evaluationResult?.value; |
| } |
| } |
| |
| /** |
| * A [FieldFormalParameterElementImpl] for parameters that have an initializer. |
| */ |
| class DefaultFieldFormalParameterElementImpl |
| extends FieldFormalParameterElementImpl with ConstVariableElement { |
| /** |
| * Initialize a newly created parameter element to have the given [name] and |
| * [nameOffset]. |
| */ |
| DefaultFieldFormalParameterElementImpl(String name, int nameOffset) |
| : super(name, nameOffset); |
| |
| /** |
| * Initialize a newly created parameter element to have the given [name]. |
| */ |
| DefaultFieldFormalParameterElementImpl.forNode(Identifier name) |
| : super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| DefaultFieldFormalParameterElementImpl.forSerialized( |
| UnlinkedParam unlinkedParam, ElementImpl enclosingElement) |
| : super.forSerialized(unlinkedParam, enclosingElement); |
| } |
| |
| /** |
| * A [ParameterElement] for parameters that have an initializer. |
| */ |
| class DefaultParameterElementImpl extends ParameterElementImpl |
| with ConstVariableElement { |
| /** |
| * Initialize a newly created parameter element to have the given [name] and |
| * [nameOffset]. |
| */ |
| DefaultParameterElementImpl(String name, int nameOffset) |
| : super(name, nameOffset); |
| |
| /** |
| * Initialize a newly created parameter element to have the given [name]. |
| */ |
| DefaultParameterElementImpl.forNode(Identifier name) : super.forNode(name); |
| |
| /** |
| * Initialize using the given serialized information. |
| */ |
| DefaultParameterElementImpl.forSerialized( |
| UnlinkedParam unlinkedParam, ElementImpl enclosingElement) |
| : super.forSerialized(unlinkedParam, enclosingElement); |
| |
| @override |
| DefaultFormalParameter computeNode() => |
| getNodeMatching((node) => node is DefaultFormalParameter); |
| } |
| |
| /** |
| * The synthetic element representing the declaration of the type `dynamic`. |
| */ |
| class DynamicElementImpl extends ElementImpl implements TypeDefiningElement { |
| /** |
| * Return the unique instance of this class. |
| */ |
| static DynamicElementImpl get instance => |
| DynamicTypeImpl.instance.element as DynamicElementImpl; |
| |
| @override |
| DynamicTypeImpl type; |
| |
| /** |
| * Initialize a newly created instance of this class. Instances of this class |
| * should <b>not</b> be created except as part of creating the type associated |
| * with this element. The single instance of this class should be accessed |
| * through the method [instance]. |
| */ |
| DynamicElementImpl() : super(Keyword.DYNAMIC.syntax, -1) { |
| setModifier(Modifier.SYNTHETIC, true); |
| } |
| |
| @override |
| ElementKind get kind => ElementKind.DYNAMIC; |
| |
| @override |
| /*=T*/ accept/*<T>*/(ElementVisitor<dynamic/*=T*/ > visitor) => null; |
| } |
| |
| /** |
| * A concrete implementation of an [ElementAnnotation]. |
| */ |
| class ElementAnnotationImpl implements ElementAnnotation { |
| /** |
| * The name of the top-level variable used to mark a method parameter as |
| * covariant. |
| */ |
| static String _COVARIANT_VARIABLE_NAME = "checked"; |
| |
| /** |
| * The name of the class used to mark an element as being deprecated. |
| */ |
| static String _DEPRECATED_CLASS_NAME = "Deprecated"; |
| |
| /** |
| * The name of the top-level variable used to mark an element as being |
| * deprecated. |
| */ |
| static String _DEPRECATED_VARIABLE_NAME = "deprecated"; |
| |
| /** |
| * The name of the top-level variable used to mark a method as being a |
| * factory. |
| */ |
| static String _FACTORY_VARIABLE_NAME = "factory"; |
| |
| /** |
| * The name of the top-level variable used to mark a class and its subclasses |
| * as being immutable. |
| */ |
| static String _IMMUTABLE_VARIABLE_NAME = "immutable"; |
| |
| /** |
| * The name of the class used to JS annotate an element. |
| */ |
| static String _JS_CLASS_NAME = "JS"; |
| |
| /** |
| * The name of `js` library, used to define JS annotations. |
| */ |
| static String _JS_LIB_NAME = "js"; |
| |
| /** |
| * The name of `meta` library, used to define analysis annotations. |
| */ |
| static String _META_LIB_NAME = "meta"; |
| |
| /** |
| * The name of the top-level variable used to mark a method as requiring |
| * overriders to call super. |
| */ |
| static String _MUST_CALL_SUPER_VARIABLE_NAME = "mustCallSuper"; |
| |
| /** |
| * The name of the top-level variable used to mark a method as being expected |
| * to override an inherited method. |
| */ |
| static String _OVERRIDE_VARIABLE_NAME = "override"; |
| |
| /** |
| * The name of the top-level variable used to mark a method as being |
| * protected. |
| */ |
| static String _PROTECTED_VARIABLE_NAME = "protected"; |
| |
| /** |
| * The name of the top-level variable used to mark a class as implementing a |
| * proxy object. |
| */ |
| static String PROXY_VARIABLE_NAME = "proxy"; |
| |
| /** |
| * The name of the class used to mark a parameter as being required. |
| */ |
| static String _REQUIRED_CLASS_NAME = "Required"; |
| |
| /** |
| * The name of the top-level variable used to mark a parameter as being |
| * required. |
| */ |
| static String _REQUIRED_VARIABLE_NAME = "required"; |
| |
| /** |
| * The element representing the field, variable, or constructor being used as |
| * an annotation. |
| */ |
| Element element; |
| |
| /** |
| * The compilation unit in which this annotation appears. |
| */ |
| CompilationUnitElementImpl compilationUnit; |
| |
| /** |
| * The AST of the annotation itself, cloned from the resolved AST for the |
| * source code. |
| */ |
| Annotation annotationAst; |
| |
| /** |
| * The result of evaluating this annotation as a compile-time constant |
| * expression, or `null` if the compilation unit containing the variable has |
| * not been resolved. |
| */ |
| EvaluationResultImpl evaluationResult; |
| |
| /** |
| * Initialize a newly created annotation. The given [compilationUnit] is the |
| * compilation unit in which the annotation appears. |
| */ |
| ElementAnnotationImpl(this.compilationUnit); |
| |
| @override |
| DartObject get constantValue => evaluationResult?.value; |
| |
| @override |
| AnalysisContext get context => compilationUnit.library.context; |
| |
| /** |
| * Return `true` if this annotation marks the associated parameter as being |
| * covariant, meaning it is allowed to have a narrower type in an override. |
| */ |
| bool get isCovariant => |
| element is PropertyAccessorElement && |
| element.name == _COVARIANT_VARIABLE_NAME && |
| element.library?.name == _META_LIB_NAME; |
| |
| @override |
| bool get isDeprecated { |
| if (element?.library?.isDartCore == true) { |
| if (element is ConstructorElement) { |
| return element.enclosingElement.name == _DEPRECATED_CLASS_NAME; |
| } else if (element is PropertyAccessorElement) { |
| return element.name == _DEPRECATED_VARIABLE_NAME; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool get isFactory => |
| element is PropertyAccessorElement && |
| element.name == _FACTORY_VARIABLE_NAME && |
| element.library?.name == _META_LIB_NAME; |
| |
| @override |
| bool get isImmutable => |
| element is PropertyAccessorElement && |
| element.name == _IMMUTABLE_VARIABLE_NAME && |
| element.library?.name == _META_LIB_NAME; |
| |
| @override |
| bool get isJS => |
| element is ConstructorElement && |
| element.enclosingElement.name == _JS_CLASS_NAME && |
| element.library?.name == _JS_LIB_NAME; |
| |
| @override |
| bool get isMustCallSuper => |
| element is PropertyAccessorElement && |
| element.name == _MUST_CALL_SUPER_VARIABLE_NAME && |
| element.library?.name == _META_LIB_NAME; |
| |
| @override |
| bool get isOverride => |
| element is PropertyAccessorElement && |
| element.name == _OVERRIDE_VARIABLE_NAME && |
| element.library?.isDartCore == true; |
| |
| @override |
| bool get isProtected => |
| element is PropertyAccessorElement && |
| element.name == _PROTECTED_VARIABLE_NAME && |
| element.library?.name == _META_LIB_NAME; |
| |
| @override |
| bool get isProxy => |
| element is PropertyAccessorElement && |
| element.name == PROXY_VARIABLE_NAME && |
| element.library?.isDartCore == true; |
| |
| @override |
| bool get isRequired => |
| element is ConstructorElement && |
| element.enclosingElement.name == _REQUIRED_CLASS_NAME && |
| element.library?.name == _META_LIB_NAME || |
| element is PropertyAccessorElement && |
| element.name == _REQUIRED_VARIABLE_NAME && |
| element.library?.name == _META_LIB_NAME; |
| |
| /** |
| * Get the library containing this annotation. |
| */ |
| Source get librarySource => compilationUnit.librarySource; |
| |
| @override |
| Source get source => compilationUnit.source; |
| |
| @override |
| DartObject computeConstantValue() { |
| if (evaluationResult == null) { |
| context?.computeResult(this, CONSTANT_VALUE); |
| } |
| return constantValue; |
| } |
| |
| @override |
| String toSource() => annotationAst.toSource(); |
| |
| @override |
| String toString() => '@$element'; |
| } |
| |
| /** |
| * A base class for concrete implementations of an [Element]. |
| */ |
| abstract class ElementImpl implements Element { |
| /** |
| * An Unicode right arrow. |
| */ |
| static final String RIGHT_ARROW = " \u2192 "; |
| |
| static int _NEXT_ID = 0; |
| |
| final int id = _NEXT_ID++; |
| |
| /** |
| * The enclosing element of this element, or `null` if this element is at the |
| * root of the element structure. |
| */ |
| ElementImpl _enclosingElement; |
| |
| /** |
| * The name of this element. |
| */ |
| String _name; |
| |
| /** |
| * The offset of the name of this element in the file that contains the |
| * declaration of this element. |
| */ |
| int _nameOffset = 0; |
| |
| /** |
| * A bit-encoded form of the modifiers associated with this element. |
| */ |
| int _modifiers = 0; |
| |
| /** |
| * A list containing all of the metadata associated with this element. |
| */ |
| List<ElementAnnotation> _metadata; |
| |
| /** |
| * A cached copy of the calculated hashCode for this element. |
| */ |
| int _cachedHashCode; |
| |
| /** |
| * A cached copy of the calculated location for this element. |
| */ |
| ElementLocation _cachedLocation; |
| |
| /** |
| * The documentation comment for this element. |
| */ |
| String _docComment; |
| |
| /** |
| * The offset of the beginning of the element's code in the file that contains |
| * the element, or `null` if the element is synthetic. |
| */ |
| int _codeOffset; |
| |
| /** |
| * The length of the element's code, or `null` if the element is synthetic. |
| */ |
| int _codeLength; |
| |
| /** |
| * Initialize a newly created element to have the given [name] at the given |
| * [_nameOffset]. |
| */ |
| ElementImpl(String name, this._nameOffset) { |
| this._name = StringUtilities.intern(name); |
| } |
| |
| /** |
| * Initialize a newly created element to have the given [name]. |
| */ |
| ElementImpl.forNode(Identifier name) |
| : this(name == null ? "" : name.name, name == null ? -1 : name.offset); |
| |
| /** |
| * Initialize from serialized information. |
| */ |
| ElementImpl.forSerialized(this._enclosingElement); |
| |
| /** |
| * The length of the element's code, or `null` if the element is synthetic. |
| */ |
| int get codeLength => _codeLength; |
| |
| /** |
| * The offset of the beginning of the element's code in the file that contains |
| * the element, or `null` if the element is synthetic. |
| */ |
| int get codeOffset => _codeOffset; |
| |
| @override |
| AnalysisContext get context { |
| if (_enclosingElement == null) { |
| return null; |
| } |
| return _enclosingElement.context; |
| } |
| |
| @override |
| String get displayName => _name; |
| |
| @override |
| String get documentationComment => _docComment; |
| |
| /** |
| * The documentation comment source for this element. |
| */ |
| void set documentationComment(String doc) { |
| assert(!isResynthesized); |
| _docComment = doc?.replaceAll('\r\n', '\n'); |
| } |
| |
| @override |
| Element get enclosingElement => _enclosingElement; |
| |
| /** |
| * Set the enclosing element of this element to the given [element]. |
| * |
| * Throws [FrozenHashCodeException] if the hashCode can't be changed. |
| */ |
| void set enclosingElement(Element element) { |
| _enclosingElement = element as ElementImpl; |
| } |
| |
| /** |
| * Return the enclosing unit element (which might be the same as `this`), or |
| * `null` if this element is not contained in any compilation unit. |
| */ |
| CompilationUnitElementImpl get enclosingUnit { |
| return _enclosingElement?.enclosingUnit; |
| } |
| |
| @override |
| int get hashCode { |
| // TODO: We might want to re-visit this optimization in the future. |
| // We cache the hash code value as this is a very frequently called method. |
| if (_cachedHashCode == null) { |
| _cachedHashCode = location.hashCode; |
| } |
| return _cachedHashCode; |
| } |
| |
| /** |
| * Return an identifier that uniquely identifies this element among the |
| * children of this element's parent. |
| */ |
| String get identifier => name; |
| |
| @override |
| bool get isDeprecated { |
| for (ElementAnnotation annotation in metadata) { |
| if (annotation.isDeprecated) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool get isFactory { |
| for (ElementAnnotation annotation in metadata) { |
| if (annotation.isFactory) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool get isJS { |
| for (ElementAnnotation annotation in metadata) { |
| if (annotation.isJS) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool get isOverride { |
| for (ElementAnnotation annotation in metadata) { |
| if (annotation.isOverride) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool get isPrivate { |
| String name = displayName; |
| if (name == null) { |
| return true; |
| } |
| return Identifier.isPrivateName(name); |
| } |
| |
| @override |
| bool get isProtected { |
| for (ElementAnnotation annotation in metadata) { |
| if (annotation.isProtected) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool get isPublic => !isPrivate; |
| |
| @override |
| bool get isRequired { |
| for (ElementAnnotation annotation in metadata) { |
| if (annotation.isRequired) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Return `true` if this element is resynthesized from a summary. |
| */ |
| bool get isResynthesized => enclosingUnit?.resynthesizerContext != null; |
| |
| @override |
| bool get isSynthetic => hasModifier(Modifier.SYNTHETIC); |
| |
| /** |
| * Set whether this element is synthetic. |
| */ |
| void set isSynthetic(bool isSynthetic) { |
| setModifier(Modifier.SYNTHETIC, isSynthetic); |
| } |
| |
| @override |
| LibraryElement get library => |
| getAncestor((element) => element is LibraryElement); |
| |
| @override |
| Source get librarySource => library?.source; |
| |
| @override |
| ElementLocation get location { |
| if (_cachedLocation == null) { |
| if (library == null) { |
| return new ElementLocationImpl.con1(this); |
| } |
| _cachedLocation = new ElementLocationImpl.con1(this); |
| } |
| return _cachedLocation; |
| } |
| |
| List<ElementAnnotation> get metadata { |
| return _metadata ?? const <ElementAnnotation>[]; |
| } |
| |
| void set metadata(List<ElementAnnotation> metadata) { |
| assert(!isResynthesized); |
| _metadata = metadata; |
| } |
| |
| @override |
| String get name => _name; |
| |
| /** |
| * Changes the name of this element. |
| * |
| * Throws [FrozenHashCodeException] if the hashCode can't be changed. |
| */ |
| void set name(String name) { |
| this._name = name; |
| } |
| |
| @override |
| int get nameLength => displayName != null ? displayName.length : 0; |
| |
| @override |
| int get nameOffset => _nameOffset; |
| |
| /** |
| * Sets the offset of the name of this element in the file that contains the |
| * declaration of this element. |
| * |
| * Throws [FrozenHashCodeException] if the hashCode can't be changed. |
| */ |
| void set nameOffset(int offset) { |
| _nameOffset = offset; |
| } |
| |
| @override |
| Source get source { |
| if (_enclosingElement == null) { |
| return null; |
| } |
| return _enclosingElement.source; |
| } |
| |
| /** |
| * Return the context to resolve type parameters in, or `null` if neither this |
| * element nor any of its ancestors is of a kind that can declare type |
| * parameters. |
| */ |
| TypeParameterizedElementMixin get typeParameterContext { |
| return _enclosingElement?.typeParameterContext; |
| } |
| |
| @override |
| CompilationUnit get unit => context.resolveCompilationUnit(source, library); |
| |
| @override |
| bool operator ==(Object object) { |
| if (identical(this, object)) { |
| return true; |
| } |
| return object is Element && |
| object.kind == kind && |
| object.location == location; |
| } |
| |
| /** |
| * Append to the given [buffer] a comma-separated list of the names of the |
| * types of this element and every enclosing element. |
| */ |
| void appendPathTo(StringBuffer buffer) { |
| Element element = this; |
| while (element != null) { |
| if (element != this) { |
| buffer.write(', '); |
| } |
| buffer.write(element.runtimeType); |
| String name = element.name; |
| if (name != null) { |
| buffer.write(' ('); |
| buffer.write(name); |
| buffer.write(')'); |
| } |
| element = element.enclosingElement; |
| } |
| } |
| |
| /** |
| * Append a textual representation of this element to the given [buffer]. |
| */ |
| void appendTo(StringBuffer buffer) { |
| if (_name == null) { |
| buffer.write("<unnamed "); |
| buffer.write(runtimeType.toString()); |
| buffer.write(">"); |
| } else { |
| buffer.write(_name); |
| } |
| } |
| |
| @override |
| String computeDocumentationComment() => documentationComment; |
| |
| @override |
| AstNode computeNode() => getNodeMatching((node) => node is AstNode); |
| |
| /** |
| * Set this element as the enclosing element for given [element]. |
| */ |
| void encloseElement(ElementImpl element) { |
| element.enclosingElement = this; |
| } |
| |
| @override |
| Element/*=E*/ getAncestor/*<E extends Element >*/( |
| Predicate<Element> predicate) { |
| Element ancestor = _enclosingElement; |
| while (ancestor != null && !predicate(ancestor)) { |
| ancestor = ancestor.enclosingElement; |
| } |
| return ancestor as Element/*=E*/; |
| } |
| |
| /** |
| * Return the child of this element that is uniquely identified by the given |
| * [identifier], or `null` if there is no such child. |
| */ |
| ElementImpl getChild(String identifier) => null; |
| |
| @override |
| String getExtendedDisplayName(String shortName) { |
| if (shortName == null) { |
| shortName = displayName; |
| } |
| Source source = this.source; |
| if (source != null) { |
| return "$shortName (${source.fullName})"; |
| } |
| return shortName; |
| } |
| |
| /** |
| * Return the resolved [AstNode] of the given type enclosing [getNameOffset]. |
| */ |
| AstNode getNodeMatching(Predicate<AstNode> predicate) { |
| CompilationUnit unit = this.unit; |
| if (unit == null) { |
| return null; |
| } |
| int offset = nameOffset; |
| AstNode node = new NodeLocator(offset).searchWithin(unit); |
| if (node == null) { |
| return null; |
| } |
| return node.getAncestor(predicate); |
| } |
| |
| /** |
| * Return `true` if this element has the given [modifier] associated with it. |
| */ |
| bool hasModifier(Modifier modifier) => |
| BooleanArray.get(_modifiers, modifier.ordinal); |
| |
| @override |
| bool isAccessibleIn(LibraryElement library) { |
| if (Identifier.isPrivateName(name)) { |
| return library == this.library; |
| } |
| return true; |
| } |
| |
| /** |
| * Use the given [visitor] to visit all of the [children] in the given array. |
| */ |
| void safelyVisitChildren(List<Element> children, ElementVisitor visitor) { |
| if (children != null) { |
| for (Element child in children) { |
| child.accept(visitor); |
| } |
| } |
| } |
| |
| /** |
| * Set the code range for this element. |
| */ |
| void setCodeRange(int offset, int length) { |
| assert(!isResynthesized); |
| _codeOffset = offset; |
| _codeLength = length; |
| } |
| |
| /** |
| * Set whether the given [modifier] is associated with this element to |
| * correspond to the given [value]. |
| */ |
| void setModifier(Modifier modifier, bool value) { |
| _modifiers = BooleanArray.set(_modifiers, modifier.ordinal, value); |
| } |
| |
| @override |
| String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| appendTo(buffer); |
| return buffer.toString(); |
| } |
| |
| @override |
| void visitChildren(ElementVisitor visitor) { |
| // There are no children to visit |
| } |
| |
| /** |
| * Return annotations for the given [unlinkedConsts] in the [unit]. |
| */ |
| List<ElementAnnotation> _buildAnnotations( |
| CompilationUnitElementImpl unit, List<UnlinkedExpr> unlinkedConsts) { |
| int length = unlinkedConsts.length; |
| if (length != 0) { |
| List<ElementAnnotation> annotations = new List<ElementAnnotation>(length); |
| ResynthesizerContext context = unit.resynthesizerContext; |
| for (int i = 0; i < length; i++) { |
| annotations[i] = context.buildAnnotation(this, unlinkedConsts[i]); |
| } |
| return annotations; |
| } else { |
| return const <ElementAnnotation>[]; |
| } |
| } |
| |
| /** |
| * If the element associated with the given [type] is a generic function type |
| * element, then make it a child of this element. Return the [type] as a |
| * convenience. |
| */ |
| DartType _checkElementOfType(DartType type) { |
| Element element = type?.element; |
| if (element is GenericFunctionTypeElementImpl && |
| element.enclosingElement == null) { |
| element.enclosingElement = this; |
| } |
| return type; |
| } |
| |
| /** |
| * If the given [type] is a generic function type, then the element associated |
| * with the type is implicitly a child of this element and should be visted by |
| * the given [visitor]. |
| */ |
| void _safelyVisitPossibleChild(DartType type, ElementVisitor visitor) { |
| Element element = type?.element; |
| if (element is GenericFunctionTypeElementImpl) { |
| element.accept(visitor); |
| } |
| } |
| |
| static int findElementIndexUsingIdentical(List items, Object item) { |
| int length = items.length; |
| for (int i = 0; i < length; i++) { |
| if (identical(items[i], item)) { |
| return i; |
| } |
| } |
| throw new StateError('Unable to find $item in $items'); |
| } |
| } |
| |
| /** |
| * A concrete implementation of an [ElementLocation]. |
| */ |
| class ElementLocationImpl implements ElementLocation { |
| /** |
| * The character used to separate components in the encoded form. |
| */ |
| static int _SEPARATOR_CHAR = 0x3B; |
| |
| /** |
| * The path to the element whose location is represented by this object. |
| */ |
| List<String> _components; |
| |
| /** |
| * The object managing [indexKeyId] and [indexLocationId]. |
| */ |
| Object indexOwner; |
| |
| /** |
| * A cached id of this location in index. |
| */ |
| int indexKeyId; |
| |
| /** |
| * A cached id of this location in index. |
| */ |
| int indexLocationId; |
| |
| /** |
| * Initialize a newly created location to represent the given [element]. |
| */ |
| ElementLocationImpl.con1(Element element) { |
| List<String> components = new List<String>(); |
| Element ancestor = element; |
| while (ancestor != null) { |
| components.insert(0, (ancestor as ElementImpl).identifier); |
| ancestor = ancestor.enclosingElement; |
| } |
| this._components = components; |
| } |
| |
| /** |
| * Initialize a newly created location from the given [encoding]. |
| */ |
| ElementLocationImpl.con2(String encoding) { |
| this._components = _decode(encoding); |
| } |
| |
| /** |
| * Initialize a newly created location from the given [components]. |
|