blob: 67849790a49bbaf9d90de1facab408b822373bf2 [file] [log] [blame]
// Copyright (c) 2013, 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 elements.modelx;
import '../common.dart';
import '../common/names.dart' show Identifiers;
import '../common/resolution.dart' show Resolution, ParsingContext;
import '../compiler.dart' show Compiler;
import '../constants/constant_constructors.dart';
import '../constants/constructors.dart';
import '../constants/expressions.dart';
import '../diagnostics/messages.dart' show MessageTemplate;
import '../ordered_typeset.dart' show OrderedTypeSet;
import '../resolution/class_members.dart' show ClassMemberMixin;
import '../resolution/resolution.dart' show AnalyzableElementX;
import '../resolution/scope.dart'
show ClassScope, LibraryScope, Scope, TypeDeclarationScope;
import '../resolution/tree_elements.dart' show TreeElements;
import '../resolution/typedefs.dart' show TypedefCyclicVisitor;
import '../script.dart';
import 'package:front_end/src/fasta/scanner.dart' show ErrorToken, Token;
import 'package:front_end/src/fasta/scanner.dart' as Tokens show EOF_TOKEN;
import '../tree/tree.dart';
import '../util/util.dart';
import 'common.dart';
import 'elements.dart';
import 'entities.dart';
import 'jumps.dart';
import 'names.dart';
import 'resolution_types.dart';
import 'visitor.dart' show ElementVisitor;
/// Object that identifies a declaration site.
///
/// For most elements, this is the element itself, but for variable declarations
/// where multi-declarations like `var a, b, c` are allowed, the declaration
/// site is a separate object.
// TODO(johnniwinther): Add [beginToken] and [endToken] getters.
abstract class DeclarationSite {}
abstract class ElementX extends Element with ElementCommon {
static int _elementHashCode = 0;
static int newHashCode() =>
_elementHashCode = (_elementHashCode + 1).toUnsigned(30);
final String name;
final ElementKind kind;
final Element enclosingElement;
final int hashCode = newHashCode();
List<MetadataAnnotation> metadataInternal;
ElementX(this.name, this.kind, this.enclosingElement) {
assert(isError || implementationLibrary != null);
}
Modifiers get modifiers => Modifiers.EMPTY;
Node parseNode(ParsingContext parsing) {
parsing.reporter.internalError(this, 'parseNode not implemented on $this.');
return null;
}
void set metadata(List<MetadataAnnotation> metadata) {
assert(metadataInternal == null);
for (MetadataAnnotationX annotation in metadata) {
assert(annotation.annotatedElement == null);
annotation.annotatedElement = this;
}
metadataInternal = metadata;
}
Iterable<MetadataAnnotation> get metadata {
if (isPatch && metadataInternal != null) {
if (origin.metadata.isEmpty) {
return metadataInternal;
} else {
return <MetadataAnnotation>[]
..addAll(origin.metadata)
..addAll(metadataInternal);
}
}
return metadataInternal != null
? metadataInternal
: const <MetadataAnnotation>[];
}
bool get isClosure => false;
bool get isClassMember {
// Check that this element is defined in the scope of a Class.
return enclosingElement != null && enclosingElement.isClass;
}
bool get isInstanceMember => false;
bool get isDeferredLoaderGetter => false;
bool get isConst => modifiers.isConst;
bool get isFinal => modifiers.isFinal;
bool get isStatic => modifiers.isStatic;
bool get isOperator => Elements.isOperatorName(name);
bool get isSynthesized => false;
bool get isMixinApplication => false;
bool get isLocal => false;
// TODO(johnniwinther): This breaks for libraries (for which enclosing
// elements are null) and is invalid for top level variable declarations for
// which the enclosing element is a VariableDeclarations and not a compilation
// unit.
bool get isTopLevel {
return enclosingElement != null && enclosingElement.isCompilationUnit;
}
@override
int get sourceOffset => position?.charOffset;
Token get position => null;
SourceSpan get sourcePosition {
if (position == null) return null;
Uri uri = compilationUnit.script.resourceUri;
return new SourceSpan(uri, position.charOffset, position.charEnd);
}
Token findMyName(Token token) {
return findNameToken(token, isConstructor, name, enclosingElement.name);
}
static Token findNameToken(
Token token, bool isConstructor, String name, String enclosingClassName) {
// We search for the token that has the name of this element.
// For constructors, that doesn't work because they may have
// named formed out of multiple tokens (named constructors) so
// for those we search for the class name instead.
String needle = isConstructor ? enclosingClassName : name;
// The unary '-' operator has a special element name (specified).
if (needle == 'unary-') needle = '-';
for (Token t = token; Tokens.EOF_TOKEN != t.kind; t = t.next) {
if (t is! ErrorToken && needle == t.lexeme) return t;
}
return token;
}
CompilationUnitElement get compilationUnit {
Element element = this;
while (!element.isCompilationUnit) {
element = element.enclosingElement;
}
return element;
}
LibraryElement get library => enclosingElement.library;
Name get memberName => new Name(name, library);
LibraryElement get implementationLibrary {
Element element = this;
while (!identical(element.kind, ElementKind.LIBRARY)) {
element = element.enclosingElement;
}
return element;
}
ClassElement get enclosingClass {
for (Element e = this; e != null; e = e.enclosingElement) {
if (e.isClass) return e.declaration;
}
return null;
}
/**
* Creates the scope for this element.
*/
Scope buildScope() => enclosingElement.buildScope();
String toString() {
// TODO(johnniwinther): Test for nullness of name, or make non-nullness an
// invariant for all element types?
var nameText = name != null ? name : '?';
if (enclosingElement != null && !isTopLevel) {
String holderName = enclosingElement.name != null
? enclosingElement.name
: '${enclosingElement.kind}?';
return '$kind($holderName#${nameText})';
} else {
return '$kind(${nameText})';
}
}
FunctionElement asFunctionElement() => null;
bool get isAbstract => modifiers.isAbstract;
bool get hasTreeElements => analyzableElement.hasTreeElements;
TreeElements get treeElements => analyzableElement.treeElements;
AnalyzableElement get analyzableElement {
Element element = outermostEnclosingMemberOrTopLevel;
if (element.isAbstractField || element.isPrefix) return element.library;
return element;
}
DeclarationSite get declarationSite => null;
void reuseElement() {
throw "reuseElement isn't implemented on ${runtimeType}.";
}
}
class ErroneousElementX extends ElementX
with ConstructorElementCommon
implements ErroneousElement {
final MessageKind messageKind;
final Map messageArguments;
ErroneousElementX(
this.messageKind, this.messageArguments, String name, Element enclosing)
: super(name, ElementKind.ERROR, enclosing);
bool get isTopLevel => false;
bool get isSynthesized => true;
bool get isCyclicRedirection => false;
bool get isDefaultConstructor => false;
bool get isMalformed => true;
PrefixElement get redirectionDeferredPrefix => null;
AbstractFieldElement abstractField;
unsupported() {
throw 'unsupported operation on erroneous element';
}
get asyncMarker => AsyncMarker.SYNC;
Iterable<MetadataAnnotation> get metadata => unsupported();
bool get hasNode => false;
get node => unsupported();
get hasResolvedAst => false;
get resolvedAst => unsupported();
get type => unsupported();
get cachedNode => unsupported();
get functionSignature => unsupported();
get parameterStructure => unsupported();
get parameters => unsupported();
FunctionElement get patch => null;
FunctionElement get origin => this;
get immediateRedirectionTarget => unsupported();
get nestedClosures => unsupported();
get memberContext => unsupported();
get executableContext => unsupported();
get isExternal => unsupported();
get constantConstructor => null;
bool get isRedirectingGenerative => unsupported();
bool get isRedirectingFactory => unsupported();
computeType(Resolution resolution) => unsupported();
bool get hasFunctionSignature => false;
bool get hasEffectiveTarget => true;
get effectiveTarget => this;
computeEffectiveTargetType(ResolutionInterfaceType newType) => unsupported();
get definingConstructor => null;
FunctionElement asFunctionElement() => this;
String get message {
return MessageTemplate.TEMPLATES[messageKind]
.message(messageArguments)
.toString();
}
String toString() => '<$name: $message>';
accept(ElementVisitor visitor, arg) {
return visitor.visitErroneousElement(this, arg);
}
@override
get isEffectiveTargetMalformed {
throw new UnsupportedError("isEffectiveTargetMalformed");
}
@override
List<ResolutionDartType> get typeVariables => unsupported();
}
/// A constructor that was synthesized to recover from a compile-time error.
class ErroneousConstructorElementX extends ErroneousElementX
with
PatchMixin<FunctionElement>,
AnalyzableElementX,
ConstantConstructorMixin
implements ConstructorElementX {
// TODO(ahe): Instead of subclassing [ErroneousElementX], this class should
// be more like [ErroneousFieldElementX]. In particular, its kind should be
// [ElementKind.GENERATIVE_CONSTRUCTOR], and it shouldn't throw as much.
ErroneousConstructorElementX(MessageKind messageKind, Map messageArguments,
String name, Element enclosing)
: super(messageKind, messageArguments, name, enclosing);
@override
bool get isRedirectingGenerative => false;
@override
bool isRedirectingGenerativeInternal;
void set isRedirectingGenerative(_) {
throw new UnsupportedError("isRedirectingGenerative");
}
@override
bool get isRedirectingFactory => false;
@override
get definingElement {
throw new UnsupportedError("definingElement");
}
@override
get asyncMarker {
throw new UnsupportedError("asyncMarker");
}
@override
set asyncMarker(_) {
throw new UnsupportedError("asyncMarker=");
}
@override
get _asyncMarker {
throw new UnsupportedError("_asyncMarker");
}
@override
set _asyncMarker(_) {
throw new UnsupportedError("_asyncMarker=");
}
@override
get effectiveTargetInternal {
throw new UnsupportedError("effectiveTargetInternal");
}
@override
set effectiveTargetInternal(_) {
throw new UnsupportedError("effectiveTargetInternal=");
}
@override
get _effectiveTargetType {
throw new UnsupportedError("_effectiveTargetType");
}
@override
set _effectiveTargetType(_) {
throw new UnsupportedError("_effectiveTargetType=");
}
@override
get effectiveTargetType {
throw new UnsupportedError("effectiveTargetType");
}
@override
get _isEffectiveTargetMalformed {
throw new UnsupportedError("_isEffectiveTargetMalformed");
}
@override
set _isEffectiveTargetMalformed(_) {
throw new UnsupportedError("_isEffectiveTargetMalformed=");
}
@override
get isEffectiveTargetMalformed {
throw new UnsupportedError("isEffectiveTargetMalformed");
}
@override
void setEffectiveTarget(ConstructorElement target, ResolutionDartType type,
{bool isMalformed: false}) {
throw new UnsupportedError("setEffectiveTarget");
}
@override
void _computeSignature(Resolution resolution) {
throw new UnsupportedError("_computeSignature");
}
@override
get typeCache {
throw new UnsupportedError("typeCache");
}
@override
set typeCache(_) {
throw new UnsupportedError("typeCache=");
}
@override
get immediateRedirectionTarget {
throw new UnsupportedError("immediateRedirectionTarget");
}
@override
get _immediateRedirectionTarget {
throw new UnsupportedError("_immediateRedirectionTarget");
}
@override
set _immediateRedirectionTarget(_) {
throw new UnsupportedError("_immediateRedirectionTarget=");
}
@override
setImmediateRedirectionTarget(a, b) {
throw new UnsupportedError("setImmediateRedirectionTarget");
}
@override
get _functionSignatureCache {
throw new UnsupportedError("functionSignatureCache");
}
@override
set _functionSignatureCache(_) {
throw new UnsupportedError("functionSignatureCache=");
}
@override
set functionSignature(_) {
throw new UnsupportedError("functionSignature=");
}
@override
get parameterStructure {
throw new UnsupportedError("parameterStructure");
}
@override
get nestedClosures {
throw new UnsupportedError("nestedClosures");
}
@override
set nestedClosures(_) {
throw new UnsupportedError("nestedClosures=");
}
@override
get _redirectionDeferredPrefix {
throw new UnsupportedError("_redirectionDeferredPrefix");
}
@override
set _redirectionDeferredPrefix(_) {
throw new UnsupportedError("_redirectionDeferredPrefix=");
}
// TODO(johnniwinther): Remove this.
ConstructorElementX get declaration => super.declaration;
// TODO(johnniwinther): Remove this.
ConstructorElementX get implementation => super.implementation;
// TODO(johnniwinther): Remove this.
ConstructorElementX get origin => super.origin;
// TODO(johnniwinther): Remove this.
ConstructorElementX get patch => super.patch;
ResolutionFunctionType computeType(Resolution resolution) =>
super.computeType(resolution);
}
/// A message attached to a [WarnOnUseElementX].
class WrappedMessage {
/// The message position. If [:null:] the position of the reference to the
/// [WarnOnUseElementX] is used.
final SourceSpan sourceSpan;
/**
* The message to report on resolving a wrapped element.
*/
final MessageKind messageKind;
/**
* The message arguments to report on resolving a wrapped element.
*/
final Map messageArguments;
WrappedMessage(this.sourceSpan, this.messageKind, this.messageArguments);
}
class WarnOnUseElementX extends ElementX implements WarnOnUseElement {
/// Warning to report on resolving this element.
final WrappedMessage warning;
/// Info to report on resolving this element.
final WrappedMessage info;
/// The element whose usage cause a warning.
final Element wrappedElement;
WarnOnUseElementX(
this.warning, this.info, Element enclosingElement, Element wrappedElement)
: this.wrappedElement = wrappedElement,
super(wrappedElement.name, ElementKind.WARN_ON_USE, enclosingElement);
Element unwrap(DiagnosticReporter reporter, Spannable usageSpannable) {
dynamic unwrapped = wrappedElement;
if (warning != null) {
Spannable spannable = warning.sourceSpan;
if (spannable == null) spannable = usageSpannable;
DiagnosticMessage warningMessage = reporter.createMessage(
spannable, warning.messageKind, warning.messageArguments);
List<DiagnosticMessage> infos = <DiagnosticMessage>[];
if (info != null) {
Spannable spannable = info.sourceSpan;
if (spannable == null) spannable = usageSpannable;
infos.add(reporter.createMessage(
spannable, info.messageKind, info.messageArguments));
}
reporter.reportWarning(warningMessage, infos);
}
if (unwrapped.isWarnOnUse) {
unwrapped = unwrapped.unwrap(reporter, usageSpannable);
}
return unwrapped;
}
accept(ElementVisitor visitor, arg) {
return visitor.visitWarnOnUseElement(this, arg);
}
}
abstract class AmbiguousElementX extends ElementX implements AmbiguousElement {
/**
* The message to report on resolving this element.
*/
final MessageKind messageKind;
/**
* The message arguments to report on resolving this element.
*/
final Map messageArguments;
/**
* The first element that this ambiguous element might refer to.
*/
final Element existingElement;
/**
* The second element that this ambiguous element might refer to.
*/
final Element newElement;
AmbiguousElementX(this.messageKind, this.messageArguments,
Element enclosingElement, Element existingElement, Element newElement)
: this.existingElement = existingElement,
this.newElement = newElement,
super(existingElement.name, ElementKind.AMBIGUOUS, enclosingElement);
Setlet flatten() {
Element element = this;
var set = new Setlet();
while (element.isAmbiguous) {
AmbiguousElement ambiguous = element;
set.add(ambiguous.newElement);
element = ambiguous.existingElement;
}
set.add(element);
return set;
}
List<DiagnosticMessage> computeInfos(
Element context, DiagnosticReporter reporter) {
return const <DiagnosticMessage>[];
}
accept(ElementVisitor visitor, arg) {
return visitor.visitAmbiguousElement(this, arg);
}
bool get isTopLevel => false;
ResolutionDynamicType get type => const ResolutionDynamicType();
}
/// Element synthesized to diagnose an ambiguous import.
class AmbiguousImportX extends AmbiguousElementX {
AmbiguousImportX(MessageKind messageKind, Map messageArguments,
Element enclosingElement, Element existingElement, Element newElement)
: super(messageKind, messageArguments, enclosingElement, existingElement,
newElement);
List<DiagnosticMessage> computeInfos(
Element context, DiagnosticReporter reporter) {
List<DiagnosticMessage> infos = <DiagnosticMessage>[];
Setlet ambiguousElements = flatten();
MessageKind code = (ambiguousElements.length == 1)
? MessageKind.AMBIGUOUS_REEXPORT
: MessageKind.AMBIGUOUS_LOCATION;
LibraryElementX importer = context.library;
for (Element element in ambiguousElements) {
Map arguments = {'name': element.name};
infos.add(reporter.createMessage(element, code, arguments));
reporter.withCurrentElement(importer, () {
for (ImportElement import in importer.importers.getImports(element)) {
infos.add(reporter.createMessage(
import, MessageKind.IMPORTED_HERE, arguments));
}
});
}
return infos;
}
}
/// Element synthesized to recover from a duplicated member of an element.
class DuplicatedElementX extends AmbiguousElementX {
DuplicatedElementX(MessageKind messageKind, Map messageArguments,
Element enclosingElement, Element existingElement, Element newElement)
: super(messageKind, messageArguments, enclosingElement, existingElement,
newElement);
bool get isMalformed => true;
}
class ScopeX {
final Map<String, Element> contents = new Map<String, Element>();
bool get isEmpty => contents.isEmpty;
Iterable<Element> get values => contents.values;
Element lookup(String name) {
return contents[name];
}
void add(Element element, DiagnosticReporter reporter) {
String name = element.name;
if (element.isAccessor) {
addAccessor(element, contents[name], reporter);
} else {
Element existing = contents.putIfAbsent(name, () => element);
if (!identical(existing, element)) {
reporter.reportError(
reporter.createMessage(
element, MessageKind.DUPLICATE_DEFINITION, {'name': name}),
<DiagnosticMessage>[
reporter.createMessage(
existing, MessageKind.EXISTING_DEFINITION, {'name': name}),
]);
}
}
}
/**
* Adds a definition for an [accessor] (getter or setter) to a scope.
* The definition binds to an abstract field that can hold both a getter
* and a setter.
*
* The abstract field is added once, for the first getter or setter, and
* reused if the other one is also added.
* The abstract field should not be treated as a proper member of the
* container, it's simply a way to return two results for one lookup.
* That is, the getter or setter does not have the abstract field as enclosing
* element, they are enclosed by the class or compilation unit, as is the
* abstract field.
*/
void addAccessor(AccessorElementX accessor, Element existing,
DiagnosticReporter reporter) {
void reportError(Element other) {
reporter.reportError(
reporter.createMessage(accessor, MessageKind.DUPLICATE_DEFINITION,
{'name': accessor.name}),
<DiagnosticMessage>[
reporter.createMessage(other, MessageKind.EXISTING_DEFINITION,
{'name': accessor.name}),
]);
contents[accessor.name] = new DuplicatedElementX(
MessageKind.DUPLICATE_DEFINITION,
{'name': accessor.name},
accessor.memberContext.enclosingElement,
other,
accessor);
}
if (existing != null) {
if (!identical(existing.kind, ElementKind.ABSTRACT_FIELD)) {
reportError(existing);
return;
} else {
AbstractFieldElementX field = existing;
accessor.abstractField = field;
if (accessor.isGetter) {
if (field.getter != null && field.getter != accessor) {
reportError(field.getter);
return;
}
field.getter = accessor;
} else {
assert(accessor.isSetter);
if (field.setter != null && field.setter != accessor) {
reportError(field.setter);
return;
}
field.setter = accessor;
}
}
} else {
Element container = accessor.enclosingClassOrCompilationUnit;
AbstractFieldElementX field =
new AbstractFieldElementX(accessor.name, container);
accessor.abstractField = field;
if (accessor.isGetter) {
field.getter = accessor;
} else {
field.setter = accessor;
}
add(field, reporter);
}
}
}
class CompilationUnitElementX extends ElementX
with CompilationUnitElementCommon
implements CompilationUnitElement {
final Script script;
PartOf partTag;
Link<Element> localMembers = const Link<Element>();
CompilationUnitElementX(Script script, LibraryElementX library)
: this.script = script,
super(script.name, ElementKind.COMPILATION_UNIT, library) {
library.addCompilationUnit(this);
}
@override
LibraryElementX get library => enclosingElement.declaration;
void set metadata(List<MetadataAnnotation> metadata) {
for (MetadataAnnotationX annotation in metadata) {
assert(annotation.annotatedElement == null);
annotation.annotatedElement = this;
}
// TODO(johnniwinther): Remove this work-around when import, export,
// part, and part-of declarations are elements.
if (metadataInternal == null) {
metadataInternal = <MetadataAnnotation>[];
}
metadataInternal.addAll(metadata);
}
void forEachLocalMember(f(Element element)) {
localMembers.forEach(f);
}
void addMember(Element element, DiagnosticReporter reporter) {
// Keep a list of top level members.
localMembers = localMembers.prepend(element);
// Provide the member to the library to build scope.
if (enclosingElement.isPatch) {
LibraryElementX library = implementationLibrary;
library.addMember(element, reporter);
} else {
library.addMember(element, reporter);
}
}
void setPartOf(PartOf tag, DiagnosticReporter reporter) {
LibraryElementX library = enclosingElement;
if (library.entryCompilationUnit == this) {
// This compilation unit is loaded as a library. The error is reported by
// the library loader.
partTag = tag;
return;
}
if (!localMembers.isEmpty) {
reporter.reportErrorMessage(tag, MessageKind.BEFORE_TOP_LEVEL);
return;
}
if (partTag != null) {
reporter.reportWarningMessage(tag, MessageKind.DUPLICATED_PART_OF);
return;
}
partTag = tag;
LibraryName libraryTag = library.libraryTag;
Expression libraryReference = tag.name;
if (libraryReference is LiteralString) {
// Name is a URI. Resolve and compare to library's URI.
String content = libraryReference.dartString.slowToString();
Uri uri = this.script.readableUri.resolve(content);
Uri expectedUri = library.canonicalUri;
// Also allow `string.dart` to refer to `dart:core` as `core.dart`.
if (library.isPlatformLibrary && !uri.isScheme("dart")) {
expectedUri = library.entryCompilationUnit.script.readableUri;
}
if (uri != expectedUri) {
// Consider finding a relative URI reference for the error message.
reporter.reportWarningMessage(tag.name,
MessageKind.LIBRARY_URI_MISMATCH, {'libraryUri': expectedUri});
}
return;
}
String actualName = tag.name.toString();
if (libraryTag != null) {
String expectedName = libraryTag.name.toString();
if (expectedName != actualName) {
reporter.reportWarningMessage(tag.name,
MessageKind.LIBRARY_NAME_MISMATCH, {'libraryName': expectedName});
}
} else {
reporter.reportWarning(
reporter.createMessage(library, MessageKind.MISSING_LIBRARY_NAME,
{'libraryName': actualName}),
<DiagnosticMessage>[
reporter.createMessage(
tag.name, MessageKind.THIS_IS_THE_PART_OF_TAG),
]);
}
}
bool get hasMembers => !localMembers.isEmpty;
AnalyzableElement get analyzableElement => library;
accept(ElementVisitor visitor, arg) {
return visitor.visitCompilationUnitElement(this, arg);
}
}
/// Map from [Element] to the [ImportElement]s throught which it was imported.
///
/// This is used for error reporting and deferred loading.
class Importers {
Map<Element, List<ImportElement>> importers =
new Map<Element, List<ImportElement>>();
/// Returns the list of [ImportElement]s through which [element] was
/// imported.
List<ImportElement> getImports(Element element) {
List<ImportElement> imports = importers[element];
return imports != null ? imports : const <ImportElement>[];
}
/// Returns the first [ImportElement] through which [element] was imported.
ImportElement getImport(Element element) => getImports(element).first;
/// Register [element] as imported through [import];
void registerImport(Element element, ImportElement import) {
importers.putIfAbsent(element, () => <ImportElement>[]).add(import);
}
}
class ImportScope {
/**
* Map for elements imported through import declarations.
*
* Addition to the map is performed by [addImport]. Lookup is done trough
* [find].
*/
final Map<String, Element> importScope = new Map<String, Element>();
/**
* Adds [element] to the import scope of this library.
*
* If an element by the same name is already in the imported scope, an
* [ErroneousElement] will be put in the imported scope, allowing for
* detection of ambiguous uses of imported names.
*/
void addImport(Element enclosingElement, Element element,
ImportElement import, DiagnosticReporter reporter) {
LibraryElementX library = enclosingElement.library;
Importers importers = library.importers;
String name = element.name;
// The loadLibrary function always shadows existing bindings to that name.
if (element.isDeferredLoaderGetter) {
importScope.remove(name);
// TODO(sigurdm): Print a hint.
}
Element existing = importScope.putIfAbsent(name, () => element);
importers.registerImport(element, import);
void registerWarnOnUseElement(ImportElement import, MessageKind messageKind,
Element hidingElement, Element hiddenElement) {
Uri hiddenUri = hiddenElement.library.canonicalUri;
Uri hidingUri = hidingElement.library.canonicalUri;
Element element = new WarnOnUseElementX(
new WrappedMessage(
null, // Report on reference to [hidingElement].
messageKind,
{'name': name, 'hiddenUri': hiddenUri, 'hidingUri': hidingUri}),
new WrappedMessage(reporter.spanFromSpannable(import),
MessageKind.IMPORTED_HERE, {'name': name}),
enclosingElement,
hidingElement);
importScope[name] = element;
importers.registerImport(element, import);
}
if (existing != element) {
ImportElement existingImport = importers.getImport(existing);
if (existing.library.isPlatformLibrary &&
!element.library.isPlatformLibrary) {
// [existing] is implicitly hidden.
registerWarnOnUseElement(
import, MessageKind.HIDDEN_IMPORT, element, existing);
} else if (!existing.library.isPlatformLibrary &&
element.library.isPlatformLibrary) {
// [element] is implicitly hidden.
if (import.isSynthesized) {
// [element] is imported implicitly (probably through dart:core).
registerWarnOnUseElement(existingImport,
MessageKind.HIDDEN_IMPLICIT_IMPORT, existing, element);
} else {
registerWarnOnUseElement(
import, MessageKind.HIDDEN_IMPORT, existing, element);
}
} else {
Element ambiguousElement = new AmbiguousImportX(
MessageKind.DUPLICATE_IMPORT,
{'name': name},
enclosingElement,
existing,
element);
importScope[name] = ambiguousElement;
importers.registerImport(ambiguousElement, import);
importers.registerImport(ambiguousElement, existingImport);
}
}
}
Element operator [](String name) => importScope[name];
void forEach(f(Element element)) => importScope.values.forEach(f);
}
abstract class LibraryDependencyElementX extends ElementX {
final LibraryDependency node;
final Uri uri;
LibraryElement libraryDependency;
LibraryDependencyElementX(CompilationUnitElement enclosingElement,
ElementKind kind, this.node, this.uri)
: super('', kind, enclosingElement);
@override
List<MetadataAnnotation> get metadata => node.metadata;
void set metadata(value) {
// The metadata is stored on [libraryDependency].
failedAt(this, 'Cannot set metadata on a import/export.');
}
@override
Token get position => node.getBeginToken();
SourceSpan get sourcePosition {
return new SourceSpan.fromNode(compilationUnit.script.resourceUri, node);
}
String toString() => '$kind($uri)';
}
class ImportElementX extends LibraryDependencyElementX
implements ImportElement {
PrefixElementX prefix;
ImportElementX(CompilationUnitElement enclosingElement, Import node, Uri uri)
: super(enclosingElement, ElementKind.IMPORT, node, uri);
@override
Import get node => super.node;
@override
LibraryElement get importedLibrary => libraryDependency;
@override
accept(ElementVisitor visitor, arg) => visitor.visitImportElement(this, arg);
@override
bool get isDeferred => node.isDeferred;
}
class SyntheticImportElement extends ImportElementX {
SyntheticImportElement(CompilationUnitElement enclosingElement, Uri uri,
LibraryElement libraryDependency)
: super(enclosingElement, null, uri) {
this.libraryDependency = libraryDependency;
}
@override
Token get position => library.position;
@override
bool get isSynthesized => true;
@override
bool get isDeferred => false;
@override
List<MetadataAnnotation> get metadata => const <MetadataAnnotation>[];
@override
SourceSpan get sourcePosition => library.sourcePosition;
}
class ExportElementX extends LibraryDependencyElementX
implements ExportElement {
ExportElementX(CompilationUnitElement enclosingElement, Export node, Uri uri)
: super(enclosingElement, ElementKind.EXPORT, node, uri);
Export get node => super.node;
@override
LibraryElement get exportedLibrary => libraryDependency;
@override
accept(ElementVisitor visitor, arg) => visitor.visitExportElement(this, arg);
}
class LibraryElementX extends ElementX
with LibraryElementCommon, AnalyzableElementX, PatchMixin<LibraryElementX>
implements LibraryElement {
final Uri canonicalUri;
/// True if the constructing script was synthesized.
final bool isSynthesized;
CompilationUnitElement entryCompilationUnit;
Link<CompilationUnitElement> compilationUnits =
const Link<CompilationUnitElement>();
LinkBuilder<LibraryTag> tagsBuilder = new LinkBuilder<LibraryTag>();
List<LibraryTag> tagsCache;
LibraryName libraryTag;
Link<Element> localMembers = const Link<Element>();
final ScopeX localScope = new ScopeX();
final ImportScope importScope = new ImportScope();
/// A mapping from an imported element to the "import" tag.
final Importers importers = new Importers();
/**
* Link for elements exported either through export declarations or through
* declaration. This field should not be accessed directly but instead through
* the [exports] getter.
*
* [LibraryDependencyHandler] sets this field through [setExports] when the
* library is loaded.
*/
Link<Element> slotForExports;
List<ImportElement> _imports = <ImportElement>[];
List<ExportElement> _exports = <ExportElement>[];
final Map<LibraryDependency, LibraryElement> tagMapping =
new Map<LibraryDependency, LibraryElement>();
final Map<String, MixinApplicationElementX> mixinApplicationCache =
<String, MixinApplicationElementX>{};
LibraryElementX(Script script, [Uri canonicalUri, LibraryElementX origin])
: this.canonicalUri =
((canonicalUri == null) ? script.readableUri : canonicalUri),
this.isSynthesized = script.isSynthesized,
super(script.name, ElementKind.LIBRARY, null) {
entryCompilationUnit = new CompilationUnitElementX(script, this);
if (origin != null) {
origin.applyPatch(this);
}
}
Iterable<MetadataAnnotation> get metadata {
if (libraryTag != null) {
return libraryTag.metadata;
}
return const <MetadataAnnotation>[];
}
void set metadata(value) {
// The metadata is stored on [libraryTag].
failedAt(this, 'Cannot set metadata on Library');
}
CompilationUnitElement get compilationUnit => entryCompilationUnit;
AnalyzableElement get analyzableElement => this;
void addCompilationUnit(CompilationUnitElement element) {
compilationUnits = compilationUnits.prepend(element);
}
void addTag(LibraryTag tag, DiagnosticReporter reporter) {
if (tagsCache != null) {
reporter.internalError(
tag, "Library tags for $this have already been computed.");
}
tagsBuilder.addLast(tag);
}
Iterable<LibraryTag> get tags {
if (tagsCache == null) {
tagsCache = tagsBuilder.toList();
tagsBuilder = null;
}
return tagsCache;
}
void addImportDeclaration(ImportElement import) {
_imports.add(import);
}
Iterable<ImportElement> get imports => _imports;
void addExportDeclaration(ExportElement export) {
_exports.add(export);
}
Iterable<ExportElement> get exports => _exports;
/**
* Adds [element] to the import scope of this library.
*
* If an element by the same name is already in the imported scope, an
* [ErroneousElement] will be put in the imported scope, allowing for
* detection of ambiguous uses of imported names.
*/
void addImport(
Element element, ImportElement import, DiagnosticReporter reporter) {
importScope.addImport(this, element, import, reporter);
}
void addMember(Element element, DiagnosticReporter reporter) {
localMembers = localMembers.prepend(element);
addToScope(element, reporter);
}
void addToScope(Element element, DiagnosticReporter reporter) {
localScope.add(element, reporter);
}
Element localLookup(String elementName) {
Element result = localScope.lookup(elementName);
if (result == null && isPatch) {
result = origin.localLookup(elementName);
}
return result;
}
/**
* Returns [:true:] if the export scope has already been computed for this
* library.
*/
bool get exportsHandled => slotForExports != null;
/**
* Sets the export scope of this library. This method can only be called once.
*/
void setExports(Iterable<Element> exportedElements) {
assert(!exportsHandled,
failedAt(this, 'Exports already set to $slotForExports on $this'));
assert(exportedElements != null, failedAt(this));
var builder = new LinkBuilder<Element>();
for (Element export in exportedElements) {
builder.addLast(export);
}
slotForExports = builder.toLink();
}
LibraryElement get library => isPatch ? origin : this;
/**
* Look up a top-level element in this library. The element could
* potentially have been imported from another library. Returns
* null if no such element exist and an [ErroneousElement] if multiple
* elements have been imported.
*/
Element find(String elementName) {
Element result = localScope.lookup(elementName);
if (result != null) return result;
if (origin != null) {
result = origin.localScope.lookup(elementName);
if (result != null) return result;
}
result = importScope[elementName];
if (result != null) return result;
if (origin != null) {
result = origin.importScope[elementName];
if (result != null) return result;
}
return null;
}
/** Look up a top-level element in this library, but only look for
* non-imported elements. Returns null if no such element exist. */
Element findLocal(String elementName) {
// TODO((johnniwinther): How to handle injected elements in the patch
// library?
Element result = localScope.lookup(elementName);
if (result == null && isPatch) {
return origin.findLocal(elementName);
}
return result;
}
Element findExported(String elementName) {
assert(exportsHandled, failedAt(this, 'Exports not handled on $this'));
for (Link link = slotForExports; !link.isEmpty; link = link.tail) {
Element element = link.head;
if (element.name == elementName) return element;
}
return null;
}
void forEachExport(f(Element element)) {
assert(exportsHandled, failedAt(this, 'Exports not handled on $this'));
slotForExports.forEach((Element e) => f(e));
}
Iterable<ImportElement> getImportsFor(Element element) {
return importers.getImports(element);
}
void forEachImport(f(Element element)) => importScope.forEach(f);
void forEachLocalMember(f(Element element)) {
if (isPatch) {
// Patch libraries traverse both origin and injected members.
origin.localMembers.forEach(f);
void filterPatch(Element element) {
if (!element.isPatch) {
// Do not traverse the patch members.
f(element);
}
}
localMembers.forEach(filterPatch);
} else {
localMembers.forEach(f);
}
}
Iterable<Element> getNonPrivateElementsInScope() {
return localScope.values.where((Element element) {
// At this point [localScope] only contains members so we don't need
// to check for foreign or prefix elements.
return !Name.isPrivateName(element.name);
});
}
bool get hasLibraryName => libraryTag != null;
String get libraryName {
if (libraryTag == null) return '';
return libraryTag.name.toString();
}
Scope buildScope() => new LibraryScope(this);
String toString() {
if (origin != null) {
return 'patch library(${canonicalUri})';
} else if (patch != null) {
return 'origin library(${canonicalUri})';
} else {
return 'library(${canonicalUri})';
}
}
accept(ElementVisitor visitor, arg) {
return visitor.visitLibraryElement(this, arg);
}
// TODO(johnniwinther): Remove this.
LibraryElementX get declaration => super.declaration;
// TODO(johnniwinther): Remove this.
LibraryElementX get implementation => super.implementation;
// TODO(johnniwinther): Remove this.
LibraryElementX get origin => super.origin;
// TODO(johnniwinther): Remove this.
LibraryElementX get patch => super.patch;
}
class PrefixElementX extends ElementX implements PrefixElement {
Token firstPosition;
final ImportScope importScope = new ImportScope();
bool get isDeferred => deferredImport != null;
// Only needed for deferred imports.
final ImportElement deferredImport;
PrefixElementX(
String prefix, Element enclosing, this.firstPosition, this.deferredImport)
: super(prefix, ElementKind.PREFIX, enclosing);
bool get isTopLevel => false;
Element lookupLocalMember(String memberName) => importScope[memberName];
void forEachLocalMember(f(Element member)) => importScope.forEach(f);
ResolutionDartType computeType(Resolution resolution) =>
const ResolutionDynamicType();
Token get position => firstPosition;
void addImport(
Element element, ImportElement import, DiagnosticReporter reporter) {
importScope.addImport(this, element, import, reporter);
}
accept(ElementVisitor visitor, arg) {
return visitor.visitPrefixElement(this, arg);
}
@override
GetterElement get loadLibrary {
return isDeferred ? lookupLocalMember(Identifiers.loadLibrary) : null;
}
String toString() => '$kind($name)';
}
class TypedefElementX extends ElementX
with AstElementMixin, AnalyzableElementX, TypeDeclarationElementX
implements TypedefElement {
Typedef cachedNode;
/**
* The type annotation which defines this typedef.
*/
ResolutionDartType aliasCache;
ResolutionDartType get alias {
assert(hasBeenCheckedForCycles,
failedAt(this, "$this has not been checked for cycles."));
return aliasCache;
}
/// [:true:] if the typedef has been checked for cyclic reference.
bool hasBeenCheckedForCycles = false;
int resolutionState = STATE_NOT_STARTED;
TypedefElementX(String name, Element enclosing)
: super(name, ElementKind.TYPEDEF, enclosing);
bool get hasNode => cachedNode != null;
Typedef get node {
assert(cachedNode != null,
failedAt(this, "Node has not been computed for $this."));
return cachedNode;
}
/**
* Function signature for a typedef of a function type. The signature is
* kept to provide full information about parameter names through the mirror
* system.
*
* The [functionSignature] is not available until the typedef element has been
* resolved.
*/
FunctionSignature functionSignature;
ResolutionTypedefType computeType(Resolution resolution) {
if (thisTypeCache != null) return thisTypeCache;
Typedef node = parseNode(resolution.parsingContext);
setThisAndRawTypes(createTypeVariables(node.templateParameters));
ensureResolved(resolution);
return thisTypeCache;
}
void ensureResolved(Resolution resolution) {
if (resolutionState == STATE_NOT_STARTED) {
resolution.resolveTypedef(this);
}
}
ResolutionTypedefType createType(List<ResolutionDartType> typeArguments) {
return new ResolutionTypedefType(this, typeArguments);
}
Scope buildScope() {
return new TypeDeclarationScope(enclosingElement.buildScope(), this);
}
void checkCyclicReference(Resolution resolution) {
if (hasBeenCheckedForCycles) return;
TypedefCyclicVisitor visitor =
new TypedefCyclicVisitor(resolution.reporter, this);
computeType(resolution).accept(visitor, null);
hasBeenCheckedForCycles = true;
}
accept(ElementVisitor visitor, arg) {
return visitor.visitTypedefElement(this, arg);
}
// A typedef cannot be patched therefore defines itself.
AstElement get definingElement => this;
ResolutionTypedefType get thisType => super.thisType;
ResolutionTypedefType get rawType => super.rawType;
}
// This class holds common information for a list of variable or field
// declarations. It contains the node, and the type. A [VariableElementX]
// forwards its [computeType] and [parseNode] methods to this class.
class VariableList implements DeclarationSite {
VariableDefinitions definitions;
ResolutionDartType type;
final Modifiers modifiers;
List<MetadataAnnotation> metadataInternal;
VariableList(Modifiers this.modifiers);
VariableList.node(VariableDefinitions node, this.type)
: this.definitions = node,
this.modifiers = node.modifiers {
assert(modifiers != null);
}
Iterable<MetadataAnnotation> get metadata {
return metadataInternal != null
? metadataInternal
: const <MetadataAnnotation>[];
}
void set metadata(List<MetadataAnnotation> metadata) {
if (metadata.isEmpty) {
// For a multi declaration like:
//
// @foo @bar var a, b, c
//
// the metadata list is reported through the declaration of `a`, and `b`
// and `c` report an empty list of metadata.
return;
}
assert(metadataInternal == null);
metadataInternal = metadata;
}
VariableDefinitions parseNode(Element element, ParsingContext parsing) {
return definitions;
}
ResolutionDartType computeType(Element element, Resolution resolution) =>
type;
}
abstract class ConstantVariableMixin implements VariableElement {
ConstantExpression constantCache;
// TODO(johnniwinther): Update the on `constant = ...` when evaluation of
// constant expression can handle references to unanalyzed constant variables.
@override
bool get hasConstant => false;
ConstantExpression get constant {
if (isPatch) {
ConstantVariableMixin originVariable = origin;
return originVariable.constant;
}
assert(!isConst || constantCache != null,
failedAt(this, "Constant has not been computed for $this."));
return constantCache;
}
void set constant(ConstantExpression value) {
if (isPatch) {
ConstantVariableMixin originVariable = origin;
originVariable.constant = value;
return;
}
if (constantCache != null &&
constantCache.kind == ConstantExpressionKind.ERRONEOUS) {
// TODO(johnniwinther): Find out why we sometimes compute a non-erroneous
// constant for a variable already known to be erroneous.
return;
}
if (constantCache != null && constantCache != value) {
// Allow setting the constant as erroneous. Constants computed during
// resolution are locally valid but might be effectively erroneous. For
// instance `a ? true : false` where a is `const a = m()`. Since `a` is
// declared to be constant, the conditional is assumed valid, but when
// computing the value we see that it isn't.
// TODO(johnniwinther): Remove this exception when all constant
// expressions are computed during resolution.
assert(
value == null || value.kind == ConstantExpressionKind.ERRONEOUS,
failedAt(
this,
"Constant has already been computed for $this. "
"Existing constant: "
"${constantCache != null ? constantCache.toStructuredText() : ''}"
", New constant: "
"${value != null ? value.toStructuredText() : ''}."));
}
constantCache = value;
}
}
abstract class VariableElementX extends ElementX
with AstElementMixin, ConstantVariableMixin
implements VariableElement {
final Token token;
final VariableList variables;
VariableDefinitions definitionsCache;
Expression definitionCache;
Expression initializerCache;
Modifiers get modifiers => variables.modifiers;
VariableElementX(String name, ElementKind kind, Element enclosingElement,
VariableList variables, this.token)
: this.variables = variables,
super(name, kind, enclosingElement);
// TODO(johnniwinther): Ensure that the [TreeElements] for this variable hold
// the mappings for all its metadata.
Iterable<MetadataAnnotation> get metadata => variables.metadata;
void set metadata(List<MetadataAnnotation> metadata) {
for (MetadataAnnotationX annotation in metadata) {
assert(annotation.annotatedElement == null);
annotation.annotatedElement = this;
}
variables.metadata = metadata;
}
// A variable cannot be patched therefore defines itself.
AstElement get definingElement => this;
bool get hasNode => definitionsCache != null;
VariableDefinitions get node {
assert(definitionsCache != null,
failedAt(this, "Node has not been computed for $this."));
return definitionsCache;
}
/// Returns the node that defines this field.
///
/// For instance in `var a, b = true`, the definitions nodes for fields 'a'
/// and 'b' are the nodes for `a` and `b = true`, respectively.
Expression get definition {
assert(definitionCache != null,
failedAt(this, "Definition node has not been computed for $this."));
return definitionCache;
}
Expression get initializer {
assert(definitionsCache != null,
failedAt(this, "Initializer has not been computed for $this."));
return initializerCache;
}
Node parseNode(ParsingContext parsing) {
if (definitionsCache != null) return definitionsCache;
VariableDefinitions definitions = variables.parseNode(this, parsing);
createDefinitions(definitions);
return definitionsCache;
}
void createDefinitions(VariableDefinitions definitions) {
assert(
definitionsCache == null,
failedAt(
this, "VariableDefinitions has already been computed for $this."));
for (Link<Node> link = definitions.definitions.nodes;
!link.isEmpty;
link = link.tail) {
Expression initializedIdentifier = link.head;
Identifier identifier = initializedIdentifier.asIdentifier();
if (identifier == null) {
SendSet sendSet = initializedIdentifier.asSendSet();
identifier = sendSet.selector.asIdentifier();
if (identical(name, identifier.source)) {
definitionCache = initializedIdentifier;
initializerCache = sendSet.arguments.first;
}
} else if (identical(name, identifier.source)) {
definitionCache = initializedIdentifier;
}
}
if (definitionCache == null) {
failedAt(definitionCache, "Could not find '$name'.");
}
definitionsCache = definitions;
}
ResolutionDartType computeType(Resolution resolution) {
if (variables.type != null) return variables.type;
// Call [parseNode] to ensure that [definitionsCache] and [initializerCache]
// are set as a consequence of calling [computeType].
parseNode(resolution.parsingContext);
return variables.computeType(this, resolution);
}
ResolutionDartType get type {
assert(variables.type != null,
failedAt(this, "Type has not been computed for $this."));
return variables.type;
}
bool get isInstanceMember => isClassMember && !isStatic;
// Note: cachedNode.beginToken will not be correct in all
// cases, for example, for function typed parameters.
Token get position => token;
DeclarationSite get declarationSite => variables;
}
class LocalVariableElementX extends VariableElementX
implements LocalVariableElement {
LocalVariableElementX(String name, ExecutableElement enclosingElement,
VariableList variables, Token token)
: super(name, ElementKind.VARIABLE, enclosingElement, variables, token) {
createDefinitions(variables.definitions);
}
ExecutableElement get executableContext => enclosingElement;
MemberElement get memberContext => executableContext.memberContext;
bool get isLocal => true;
accept(ElementVisitor visitor, arg) {
return visitor.visitLocalVariableElement(this, arg);
}
}
class FieldElementX extends VariableElementX
with AnalyzableElementX
implements FieldElement {
List<MethodElement> nestedClosures = new List<MethodElement>();
FieldElementX(
Identifier name, Element enclosingElement, VariableList variables)
: super(name.source, ElementKind.FIELD, enclosingElement, variables,
name.token);
accept(ElementVisitor visitor, arg) {
return visitor.visitFieldElement(this, arg);
}
MemberElement get memberContext => this;
void reuseElement() {
super.reuseElement();
nestedClosures.clear();
}
FieldElementX copyWithEnclosing(Element enclosingElement) {
return new FieldElementX(
new Identifier(token), enclosingElement, variables);
}
}
/// A field that was synthesized to recover from a compile-time error.
class ErroneousFieldElementX extends ElementX
with ConstantVariableMixin
implements FieldElementX {
final VariableList variables;
ErroneousFieldElementX(Identifier name, Element enclosingElement)
: variables = new VariableList(Modifiers.EMPTY)
..definitions = new VariableDefinitions(
null, Modifiers.EMPTY, new NodeList.singleton(name))
..type = const ResolutionDynamicType(),
super(name.source, ElementKind.FIELD, enclosingElement);
VariableDefinitions get definitionsCache => variables.definitions;
set definitionsCache(VariableDefinitions _) {
throw new UnsupportedError("definitionsCache=");
}
bool get hasNode => true;
VariableDefinitions get node => definitionsCache;
bool get hasResolvedAst => false;
ResolvedAst get resolvedAst {
throw new UnsupportedError("resolvedAst");
}
ResolutionDynamicType get type => const ResolutionDynamicType();
Token get token => node.getBeginToken();
get definitionCache {
throw new UnsupportedError("definitionCache");
}
set definitionCache(_) {
throw new UnsupportedError("definitionCache=");
}
get initializerCache {
throw new UnsupportedError("initializerCache");
}
set initializerCache(_) {
throw new UnsupportedError("initializerCache=");
}
void createDefinitions(VariableDefinitions definitions) {
throw new UnsupportedError("createDefinitions");
}
get initializer => null;
get definition => null;
bool get isMalformed => true;
get nestedClosures {
throw new UnsupportedError("nestedClosures");
}
set nestedClosures(_) {
throw new UnsupportedError("nestedClosures=");
}
// TODO(ahe): Should this throw or do nothing?
accept(ElementVisitor visitor, arg) {
return visitor.visitFieldElement(this, arg);
}
// TODO(ahe): Should return the context of the error site?
MemberElement get memberContext => this;
// TODO(ahe): Should return the definingElement of the error site?
AstElement get definingElement => this;
void reuseElement() {
throw new UnsupportedError("reuseElement");
}
FieldElementX copyWithEnclosing(Element enclosingElement) {
throw new UnsupportedError("copyWithEnclosing");
}
ResolutionDartType computeType(Resolution resolution) => type;
}
/// [Element] for a parameter-like element.
class FormalElementX extends ElementX
with AstElementMixin
implements FormalElement {
final VariableDefinitions definitions;
final Identifier identifier;
ResolutionDartType typeCache;
@override
List<ResolutionDartType> get typeVariables => functionSignature.typeVariables;
/**
* Function signature for a variable with a function type. The signature is
* kept to provide full information about parameter names through the mirror
* system.
*/
FunctionSignature _functionSignatureCache;
FormalElementX(ElementKind elementKind, FunctionTypedElement enclosingElement,
this.definitions, Identifier identifier)
: this.identifier = identifier,
super(identifier.source, elementKind, enclosingElement);
FormalElementX.unnamed(ElementKind elementKind,
FunctionTypedElement enclosingElement, this.definitions)
: this.identifier = null,
super("<unnamed>", elementKind, enclosingElement);
/// Whether this is an unnamed parameter in a Function type.
bool get isUnnamed => identifier == null;
FunctionTypedElement get functionDeclaration => enclosingElement;
Modifiers get modifiers => definitions.modifiers;
Token get position => identifier.getBeginToken();
Node parseNode(ParsingContext parsing) => definitions;
ResolutionDartType computeType(Resolution resolution) {
assert(type != null,
failedAt(this, "Parameter type has not been set for $this."));
return type;
}
ResolutionDartType get type {
assert(typeCache != null,
failedAt(this, "Parameter type has not been set for $this."));
return typeCache;
}
FunctionSignature get functionSignature {
assert(_functionSignatureCache != null,
failedAt(this, "Parameter signature has not been computed for $this."));
return _functionSignatureCache;
}
void set functionSignature(FunctionSignature value) {
assert(
_functionSignatureCache == null,
failedAt(
this, "Parameter signature has already been computed for $this."));
_functionSignatureCache = value;
typeCache = _functionSignatureCache.type;
}
bool get hasNode => true;
VariableDefinitions get node => definitions;
ResolutionFunctionType get functionType => type;
accept(ElementVisitor visitor, arg) {
return visitor.visitFormalElement(this, arg);
}
// A parameter is defined by the declaration element.
AstElement get definingElement => declaration;
}
/// [Element] for a formal parameter.
///
/// A [ParameterElementX] can be patched. A parameter of an external method is
/// patched with the corresponding parameter of the patch method. This is done
/// to ensure that default values on parameters are computed once (on the
/// origin parameter) but can be found through both the origin and the patch.
abstract class ParameterElementX extends FormalElementX
with PatchMixin<ParameterElement>, ConstantVariableMixin
implements ParameterElement {
final Expression initializer;
final bool isOptional;
final bool isNamed;
ParameterElementX(
ElementKind elementKind,
FunctionElement functionDeclaration,
VariableDefinitions definitions,
Identifier identifier,
this.initializer,
{this.isOptional: false,
this.isNamed: false})
: super(elementKind, functionDeclaration, definitions, identifier);
FunctionElement get functionDeclaration => enclosingElement;
ExecutableElement get executableContext => enclosingElement;
MemberElement get memberContext => executableContext.memberContext;
accept(ElementVisitor visitor, arg) {
return visitor.visitParameterElement(this, arg);
}
bool get isLocal => true;
String toString() {
if (isPatched) {
return 'origin ${super.toString()}';
} else if (isPatch) {
return 'patch ${super.toString()}';
}
return super.toString();
}
}
class LocalParameterElementX extends ParameterElementX
implements LocalParameterElement {
LocalParameterElementX(
FunctionElement functionDeclaration,
VariableDefinitions definitions,
Identifier identifier,
Expression initializer,
{bool isOptional: false,
bool isNamed: false})
: super(ElementKind.PARAMETER, functionDeclaration, definitions,
identifier, initializer,
isOptional: isOptional, isNamed: isNamed);
}
/// Parameters in constructors that directly initialize fields. For example:
/// `A(this.field)`.
class InitializingFormalElementX extends ParameterElementX
implements InitializingFormalElement {
final FieldElement fieldElement;
InitializingFormalElementX(
ConstructorElement constructorDeclaration,
VariableDefinitions variables,
Identifier identifier,
Expression initializer,
this.fieldElement,
{bool isOptional: false,
bool isNamed: false})
: super(ElementKind.INITIALIZING_FORMAL, constructorDeclaration,
variables, identifier, initializer,
isOptional: isOptional, isNamed: isNamed);
accept(ElementVisitor visitor, arg) {
return visitor.visitFieldParameterElement(this, arg);
}
MemberElement get memberContext => enclosingElement;
@override
bool get isFinal => true;
@override
bool get isLocal => true;
ConstructorElement get functionDeclaration => super.functionDeclaration;
}
// ignore: strong_mode_invalid_method_override_from_base
class ErroneousInitializingFormalElementX extends ParameterElementX
implements InitializingFormalElementX {
final ErroneousFieldElementX fieldElement;
ErroneousInitializingFormalElementX(
Identifier identifier, Element enclosingElement)
: this.fieldElement =
new ErroneousFieldElementX(identifier, enclosingElement),
super(ElementKind.INITIALIZING_FORMAL, enclosingElement, null,
identifier, null);
VariableDefinitions get definitions => fieldElement.node;
MemberElement get memberContext => enclosingElement;
bool get isLocal => false;
bool get isMalformed => true;
ResolutionDynamicType get type => const ResolutionDynamicType();
ConstructorElement get functionDeclaration => super.functionDeclaration;
}
class AbstractFieldElementX extends ElementX
with AbstractFieldElementCommon
implements AbstractFieldElement {
GetterElementX getter;
SetterElementX setter;
AbstractFieldElementX(String name, Element enclosing)
: super(name, ElementKind.ABSTRACT_FIELD, enclosing);
ResolutionDartType computeType(Compiler compiler) {
throw "internal error: AbstractFieldElement has no type";
}
Node parseNode(ParsingContext parsing) {
throw "internal error: AbstractFieldElement has no node";
}
Token get position {
// The getter and setter may be defined in two different
// compilation units. However, we know that one of them is
// non-null and defined in the same compilation unit as the
// abstract element.
// TODO(lrn): No we don't know that if the element from the same
// compilation unit is patched.
//
// We need to make sure that the position returned is relative to
// the compilation unit of the abstract element.
if (getter != null && identical(getter.compilationUnit, compilationUnit)) {
return getter.position;
} else {
return setter.position;
}
}
Modifiers get modifiers {
// The resolver ensures that the flags match (ignoring abstract).
if (getter != null) {
return new Modifiers.withFlags(getter.modifiers.nodes,
getter.modifiers.flags | Modifiers.FLAG_ABSTRACT);
} else {
return new Modifiers.withFlags(setter.modifiers.nodes,
setter.modifiers.flags | Modifiers.FLAG_ABSTRACT);
}
}
accept(ElementVisitor visitor, arg) {
return visitor.visitAbstractFieldElement(this, arg);
}
}
// TODO(johnniwinther): [FunctionSignature] should be merged with
// [FunctionType].
class FunctionSignatureX extends FunctionSignatureCommon
implements FunctionSignature {
final List<ResolutionDartType> typeVariables;
final List<FormalElement> requiredParameters;
final List<FormalElement> optionalParameters;
final int requiredParameterCount;
final int optionalParameterCount;
final bool optionalParametersAreNamed;
final List<FormalElement> orderedOptionalParameters;
final ResolutionFunctionType type;
final bool hasOptionalParameters;
FunctionSignatureX(
{this.typeVariables: const <ResolutionDartType>[],
this.requiredParameters: const <FormalElement>[],
this.requiredParameterCount: 0,
List<Element> optionalParameters: const <FormalElement>[],
this.optionalParameterCount: 0,
this.optionalParametersAreNamed: false,
this.orderedOptionalParameters: const <FormalElement>[],
this.type})
: optionalParameters = optionalParameters,
hasOptionalParameters = !optionalParameters.isEmpty;
}
abstract class BaseFunctionElementX extends ElementX
with PatchMixin<FunctionElement>, AstElementMixin
implements FunctionElement {
ResolutionDartType typeCache;
final Modifiers modifiers;
List<MethodElement> nestedClosures = new List<MethodElement>();
FunctionSignature _functionSignatureCache;
AsyncMarker _asyncMarker = AsyncMarker.SYNC;
BaseFunctionElementX(String name, ElementKind kind, Modifiers this.modifiers,
Element enclosing)
: super(name, kind, enclosing) {
assert(modifiers != null);
}
AsyncMarker get asyncMarker {
if (isPatched) {
return patch.asyncMarker;
}
return _asyncMarker;
}
void set asyncMarker(AsyncMarker value) {
if (isPatched) {
BaseFunctionElementX function = patch;
function.asyncMarker = value;
} else {
_asyncMarker = value;
}
}
bool get isExternal => modifiers.isExternal;
bool get isInstanceMember {
return isClassMember && !isConstructor && !isStatic;
}
ParameterStructure get parameterStructure =>
functionSignature.parameterStructure;
bool get hasFunctionSignature => _functionSignatureCache != null;
void _computeSignature(Resolution resolution) {
if (hasFunctionSignature) return;
functionSignature = resolution.resolveSignature(this);
}
FunctionSignature get functionSignature {
assert(hasFunctionSignature,
failedAt(this, "Function signature has not been computed for $this."));
return _functionSignatureCache;
}
void set functionSignature(FunctionSignature value) {
// TODO(johnniwinther): Strengthen the invariant to `!hasFunctionSignature`
// when checked mode checks are not enqueued eagerly.
assert(
!hasFunctionSignature || type == value.type,
failedAt(
this, "Function signature has already been computed for $this."));
_functionSignatureCache = value;
typeCache = _functionSignatureCache.type;
}
List<ParameterElement> get parameters {
// TODO(johnniwinther): Store the list directly, possibly by using List
// instead of Link in FunctionSignature.
List<ParameterElement> list = <ParameterElement>[];
functionSignature.forEachParameter((e) => list.add(e));
return list;
}
ResolutionFunctionType computeType(Resolution resolution) {
if (typeCache != null) return typeCache;
_computeSignature(resolution);
assert(typeCache != null,
failedAt(this, "Type cache expected to be set on $this."));
return typeCache;
}
ResolutionFunctionType get type {
assert(typeCache != null,
failedAt(this, "Type has not been computed for $this."));
return typeCache;
}
FunctionElement asFunctionElement() => this;
@override
Scope buildScope() => new TypeDeclarationScope(super.buildScope(), this);
String toString() {
if (isPatch) {
return 'patch ${super.toString()}';
} else if (isPatched) {
return 'origin ${super.toString()}';
} else {
return super.toString();
}
}
bool get isAbstract => false;
// A function is defined by the implementation element.
AstElement get definingElement => implementation;
@override
List<ResolutionDartType> get typeVariables => functionSignature.typeVariables;
// TODO(johnniwinther): Remove this.
FunctionElement get declaration => super.declaration;
// TODO(johnniwinther): Remove this.
FunctionElement get implementation => super.implementation;
// TODO(johnniwinther): Remove this.
FunctionElement get origin => super.origin;
// TODO(johnniwinther): Remove this.
FunctionElement get patch => super.patch;
}
abstract class FunctionElementX extends BaseFunctionElementX
with AnalyzableElementX
implements MethodElement {
FunctionElementX(
String name, ElementKind kind, Modifiers modifiers, Element enclosing)
: super(name, kind, modifiers, enclosing);
MemberElement get memberContext => this;
@override
SourceSpan get sourcePosition {
SourceSpan span = super.sourcePosition;
if (span != null && hasNode) {
FunctionExpression functionExpression = node.asFunctionExpression();
if (functionExpression != null) {
span = new SourceSpan.fromNode(span.uri, functionExpression);
}
}
return span;
}
void reuseElement() {
super.reuseElement();
nestedClosures.clear();
_functionSignatureCache = null;
typeCache = null;
}
}
abstract class MethodElementX extends FunctionElementX {
final bool hasBody;
MethodElementX(
String name,
ElementKind kind,
Modifiers modifiers,
Element enclosing,
// TODO(15101): Make this a named parameter.
this.hasBody)
: super(name, kind, modifiers, enclosing);
@override
bool get isAbstract {
return !modifiers.isExternal && !hasBody;
}
accept(ElementVisitor visitor, arg) {
return visitor.visitMethodElement(this, arg);
}
}
abstract class AccessorElementX extends MethodElementX
implements AccessorElement {
AbstractFieldElement abstractField;
AccessorElementX(String name, ElementKind kind, Modifiers modifiers,
Element enclosing, bool hasBody)
: super(name, kind, modifiers, enclosing, hasBody);
}
abstract class GetterElementX extends AccessorElementX
implements GetterElement {
GetterElementX(
String name, Modifiers modifiers, Element enclosing, bool hasBody)
: super(name, ElementKind.GETTER, modifiers, enclosing, hasBody);
accept(ElementVisitor visitor, arg) {
return visitor.visitGetterElement(this, arg);
}
}
abstract class SetterElementX extends AccessorElementX
implements SetterElement {
SetterElementX(
String name, Modifiers modifiers, Element enclosing, bool hasBody)
: super(name, ElementKind.SETTER, modifiers, enclosing, hasBody);
accept(ElementVisitor visitor, arg) {
return visitor.visitSetterElement(this, arg);
}
}
class LocalFunctionElementX extends BaseFunctionElementX
implements LocalFunctionElement {
final FunctionExpression node;
MethodElement callMethod;
LocalFunctionElementX(String name, FunctionExpression this.node,
ElementKind kind, Modifiers modifiers, ExecutableElement enclosing)
: super(name, kind, modifiers, enclosing);
ExecutableElement get executableContext => enclosingElement;
MemberElement get memberContext => executableContext.memberContext;
bool get hasNode => true;
FunctionExpression parseNode(ParsingContext parsing) => node;
Token get position {
// Use the name as position if this is not an unnamed closure.
if (node.name != null) {
return node.name.getBeginToken();
} else {
return node.getBeginToken();
}
}
bool get isLocal => true;
accept(ElementVisitor visitor, arg) {
return visitor.visitLocalFunctionElement(this, arg);
}
}
abstract class ConstantConstructorMixin implements ConstructorElement {
ConstantConstructor _constantConstructor;
ConstantConstructor get constantConstructor {
if (isPatch) {
ConstructorElement originConstructor = origin;
return originConstructor.constantConstructor;
}
if (!isConst || isFromEnvironmentConstructor) return null;
if (_constantConstructor == null) {
_constantConstructor = computeConstantConstructor(resolvedAst);
}
return _constantConstructor;
}
void set constantConstructor(ConstantConstructor value) {
if (isPatch) {
ConstantConstructorMixin originConstructor = origin;
originConstructor.constantConstructor = value;
} else {
assert(
isConst,
failedAt(
this,
"Constant constructor set on non-constant "
"constructor $this."));
assert(
!isFromEnvironmentConstructor,
failedAt(
this,
"Constant constructor set on fromEnvironment "
"constructor: $this."));
assert(
_constantConstructor == null || _constantConstructor == value,
failedAt(
this,
"Constant constructor already computed for $this:"
"Existing: $_constantConstructor, new: $value"));
_constantConstructor = value;
}
}
/// Returns the empty list of type variables by default.
@override
List<ResolutionDartType> get typeVariables => functionSignature.typeVariables;
}
abstract class ConstructorElementX extends FunctionElementX
with ConstantConstructorMixin, ConstructorElementCommon
implements ConstructorElement {
bool isRedirectingGenerativeInternal = false;
ConstructorElementX(
String name, ElementKind kind, Modifiers modifiers, Element enclosing)
: super(name, kind, modifiers, enclosing);
ConstructorElement _immediateRedirectionTarget;
PrefixElement _redirectionDeferredPrefix;
bool get isRedirectingGenerative {
if (isPatched) return patch.isRedirectingGenerative;
return isRedirectingGenerativeInternal;
}
bool get isRedirectingFactory => immediateRedirectionTarget != null;
// TODO(johnniwinther): This should also return true for cyclic redirecting
// generative constructors.
bool get isCyclicRedirection => effectiveTarget.isRedirectingFactory;
bool get isDefaultConstructor => false;
/// These fields are set by the post process queue when checking for cycles.
ConstructorElement effectiveTargetInternal;
ResolutionDartType _effectiveTargetType;
bool _isEffectiveTargetMalformed;
bool get hasEffectiveTarget {
if (isPatched) {
return patch.hasEffectiveTarget;
}
return effectiveTargetInternal != null;
}
void setImmediateRedirectionTarget(
ConstructorElement target, PrefixElement prefix) {
if (isPatched) {
patch.setImmediateRedirectionTarget(target, prefix);
} else {
assert(
_immediateRedirectionTarget == null,
failedAt(this,
"Immediate redirection target has already been set on $this."));
_immediateRedirectionTarget = target;
_redirectionDeferredPrefix = prefix;
}
}
ConstructorElement get immediateRedirectionTarget {
if (isPatched) {
return patch.immediateRedirectionTarget;
}
return _immediateRedirectionTarget;
}
PrefixElement get redirectionDeferredPrefix {
if (isPatched) {
return patch.redirectionDeferredPrefix;
}
return _redirectionDeferredPrefix;
}
void setEffectiveTarget(ConstructorElement target, ResolutionDartType type,
{bool isMalformed: false}) {
if (isPatched) {
patch.setEffectiveTarget(target, type, isMalformed: isMalformed);
} else {
assert(target != null,
failedAt(this, 'No effective target provided for $this.'));
assert(
effectiveTargetInternal == null,
failedAt(
this, 'Effective target has already been computed for $this.'));
assert(
!target.isMalformed || isMalformed,
failedAt(
this,
'Effective target is not marked as malformed for $this: '
'target=$target, type=$type, isMalformed: $isMalformed'));
assert(
isMalformed || type.isInterfaceType,
failedAt(
this,
'Effective target type is not an interface type for $this: '
'target=$target, type=$type, isMalformed: $isMalformed'));
effectiveTargetInternal = target;
_effectiveTargetType = type;
_isEffectiveTargetMalformed = isMalformed;
}
}
ConstructorElement get effectiveTarget {
if (isPatched) {
return patch.effectiveTarget;
}
if (isRedirectingFactory) {
assert(effectiveTargetInternal != null);
return effectiveTargetInternal;
}
return this;
}
ResolutionDartType get effectiveTargetType {
if (isPatched) {
return patch.effectiveTargetType;
}
assert(
_effectiveTargetType != null,
failedAt(this,
'Effective target type has not yet been computed for $this.'));
return _effectiveTargetType;
}
ResolutionDartType computeEffectiveTargetType(
ResolutionInterfaceType newType) {
if (isPatched) {
return patch.computeEffectiveTargetType(newType);
}
if (!isRedirectingFactory) return newType;
return effectiveTargetType.substByContext(newType);
}
bool get isEffectiveTargetMalformed {
if (isPatched) {
return patch.isEffectiveTargetMalformed;
}
if (!isRedirectingFactory) return false;
assert(_isEffectiveTargetMalformed != null,
failedAt(this, 'Malformedness has not yet been computed for $this.'));
return _isEffectiveTargetMalformed == true;
}
accept(ElementVisitor visitor, arg) {
return visitor.visitConstructorElement(this, arg);
}
ConstructorElement get definingConstructor => null;
ClassElement get enclosingClass => enclosingElement.declaration;
// TODO(johnniwinther): Remove this.
ConstructorElementX get declaration => super.declaration;
// TODO(johnniwinther): Remove this.
ConstructorElementX get implementation => super.implementation;
// TODO(johnniwinther): Remove this.
ConstructorElementX get origin => super.origin;
// TODO(johnniwinther): Remove this.
ConstructorElementX get patch => super.patch;
}
class DeferredLoaderGetterElementX extends GetterElementX {
final PrefixElement prefix;
DeferredLoaderGetterElementX(PrefixElement prefix)
: this.prefix = prefix,
super(Identifiers.loadLibrary, Modifiers.EMPTY, prefix, false) {
functionSignature =
new FunctionSignatureX(type: new ResolutionFunctionType(this));
}
bool get isClassMember => false;
bool get isSynthesized => true;
bool get isDeferredLoaderGetter => true;
bool get isTopLevel => true;
// By having position null, the enclosing elements location is printed in
// error messages.
Token get position => null;
FunctionExpression parseNode(ParsingContext parsing) => null;
bool get hasNode => false;
FunctionExpression get node => null;
bool get hasResolvedAst => true;
ResolvedAst get resolvedAst {
return new SynthesizedResolvedAst(
this, ResolvedAstKind.DEFERRED_LOAD_LIBRARY);
}
@override
SetterElement get setter => null;
}
class ConstructorBodyElementX extends BaseFunctionElementX
implements ConstructorBodyElement {
final ResolvedAst _resolvedAst;
final ConstructorElement constructor;
ConstructorBodyElementX(
ResolvedAst resolvedAst, ConstructorElement constructor)
: this._resolvedAst = resolvedAst,
this.constructor = constructor,
super(constructor.name, ElementKind.GENERATIVE_CONSTRUCTOR_BODY,
Modifiers.EMPTY, constructor.enclosingElement) {
functionSignature = constructor.functionSignature;
}
/// Returns the constructor body associated with the given constructor or
/// creates a new constructor body, if none can be found.
///
/// Returns `null` if the constructor does not have a body.
static ConstructorBodyElementX createFromResolvedAst(
ResolvedAst constructorResolvedAst) {
ConstructorElement constructor =
constructorResolvedAst.element.implementation;
assert(constructor.isGenerativeConstructor);
if (constructorResolvedAst.kind != ResolvedAstKind.PARSED) return null;
FunctionExpression node = constructorResolvedAst.node;
// If we know the body doesn't have any code, we don't generate it.
if (!node.hasBody) return null;
if (node.hasEmptyBody) return null;
ClassElement classElement = constructor.enclosingClass;
ConstructorBodyElement bodyElement;
classElement.forEachConstructorBody((ConstructorBodyElement body) {
if (body.constructor == constructor) {
// TODO(kasperl): Find a way of stopping the iteration
// through the backend members.
bodyElement = body;
}
});
if (bodyElement == null) {
bodyElement =
new ConstructorBodyElementX(constructorResolvedAst, constructor);
classElement.addConstructorBody(bodyElement);
if (constructor.isPatch) {
// Create origin body element for patched constructors.
ConstructorBodyElementX patch = bodyElement;
ConstructorBodyElementX origin = new ConstructorBodyElementX(
constructorResolvedAst, constructor.origin);
origin.applyPatch(patch);
classElement.origin.addConstructorBody(bodyElement.origin);
}
}
assert(bodyElement.isGenerativeConstructorBody);
return bodyElement;
}
bool get hasNode => _resolvedAst.kind == ResolvedAstKind.PARSED;
FunctionExpression get node => _resolvedAst.node;
bool get hasResolvedAst => true;
ResolvedAst get resolvedAst {
if (_resolvedAst.kind == ResolvedAstKind.PARSED) {
return new ParsedResolvedAst(declaration, _resolvedAst.node,
_resolvedAst.body, _resolvedAst.elements, _resolvedAst.sourceUri);
} else {
return new SynthesizedResolvedAst(declaration, _resolvedAst.kind);
}
}
List<MetadataAnnotation> get metadata => constructor.metadata;
bool get isInstanceMember => true;
ResolutionFunctionType computeType(Resolution resolution) {
DiagnosticReporter reporter = resolution.reporter;
reporter.internalError(this, '$this.computeType.');
return null;
}
int get sourceOffset => constructor.sourceOffset;
Token get position => constructor.position;
Element get outermostEnclosingMemberOrTopLevel => constructor;
AnalyzableElement get analyzableElement => constructor.analyzableElement;
accept(ElementVisitor visitor, arg) {
return visitor.visitConstructorBodyElement(this, arg);
}
MemberElement get memberContext => constructor;
}
/**
* A constructor that is not defined in the source code but rather implied by
* the language semantics.
*
* This class is used to represent default constructors and forwarding
* constructors for mixin applications.
*/
class SynthesizedConstructorElementX extends ConstructorElementX {
final ConstructorElement definingConstructor;
ResolvedAst _resolvedAst;
SynthesizedConstructorElementX.notForDefault(
String name, this.definingConstructor, Element enclosing)
: super(name, ElementKind.GENERATIVE_CONSTRUCTOR, Modifiers.EMPTY,
enclosing) {
_resolvedAst = new SynthesizedResolvedAst(
this, ResolvedAstKind.FORWARDING_CONSTRUCTOR);
}
SynthesizedConstructorElementX.forDefault(
this.definingConstructor, Element enclosing)
: super('', ElementKind.GENERATIVE_CONSTRUCTOR, Modifiers.EMPTY,
enclosing) {
functionSignature = new FunctionSignatureX(
type: new ResolutionFunctionType.synthesized(
const ResolutionDynamicType()));
_resolvedAst =
new SynthesizedResolvedAst(this, ResolvedAstKind.DEFAULT_CONSTRUCTOR);
}
bool get isDefaultConstructor {
return _resolvedAst.kind == ResolvedAstKind.DEFAULT_CONSTRUCTOR;
}
FunctionExpression parseNode(ParsingContext parsing) => null;
bool get hasNode => false;
FunctionExpression get node => null;
Token get position => enclosingElement.position;
bool get isSynthesized => true;
bool get hasResolvedAst => true;
ResolvedAst get resolvedAst => _resolvedAst;
ResolutionFunctionType get type {
if (isDefaultConstructor) {
return super.type;
} else {
// TODO(johnniwinther): Ensure that the function type substitutes type
// variables correctly.
return definingConstructor.type;
}
}
void _computeSignature(Resolution resolution) {
if (hasFunctionSignature) return;
if (definingConstructor.isMalformed) {
functionSignature = new FunctionSignatureX(
type:
new ResolutionFunctionType.synthesized(enclosingClass.thisType));
}
// TODO(johnniwinther): Ensure that the function signature (and with it the
// function type) substitutes type variables correctly.
definingConstructor.computeType(resolution);
functionSignature = definingConstructor.functionSignature;
}
accept(ElementVisitor visitor, arg) {
return visitor.visitConstructorElement(this, arg);
}
}
abstract class TypeDeclarationElementX implements TypeDeclarationElement {
/**
* The `this type` for this type declaration.
*
* The type of [:this:] is the generic type based on this element in which
* the type arguments are the declared type variables. For instance,
* [:List<E>:] for [:List:] and [:Map<K,V>:] for [:Map:].
*
* For a class declaration this is the type of [:this:].
*
* This type is computed in [computeType].
*/
GenericType thisTypeCache;
/**
* The raw type for this type declaration.
*
* The raw type is the generic type base on this element in which the type
* arguments are all [dynamic]. For instance [:List<dynamic>:] for [:List:]
* and [:Map<dynamic,dynamic>:] for [:Map:]. For non-generic classes [rawType]
* is the same as [thisType].
*
* The [rawType] field is a canonicalization of the raw type and should be
* used to distinguish explicit and implicit uses of the [dynamic]
* type arguments. For instance should [:List:] be the [rawType] of the
* [:List:] class element whereas [:List<dynamic>:] should be its own
* instantiation of [ResolutionInterfaceType] with [:dynamic:] as type
* argument. Using this distinction, we can print the raw type with type
* arguments only when the input source has used explicit type arguments.
*
* This type is computed together with [thisType] in [computeType].
*/
GenericType rawTypeCache;
GenericType get thisType {
assert(thisTypeCache != null,
failedAt(this, 'This type has not been computed for $this'));
return thisTypeCache;
}
GenericType get rawType {
assert(rawTypeCache != null,
failedAt(this, 'Raw type has not been computed for $this'));
return rawTypeCache;
}
GenericType createType(List<ResolutionDartType> typeArguments);
void setThisAndRawTypes(List<ResolutionDartType> typeParameters) {
assert(thisTypeCache == null,
failedAt(this, "This type has already been set on $this."));
assert(rawTypeCache == null,
failedAt(this, "Raw type has already been set on $this."));
thisTypeCache = createType(typeParameters);
if (typeParameters.isEmpty) {
rawTypeCache = thisTypeCache;
} else {
List<ResolutionDartType> dynamicParameters =
new List.filled(typeParameters.length, const ResolutionDynamicType());
rawTypeCache = createType(dynamicParameters);
}
}
List<ResolutionDartType> get typeVariables => thisType.typeArguments;
/**
* Creates the type variables, their type and corresponding element, for the
* type variables declared in [parameter] on [element]. The bounds of the type
* variables are not set until [element] has been resolved.
*/
List<ResolutionDartType> createTypeVariables(NodeList parameters) {
if (parameters == null) return const <ResolutionDartType>[];
// Create types and elements for type variable.
Link<Node> nodes = parameters.nodes;
List<ResolutionDartType> arguments =
new List.generate(nodes.slowLength(), (int index) {
TypeVariable node = nodes.head;
String variableName = node.name.source;
nodes = nodes.tail;
TypeVariableElementX variableElement =
new TypeVariableElementX(variableName, this, index, node);
ResolutionTypeVariableType variableType =
new ResolutionTypeVariableType(variableElement);
variableElement.typeCache = variableType;
return variableType;
}, growable: false);
return arguments;
}
bool get isResolved => resolutionState == STATE_DONE;
int get resolutionState;
}
abstract class BaseClassElementX extends ElementX
with
AstElementMixin,
AnalyzableElementX,
ClassElementCommon,
TypeDeclarationElementX,
PatchMixin<ClassElement>,
ClassMemberMixin
implements ClassElement {
final int id;
ResolutionInterfaceType supertype;
Link<ResolutionDartType> interfaces;
int supertypeLoadState;
int resolutionState;
bool isProxy = false;
bool hasIncompleteHierarchy = false;
OrderedTypeSet allSupertypesAndSelf;
BaseClassElementX(String name, Element enclosing, this.id, int initialState)
: supertypeLoadState = initialState,
resolutionState = initialState,
super(name, ElementKind.CLASS, enclosing);
int get hashCode => id;
bool get isUnnamedMixinApplication => false;
@override
bool get isEnumClass => false;
ResolutionInterfaceType computeType(Resolution resolution) {
if (isPatch) {
origin.computeType(resolution);
thisTypeCache = origin.thisType;
rawTypeCache = origin.rawType;
} else if (thisTypeCache == null) {
computeThisAndRawType(
resolution, computeTypeParameters(resolution.parsingContext));
}
return thisTypeCache;
}
void computeThisAndRawType(
Resolution resolution, List<ResolutionDartType> typeVariables) {
if (thisTypeCache == null) {
if (origin == null) {
setThisAndRawTypes(typeVariables);
} else {
thisTypeCache = origin.computeType(resolution);
rawTypeCache = origin.rawType;
}
}
}
@override
ResolutionInterfaceType createType(List<ResolutionDartType> typeArguments) {
return new ResolutionInterfaceType(this, typeArguments);
}
List<ResolutionDartType> computeTypeParameters(ParsingContext parsing);
bool get isObject {
assert(isResolved,
failedAt(this, "isObject has not been computed for $this."));
return supertype == null;
}
void ensureResolved(Resolution resolution) {
if (resolutionState == STATE_NOT_STARTED) {
resolution.resolveClass(this);
resolution.registerClass(this);
}
}
void setDefaultConstructor(
FunctionElement constructor, DiagnosticReporter reporter);
ConstructorElement lookupDefaultConstructor() {
ConstructorElement constructor = lookupConstructor("");
// This method might be called on constructors that have not been
// resolved. As we query the live world, we return `null` in such cases
// as no default constructor exists in the live world.
if (constructor != null &&
constructor.hasFunctionSignature &&
constructor.functionSignature.requiredParameterCount == 0) {
return constructor;
}
return null;
}
/**
* Returns the super class, if any.
*
* The returned element may not be resolved yet.
*/
ClassElement get superclass {
assert(supertypeLoadState == STATE_DONE,
failedAt(this, "Superclass has not been computed for $this."));
return supertype == null ? null : supertype.element;
}
// A class declaration is defined by the declaration element.
AstElement get definingElement => declaration;
ResolutionInterfaceType get thisType => super.thisType;
ResolutionInterfaceType get rawType => super.rawType;
// TODO(johnniwinther): Remove this.
ClassElement get declaration => super.declaration;
// TODO(johnniwinther): Remove this.
ClassElement get implementation => super.implementation;
// TODO(johnniwinther): Remove this.
ClassElement get origin => super.origin;
// TODO(johnniwinther): Remove this.
ClassElement get patch => super.patch;
}
abstract class ClassElementX extends BaseClassElementX {
Link<Element> localMembersReversed = const Link<Element>();
final ScopeX localScope = new ScopeX();
Link<Element> localMembersCache;
Link<Element> get localMembers {
if (localMembersCache == null) {
localMembersCache = localMembersReversed.reverse();
}
return localMembersCache;
}
ClassElementX(String name, Element enclosing, int id, int initialState)
: super(name, enclosing, id, initialState);
bool get isMixinApplication => false;
bool get hasLocalScopeMembers => !localScope.isEmpty;
void addMember(Element element, DiagnosticReporter reporter) {
localMembersCache = null;
localMembersReversed = localMembersReversed.prepend(element);
addToScope(element, reporter);
}
void addToScope(Element element, DiagnosticReporter reporter) {
if (element.isField && element.name == name) {
reporter.reportErrorMessage(element, MessageKind.MEMBER_USES_CLASS_NAME);
}
localScope.add(element, reporter);
}
Element localLookup(String elementName) {
Element result = localScope.lookup(elementName);
if (result == null && isPatch) {
result = origin.localLookup(elementName);
}
return result;
}
void forEachLocalMember(void f(Element member)) {
localMembers.forEach(f);
}
bool get hasConstructor {
// Search in scope to be sure we search patched constructors.
for (var element in localScope.values) {
if (element.isConstructor) return true;
}
return false;
}
void setDefaultConstructor(
FunctionElement constructor, DiagnosticReporter reporter) {
// The default constructor, although synthetic, is part of a class' API.
addMember(constructor, reporter);
}
List<ResolutionDartType> computeTypeParameters(ParsingContext parsing) {
ClassNode node = parseNode(parsing);
return createTypeVariables(node.typeParameters);
}
Scope buildScope() => new ClassScope(enclosingElement.buildScope(), this);
String toString() {
if (origin != null) {
return 'patch ${super.toString()}';
} else if (patch != null) {
return 'origin ${super.toString()}';
} else {
return super.toString();
}
}
}
/// This element is used to encode an enum class.
///
/// For instance
///
/// enum A { b, c, }
///
/// is modelled as
///
/// class A {
/// final int index;
///
/// const A(this.index);
///
/// String toString() {
/// return const <int, A>{0: 'A.b', 1: 'A.c'}[index];
/// }
///
/// static const A b = const A(0);
/// static const A c = const A(1);
///
/// static const List<A> values = const <A>[b, c];
/// }
///
/// where the `A` class is encoded using this element.
///
class EnumClassElementX extends ClassElementX
implements EnumClassElement, DeclarationSite {
final Enum node;
List<EnumConstantElement> _enumValues;
EnumClassElementX(String name, Element enclosing, int id, this.node)
: super(name, enclosing, id, STATE_NOT_STARTED);
@override
bool get hasNode => true;
@override
Token get position => node.name.token;
@override
bool get isEnumClass => true;
@override
Node parseNode(ParsingContext parsing) => node;
@override
accept(ElementVisitor visitor, arg) {
return visitor.visitEnumClassElement(this, arg);
}
List<ResolutionDartType> computeTypeParameters(ParsingContext parsing) =>
const <ResolutionDartType>[];
List<EnumConstantElement> get enumValues {
assert(_enumValues != null,
failedAt(this, "enumValues has not been computed for $this."));
return _enumValues;
}
void set enumValues(List<EnumConstantElement> values) {
assert(_enumValues == null,
failedAt(this, "enumValues has already been computed for $this."));
_enumValues = values;
}
@override
DeclarationSite get declarationSite => this;
}
/// This element is used to encode the implicit constructor in an enum class.
///
/// For instance
///
/// enum A { b, c, }
///
/// is modelled as
///
/// class A {
/// final int index;
///
/// const A(this.index);
///
/// String toString() {
/// return const <int, A>{0: 'A.b', 1: 'A.c'}[index];
/// }
///
/// static const A b = const A(0);
/// static const A c = const A(1);
///
/// static const List<A> values = const <A>[b, c];
/// }
///
/// where the `const A(...)` constructor is encoded using this element.
///
class EnumConstructorElementX extends ConstructorElementX {
final FunctionExpression node;
EnumConstructorElementX(
EnumClassElementX enumClass, Modifiers modifiers, this.node)
: super(
'', // Name.
ElementKind.GENERATIVE_CONSTRUCTOR,
modifiers,
enumClass);
@override
bool get hasNode => true;
@override
FunctionExpression parseNode(ParsingContext parsing) => node;
@override
SourceSpan get sourcePosition => enclosingClass.sourcePosition;
}
/// This element is used to encode the implicit methods in an enum class.
///
/// For instance
///
/// enum A { b, c, }
///
/// is modelled as
///
/// class A {
/// final int index;
///
/// const A(this.index);
///
/// String toString() {
/// return const <int, A>{0: 'A.b', 1: 'A.c'}[index];
/// }
///
/// static const A b = const A(0);
/// static const A c = const A(1);
///
/// static const List<A> values = const <A>[b, c];
/// }
///
/// where the `toString` method is encoded using this element.
///
class EnumMethodElementX extends MethodElementX {
final FunctionExpression node;
EnumMethodElementX(
String name, EnumClassElementX enumClass, Modifiers modifiers, this.node)
: super(name, ElementKind.FUNCTION, modifiers, enumClass, true);
@override
bool get hasNode => true;
@override
FunctionExpression parseNode(ParsingContext parsing) => node;
@override
SourceSpan get sourcePosition => enclosingClass.sourcePosition;
}
/// This element is used to encode the initializing formal of the implicit
/// constructor in an enum class.
///
/// For instance
///
/// enum A { b, c, }
///
/// is modelled as
///
/// class A {
/// final int index;
///
/// const A(this.index);
///
/// String toString() {
/// return const <int, A>{0: 'A.b', 1: 'A.c'}[index];
/// }
///
/// static const A b = const A(0);
/// static const A c = const A(1);
///
/// static const List<A> values = const <A>[b, c];
/// }
///
/// where the `this.index` formal is encoded using this element.
///
class EnumFormalElementX extends InitializingFormalElementX {
EnumFormalElementX(
ConstructorElement constructor,
VariableDefinitions variables,
Identifier identifier,
EnumFieldElementX fieldElement)
: super(constructor, variables, identifier, null, fieldElement) {
typeCache = fieldElement.type;
}
@override
SourceSpan get sourcePosition => enclosingClass.sourcePosition;
}
/// This element is used to encode the implicitly fields in an enum class.
///
/// For instance
///
/// enum A { b, c, }
///
/// is modelled as
///
/// class A {
/// final int index;
///
/// const A(this.index);
///
/// String toString() {
/// return const <int, A>{0: 'A.b', 1: 'A.c'}[index];
/// }
///
/// static const A b = const A(0);
/// static const A c = const A(1);
///
/// static const List<A> values = const <A>[b, c];
/// }
///
/// where the `index` and `values` fields are encoded using this element.
///
class EnumFieldElementX extends FieldElementX {
EnumFieldElementX(Identifier name, EnumClassElementX enumClass,
VariableList variableList, Node definition,
[Expression initializer])
: super(name, enumClass, variableList) {
definitionsCache = new VariableDefinitions(
null, variableList.modifiers, new NodeList.singleton(definition));
initializerCache = initializer;
definitionCache = definition;
}
@override
SourceSpan get sourcePosition => enclosingClass.sourcePosition;
}
/// This element is used to encode the constant value in an enum class.
///
/// For instance
///
/// enum A { b, c, }
///
/// is modelled as
///
/// class A {
/// final int index;
///
/// const A(this.index);
///
/// String toString() {
/// return const <int, A>{0: 'A.b', 1: 'A.c'}[index];
/// }
///
/// static const A b = const A(0);
/// static const A c = const A(1);
///
/// static const List<A> values = const <A>[b, c];
/// }
///
/// where the `b` and `c` fields are encoded using this element.
///
class EnumConstantElementX extends EnumFieldElementX
implements EnumConstantElement {
final int index;
EnumConstantElementX(
Identifier name,
EnumClassElementX enumClass,
VariableList variableList,
Node definition,
Expression initializer,
this.index)
: super(name, enumClass, variableList, definition, initializer);
@override
SourceSpan get sourcePosition {
return new SourceSpan(enclosingClass.sourcePosition.uri,
position.charOffset, position.charEnd);
}
EnumClassElement get enclosingClass => super.enclosingClass;
}
abstract class MixinApplicationElementX extends BaseClassElementX
with MixinApplicationElementCommon
implements MixinApplicationElement {
Link<ConstructorElement> constructors = new Link<ConstructorElement>();
ResolutionInterfaceType mixinType;
MixinApplicationElementX(String name, Element enclosing, int id)
: super(name, enclosing, id, STATE_NOT_STARTED);
ClassElement get mixin => mixinType != null ? mixinType.element : null;
bool get isMixinApplication => true;
bool get hasConstructor => !constructors.isEmpty;
bool get hasLocalScopeMembers => !constructors.isEmpty;
get patch => null;
get origin => null;
bool get hasNode => true;
Token get position => node.getBeginToken();
Node parseNode(ParsingContext parsing) => node;
void addMember(Element element, DiagnosticReporter reporter) {
throw new UnsupportedError("Cannot add member to $this.");
}
void addToScope(Element element, DiagnosticReporter reporter) {
reporter.internalError(this, 'Cannot add to scope of $this.');
}
void addConstructor(FunctionElement constructor) {
constructors = constructors.prepend(constructor);
}
void setDefaultConstructor(
FunctionElement constructor, DiagnosticReporter reporter) {
assert(!hasConstructor);
addConstructor(constructor);
}
List<ResolutionDartType> computeTypeParameters(ParsingContext parsing) {
NamedMixinApplication named = node.asNamedMixinApplication();
if (named == null) {
failedAt(
node,
"Type variables on unnamed mixin applications must be set on "
"creation.");
}
return createTypeVariables(named.typeParameters);
}
accept(ElementVisitor visitor, arg) {
return visitor.visitMixinApplicationElement(this, arg);
}
}
class NamedMixinApplicationElementX extends MixinApplicationElementX
implements DeclarationSite {
final NamedMixinApplication node;
NamedMixinApplicationElementX(
String name, CompilationUnitElement enclosing, int id, this.node)
: super(name, enclosing, id);
Modifiers get modifiers => node.modifiers;
DeclarationSite get declarationSite => this;
ClassElement get subclass => null;
}
class UnnamedMixinApplicationElementX extends MixinApplicationElementX {
final Node node;
final ClassElement subclass;
UnnamedMixinApplicationElementX(
String name, ClassElement subclass, int id, this.node)
: this.subclass = subclass,
super(name, subclass.compilationUnit, id);
bool get isUnnamedMixinApplication => true;
bool get isAbstract => true;
}
class LabelDefinitionX extends LabelDefinition<Node> {
final Label label;
final String labelName;
final JumpTargetX target;
bool isBreakTarget = false;
bool isContinueTarget = false;
LabelDefinitionX(Label label, String labelName, this.target)
: this.label = label,
this.labelName = labelName;
// In case of a synthetic label, just use [labelName] for identifying the
// label.
String get name => label == null ? labelName : label.identifier.source;
void setBreakTarget() {
isBreakTarget = true;
target.isBreakTarget = true;
}
void setContinueTarget() {
isContinueTarget = true;
target.isContinueTarget = true;
}
String toString() => 'Label:${name}';
}
class JumpTargetX extends JumpTarget<Node> {
final ExecutableElement executableContext;
final Node statement;
final int nestingLevel;
List<LabelDefinition<Node>> labels = <LabelDefinition<Node>>[];
bool isBreakTarget = false;
bool isContinueTarget = false;
final int hashCode = ElementX.newHashCode();
JumpTargetX(this.statement, this.nestingLevel, this.executableContext);
MemberElement get memberContext => executableContext.memberContext;
LabelDefinition<Node> addLabel(Label label, String labelName,
{bool isBreakTarget: false, bool isContinueTarget: false}) {
LabelDefinitionX result = new LabelDefinitionX(label, labelName, this);
labels.add(result);
if (isBreakTarget) {
result.setBreakTarget();
}
if (isContinueTarget) {
result.setContinueTarget();
}
return result;
}
bool get isSwitch => statement is SwitchStatement;
bool get isSwitchCase => statement is SwitchCase;
String toString() => 'Target:$statement';
}
class TypeVariableElementX extends ElementX
with AstElementMixin
implements TypeVariableElement {
final int index;
final Node node;
ResolutionTypeVariableType typeCache;
ResolutionDartType boundCache;
TypeVariableElementX(
String name, GenericElement enclosing, this.index, this.node)
: super(name, ElementKind.TYPE_VARIABLE, enclosing);
GenericElement get typeDeclaration => enclosingElement;
ResolutionTypeVariableType computeType(Resolution resolution) => type;
ResolutionTypeVariableType get type {
assert(
typeCache != null, failedAt(this, "Type has not been set on $this."));
return typeCache;
}
ResolutionDartType get bound {
assert(
boundCache != null, failedAt(this, "Bound has not been set on $this."));
return boundCache;
}
bool get hasNode => true;
Node parseNode(ParsingContext parsing) => node;
Token get position => node.getBeginToken();
accept(ElementVisitor visitor, arg) {
return visitor.visitTypeVariableElement(this, arg);
}
// A type variable cannot be patched therefore defines itself.
AstElement get definingElement => this;
}
/**
* A single metadata annotation.
*
* For example, consider:
*
* class Data {
* const Data();
* }
*
* const data = const Data();
*
* @data
* class Foo {}
*
* @data @data
* class Bar {}
*
* In this example, there are three instances of [MetadataAnnotation]
* and they correspond each to a location in the source code where
* there is an at-sign, '@'. The [constant] of each of these instances
* are the same compile-time constant, [: const Data() :].
*
* The mirror system does not have a concept matching this class.
*/
abstract class MetadataAnnotationX implements MetadataAnnotation {
/**
* The compile-time constant which this annotation resolves to.
* In the mirror system, this would be an object mirror.
*/
ConstantExpression constant;
Element annotatedElement;
int resolutionState;
/**
* The beginning token of this annotation, or [:null:] if it is synthetic.
*/
Token get beginToken;
Token get endToken;
final int hashCode = ElementX.newHashCode();
MetadataAnnotationX([this.resolutionState = STATE_NOT_STARTED]);
MetadataAnnotation ensureResolved(Resolution resolution) {
if (annotatedElement.isClass || annotatedElement.isTypedef) {
TypeDeclarationElement typeDeclaration = annotatedElement;
typeDeclaration.ensureResolved(resolution);
}
if (resolutionState == STATE_NOT_STARTED) {
resolution.resolveMetadataAnnotation(this);
}
return this;
}
Node parseNode(ParsingContext parsing);
SourceSpan get sourcePosition {
Uri uri = annotatedElement.compilationUnit.script.resourceUri;
return new SourceSpan.fromTokens(uri, beginToken, endToken);
}
String toString() => 'MetadataAnnotation($constant, $resolutionState)';
}
/// Metadata annotation on a parameter.
class ParameterMetadataAnnotation extends MetadataAnnotationX {
final Metadata metadata;
ParameterMetadataAnnotation(Metadata this.metadata);
Node parseNode(ParsingContext parsing) => metadata.expression;
Token get beginToken => metadata.getBeginToken();
Token get endToken => metadata.getEndToken();
bool get hasNode => true;
Metadata get node => metadata;
}
/// Mixin for the implementation of patched elements.
///
/// See `patch_parser.dart` for a description of the terminology.
abstract class PatchMixin<E extends Element> implements Element {
E patch = null;
E origin = null;
bool get isPatch => origin != null;
bool get isPatched => patch != null;
bool get isImplementation => !isPatched;
bool get isDeclaration => !isPatch;
E get implementation => isPatched ? patch : this;
E get declaration => isPatch ? origin : this;
/// Applies a patch to this element. This method must be called at most once.
void applyPatch(PatchMixin<E> patch) {
assert(this.patch == null, failedAt(this, "Element is patched twice."));
assert(this.origin == null, failedAt(this, "Origin element is a patch."));
assert(patch.origin == null, failedAt(patch, "Element is patched twice."));
assert(patch.patch == null, failedAt(patch, "Patch element is patched."));
this.patch = patch as E;
patch.origin = this as E;
}
}
/// Abstract implementation of the [AstElement] interface.
abstract class AstElementMixin implements AstElement {
/// The element whose node defines this element.
///
/// For patched functions the defining element is the patch element found
/// through [implementation] since its node define the implementation of the
/// function. For patched classes the defining element is the origin element
/// found through [declaration] since its node define the inheritance relation
/// for the class. For unpatched elements the defining element is the element
/// itself.
AstElement get definingElement;
bool get hasResolvedAst {
return definingElement.hasNode && definingElement.hasTreeElements;
}
ResolvedAst get resolvedAst {
Node node = definingElement.node;
Node body;
if (definingElement.isField) {
FieldElement field = definingElement;
body = field.initializer;
} else if (node != null && node.asFunctionExpression() != null) {
body = node.asFunctionExpression().body;
}
return new ParsedResolvedAst(
declaration,
node,
body,
definingElement.treeElements,
definingElement.compilationUnit.script.resourceUri);
}
}