blob: 57c8d14c73e748b8e4db5e39c3c79988ff08caa3 [file] [log] [blame]
// 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].