blob: 0f642efa3e31af68c2bcfd0fd7ac5a37cc1f67d7 [file] [log] [blame]
// Copyright (c) 2016, 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.
/**
* This library is capable of producing linked summaries from unlinked
* ones (or prelinked ones). It functions by building a miniature
* element model to represent the contents of the summaries, and then
* scanning the element model to gather linked information and adding
* it to the summary data structures.
*
* The reason we use a miniature element model to do the linking
* (rather than resynthesizing the full element model from the
* summaries) is that it is expected that we will only need to
* traverse a small subset of the element properties in order to link.
* Resynthesizing only those properties that we need should save
* substantial CPU time.
*
* The element model implements the same interfaces as the full
* element model, so we can re-use code elsewhere in the analysis
* engine to do the linking. However, only a small subset of the
* methods and getters defined in the full element model are
* implemented here. To avoid static warnings, each element model
* class contains an implementation of `noSuchMethod`.
*
* The miniature element model follows the following design
* principles:
*
* - With few exceptions, resynthesis is done incrementally on demand,
* so that we don't pay the cost of resynthesizing elements (or
* properties of elements) that aren't referenced from a part of the
* element model that is relevant to linking.
*
* - Computation of values in the miniature element model is similar
* to the task model, but much lighter weight. Instead of declaring
* tasks and their relationships using classes, each task is simply
* a method (frequently a getter) that computes a value. Instead of
* using a general purpose cache, values are cached by the methods
* themselves in private fields (with `null` typically representing
* "not yet cached").
*
* - No attempt is made to detect cyclic dependencies due to bugs in
* the analyzer. This saves time because dependency evaluation
* doesn't have to be a separate step from evaluating a value; we
* can simply call the getter.
*
* - However, for cases where cyclic dependencies may occur in the
* absence of analyzer bugs (e.g. because of errors in the code
* being analyzed, or cycles between top level and static variables
* undergoing type inference), we do precompute dependencies, and we
* use Tarjan's strongly connected components algorithm to detect
* cycles.
*
* - As much as possible, bookkeeping data is pointed to directly by
* the element objects, rather than being stored in maps.
*
* - Where possible, we favor method dispatch instead of "is" and "as"
* checks. E.g. see [ReferenceableElementForLink.asConstructor].
*/
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/constant/value.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary/prelink.dart';
import 'package:analyzer/src/task/strong_mode.dart';
/**
* Link together the build unit consisting of [libraryUris], using
* [getDependency] to fetch the [LinkedLibrary] objects from other
* build units, and [getUnit] to fetch the [UnlinkedUnit] objects from
* both this build unit and other build units.
*
* The [strong] flag controls whether type inference is performed in strong
* mode or spec mode. Note that in spec mode, the only types that are inferred
* are the types of initializing formals, which are inferred from the types of
* the corresponding fields.
*
* A map is returned whose keys are the URIs of the libraries in this
* build unit, and whose values are the corresponding
* [LinkedLibraryBuilder]s.
*/
Map<String, LinkedLibraryBuilder> link(Set<String> libraryUris,
GetDependencyCallback getDependency, GetUnitCallback getUnit, bool strong) {
Map<String, LinkedLibraryBuilder> linkedLibraries =
<String, LinkedLibraryBuilder>{};
for (String absoluteUri in libraryUris) {
Uri uri = Uri.parse(absoluteUri);
UnlinkedUnit getRelativeUnit(String relativeUri) =>
getUnit(resolveRelativeUri(uri, Uri.parse(relativeUri)).toString());
linkedLibraries[absoluteUri] = prelink(
getUnit(absoluteUri),
getRelativeUnit,
(String relativeUri) => getRelativeUnit(relativeUri)?.publicNamespace);
}
relink(linkedLibraries, getDependency, getUnit, strong);
return linkedLibraries;
}
/**
* Given [libraries] (a map from URI to [LinkedLibraryBuilder]
* containing correct prelinked information), rebuild linked
* information, using [getDependency] to fetch the [LinkedLibrary]
* objects from other build units, and [getUnit] to fetch the
* [UnlinkedUnit] objects from both this build unit and other build
* units.
*
* The [strong] flag controls whether type inference is performed in strong
* mode or spec mode. Note that in spec mode, the only types that are inferred
* are the types of initializing formals, which are inferred from the types of
* the corresponding fields.
*/
void relink(Map<String, LinkedLibraryBuilder> libraries,
GetDependencyCallback getDependency, GetUnitCallback getUnit, bool strong) {
new _Linker(libraries, getDependency, getUnit, strong).link();
}
/**
* Create an [EntityRefBuilder] representing the given [type], in a form
* suitable for inclusion in [LinkedUnit.types]. [compilationUnit] is the
* compilation unit in which the type will be used. If [slot] is provided, it
* is stored in [EntityRefBuilder.slot].
*/
EntityRefBuilder _createLinkedType(
DartType type,
CompilationUnitElementInBuildUnit compilationUnit,
TypeParameterizedElementForLink typeParameterContext,
{int slot}) {
EntityRefBuilder result = new EntityRefBuilder(slot: slot);
if (type is InterfaceType) {
ClassElementForLink element = type.element;
result.reference = compilationUnit.addReference(element);
if (type.typeArguments.isNotEmpty) {
result.typeArguments = type.typeArguments
.map((DartType t) =>
_createLinkedType(t, compilationUnit, typeParameterContext))
.toList();
}
return result;
} else if (type is VoidTypeImpl) {
result.reference = compilationUnit.addRawReference('void');
return result;
} else if (type is BottomTypeImpl) {
result.reference = compilationUnit.addRawReference('*bottom*');
return result;
} else if (type is TypeParameterType) {
TypeParameterElementForLink element = type.element;
result.paramReference =
typeParameterContext.typeParameterNestingLevel - element.nestingLevel;
return result;
} else if (type is FunctionType) {
Element element = type.element;
if (element is FunctionElementForLink_FunctionTypedParam) {
result.reference =
compilationUnit.addReference(element.enclosingExecutable);
result.implicitFunctionTypeIndices = element.implicitFunctionTypeIndices;
if (type.typeArguments.isNotEmpty) {
result.typeArguments = type.typeArguments
.map((DartType t) =>
_createLinkedType(t, compilationUnit, typeParameterContext))
.toList();
}
return result;
}
// TODO(paulberry): implement other cases.
throw new UnimplementedError('${element.runtimeType}');
}
// TODO(paulberry): implement other cases.
throw new UnimplementedError('${type.runtimeType}');
}
/**
* Type of the callback used by [link] and [relink] to request
* [LinkedLibrary] objects from other build units.
*/
typedef LinkedLibrary GetDependencyCallback(String absoluteUri);
/**
* Type of the callback used by [link] and [relink] to request
* [UnlinkedUnit] objects.
*/
typedef UnlinkedUnit GetUnitCallback(String absoluteUri);
/**
* Element representing a class or enum resynthesized from a summary
* during linking.
*/
abstract class ClassElementForLink
implements ClassElementImpl, ReferenceableElementForLink {
Map<String, ReferenceableElementForLink> _containedNames;
@override
final CompilationUnitElementForLink enclosingElement;
@override
bool hasBeenInferred;
ClassElementForLink(CompilationUnitElementForLink enclosingElement)
: enclosingElement = enclosingElement,
hasBeenInferred = !enclosingElement.isInBuildUnit;
@override
ConstructorElementForLink get asConstructor => unnamedConstructor;
@override
ConstVariableNode get asConstVariable {
// When a class name is used as a constant variable, it doesn't depend on
// anything, so it is not necessary to include it in the constant
// dependency graph.
return null;
}
@override
DartType get asStaticType =>
enclosingElement.enclosingElement._linker.typeProvider.typeType;
@override
TypeInferenceNode get asTypeInferenceNode => null;
@override
List<ConstructorElementForLink> get constructors;
@override
List<FieldElementForLink> get fields;
/**
* Indicates whether this is the core class `Object`.
*/
bool get isObject;
@override
LibraryElementForLink get library => enclosingElement.library;
@override
String get name;
@override
ConstructorElementForLink get unnamedConstructor;
@override
ReferenceableElementForLink getContainedName(String name) {
if (_containedNames == null) {
_containedNames = <String, ReferenceableElementForLink>{};
// TODO(paulberry): what's the correct way to handle name conflicts?
for (ConstructorElementForLink constructor in constructors) {
_containedNames[constructor.name] = constructor;
}
for (FieldElementForLink field in fields) {
// TODO(paulberry): do we need to handle nonstatic fields for
// consistent behavior with erroneous code?
if (field.isStatic) {
_containedNames[field.name] = field;
}
}
// TODO(paulberry): add methods.
}
return _containedNames.putIfAbsent(
name, () => UndefinedElementForLink.instance);
}
/**
* Perform type inference and cycle detection on this class and
* store the resulting information in [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit);
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Element representing a class resynthesized from a summary during
* linking.
*/
class ClassElementForLink_Class extends ClassElementForLink
with TypeParameterizedElementForLink {
/**
* The unlinked representation of the class in the summary.
*/
final UnlinkedClass _unlinkedClass;
List<ConstructorElementForLink> _constructors;
ConstructorElementForLink _unnamedConstructor;
bool _unnamedConstructorComputed = false;
List<FieldElementForLink_ClassField> _fields;
InterfaceType _supertype;
InterfaceType _type;
List<MethodElementForLink> _methods;
List<InterfaceType> _mixins;
List<InterfaceType> _interfaces;
List<PropertyAccessorElementForLink> _accessors;
ClassElementForLink_Class(
CompilationUnitElementForLink enclosingElement, this._unlinkedClass)
: super(enclosingElement);
@override
List<PropertyAccessorElementForLink> get accessors {
if (_accessors == null) {
_accessors = <PropertyAccessorElementForLink>[];
Map<String, SyntheticVariableElementForLink> syntheticVariables =
<String, SyntheticVariableElementForLink>{};
for (UnlinkedExecutable unlinkedExecutable
in _unlinkedClass.executables) {
if (unlinkedExecutable.kind == UnlinkedExecutableKind.getter ||
unlinkedExecutable.kind == UnlinkedExecutableKind.setter) {
String name = unlinkedExecutable.name;
if (unlinkedExecutable.kind == UnlinkedExecutableKind.setter) {
assert(name.endsWith('='));
name = name.substring(0, name.length - 1);
}
SyntheticVariableElementForLink syntheticVariable = syntheticVariables
.putIfAbsent(name, () => new SyntheticVariableElementForLink());
PropertyAccessorElementForLink accessor =
new PropertyAccessorElementForLink(
this, unlinkedExecutable, syntheticVariable);
_accessors.add(accessor);
if (unlinkedExecutable.kind == UnlinkedExecutableKind.getter) {
syntheticVariable._getter = accessor;
} else {
syntheticVariable._setter = accessor;
}
}
}
}
return _accessors;
}
@override
List<ConstructorElementForLink> get constructors {
if (_constructors == null) {
_constructors = <ConstructorElementForLink>[];
for (UnlinkedExecutable unlinkedExecutable
in _unlinkedClass.executables) {
if (unlinkedExecutable.kind == UnlinkedExecutableKind.constructor) {
_constructors
.add(new ConstructorElementForLink(this, unlinkedExecutable));
}
}
}
return _constructors;
}
@override
String get displayName => _unlinkedClass.name;
@override
TypeParameterizedElementForLink get enclosingTypeParameterContext => null;
@override
List<FieldElementForLink_ClassField> get fields {
if (_fields == null) {
_fields = <FieldElementForLink_ClassField>[];
for (UnlinkedVariable field in _unlinkedClass.fields) {
_fields.add(new FieldElementForLink_ClassField(this, field));
}
}
return _fields;
}
@override
List<InterfaceType> get interfaces => _interfaces ??=
_unlinkedClass.interfaces.map(_computeInterfaceType).toList();
@override
bool get isObject => _unlinkedClass.hasNoSupertype;
@override
LibraryElementForLink get library => enclosingElement.library;
@override
List<MethodElementForLink> get methods {
if (_methods == null) {
_methods = <MethodElementForLink>[];
for (UnlinkedExecutable unlinkedExecutable
in _unlinkedClass.executables) {
if (unlinkedExecutable.kind ==
UnlinkedExecutableKind.functionOrMethod) {
_methods.add(new MethodElementForLink(this, unlinkedExecutable));
}
}
}
return _methods;
}
@override
List<InterfaceType> get mixins =>
_mixins ??= _unlinkedClass.mixins.map(_computeInterfaceType).toList();
@override
String get name => _unlinkedClass.name;
@override
InterfaceType get supertype {
if (isObject) {
return null;
}
return _supertype ??= _computeInterfaceType(_unlinkedClass.supertype);
}
@override
DartType get type =>
_type ??= buildType((int i) => typeParameterTypes[i], null);
@override
ConstructorElementForLink get unnamedConstructor {
if (!_unnamedConstructorComputed) {
for (ConstructorElementForLink constructor in constructors) {
if (constructor.name.isEmpty) {
_unnamedConstructor = constructor;
break;
}
}
_unnamedConstructorComputed = true;
}
return _unnamedConstructor;
}
@override
List<UnlinkedTypeParam> get _unlinkedTypeParams =>
_unlinkedClass.typeParameters;
@override
DartType buildType(
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
int numTypeParameters = _unlinkedClass.typeParameters.length;
if (numTypeParameters != 0) {
List<DartType> typeArguments = new List<DartType>(numTypeParameters);
for (int i = 0; i < numTypeParameters; i++) {
typeArguments[i] = getTypeArgument(i);
}
return new InterfaceTypeImpl.elementWithNameAndArgs(
this, name, typeArguments);
} else {
return _type ??= new InterfaceTypeImpl(this);
}
}
@override
void link(CompilationUnitElementInBuildUnit compilationUnit) {
for (ConstructorElementForLink constructorElement in constructors) {
constructorElement.link(compilationUnit);
}
if (compilationUnit.library._linker.strongMode) {
for (MethodElementForLink methodElement in methods) {
methodElement.link(compilationUnit);
}
for (PropertyAccessorElementForLink propertyAccessorElement
in accessors) {
propertyAccessorElement.link(compilationUnit);
}
for (FieldElementForLink_ClassField fieldElement in fields) {
fieldElement.link(compilationUnit);
}
}
}
/**
* Convert [typeRef] into an [InterfaceType].
*/
InterfaceType _computeInterfaceType(EntityRef typeRef) {
if (_unlinkedClass.supertype != null) {
DartType supertype = enclosingElement._resolveTypeRef(typeRef, this);
if (supertype is InterfaceType) {
return supertype;
}
// In the event that the supertype isn't an interface type (which may
// happen in the event of erroneous code) just fall through and pretend
// the supertype is `Object`.
}
return enclosingElement.enclosingElement._linker.typeProvider.objectType;
}
}
/**
* Element representing an enum resynthesized from a summary during
* linking.
*/
class ClassElementForLink_Enum extends ClassElementForLink {
/**
* The unlinked representation of the enum in the summary.
*/
final UnlinkedEnum _unlinkedEnum;
InterfaceType _type;
List<FieldElementForLink_EnumField> _fields;
ClassElementForLink_Enum(
CompilationUnitElementForLink enclosingElement, this._unlinkedEnum)
: super(enclosingElement);
@override
List<PropertyAccessorElement> get accessors {
// TODO(paulberry): do we need to include synthetic accessors?
return const [];
}
@override
List<ConstructorElementForLink> get constructors => const [];
@override
String get displayName => _unlinkedEnum.name;
@override
List<FieldElementForLink_EnumField> get fields {
if (_fields == null) {
_fields = <FieldElementForLink_EnumField>[];
_fields.add(new FieldElementForLink_EnumField(null));
for (UnlinkedEnumValue value in _unlinkedEnum.values) {
_fields.add(new FieldElementForLink_EnumField(value));
}
}
return _fields;
}
@override
List<InterfaceType> get interfaces => const [];
@override
bool get isObject => false;
@override
List<MethodElement> get methods => const [];
@override
List<InterfaceType> get mixins => const [];
@override
String get name => _unlinkedEnum.name;
@override
InterfaceType get supertype => library._linker.typeProvider.objectType;
@override
ConstructorElementForLink get unnamedConstructor => null;
@override
DartType buildType(DartType getTypeArgument(int i),
List<int> implicitFunctionTypeIndices) =>
_type ??= new InterfaceTypeImpl(this);
@override
void link(CompilationUnitElementInBuildUnit compilationUnit) {}
}
/**
* Element representing a compilation unit resynthesized from a
* summary during linking.
*/
abstract class CompilationUnitElementForLink implements CompilationUnitElement {
/**
* The unlinked representation of the compilation unit in the
* summary.
*/
final UnlinkedUnit _unlinkedUnit;
/**
* For each entry in [UnlinkedUnit.references], the element referred
* to by the reference, or `null` if it hasn't been located yet.
*/
final List<ReferenceableElementForLink> _references;
List<ClassElementForLink_Class> _types;
Map<String, ReferenceableElementForLink> _containedNames;
List<TopLevelVariableElementForLink> _topLevelVariables;
List<ClassElementForLink_Enum> _enums;
/**
* Index of this unit in the list of units in the enclosing library.
*/
final int unitNum;
CompilationUnitElementForLink(UnlinkedUnit unlinkedUnit, this.unitNum)
: _references = new List<ReferenceableElementForLink>(
unlinkedUnit.references.length),
_unlinkedUnit = unlinkedUnit;
@override
LibraryElementForLink get enclosingElement;
@override
List<ClassElementForLink_Enum> get enums {
if (_enums == null) {
_enums = <ClassElementForLink_Enum>[];
for (UnlinkedEnum unlinkedEnum in _unlinkedUnit.enums) {
_enums.add(new ClassElementForLink_Enum(this, unlinkedEnum));
}
}
return _enums;
}
/**
* Indicates whether this compilation element is part of the build unit
* currently being linked.
*/
bool get isInBuildUnit;
@override
LibraryElementForLink get library => enclosingElement;
@override
List<TopLevelVariableElementForLink> get topLevelVariables {
if (_topLevelVariables == null) {
_topLevelVariables = <TopLevelVariableElementForLink>[];
for (UnlinkedVariable unlinkedVariable in _unlinkedUnit.variables) {
_topLevelVariables
.add(new TopLevelVariableElementForLink(this, unlinkedVariable));
}
}
return _topLevelVariables;
}
@override
List<ClassElementForLink_Class> get types {
if (_types == null) {
_types = <ClassElementForLink_Class>[];
for (UnlinkedClass unlinkedClass in _unlinkedUnit.classes) {
_types.add(new ClassElementForLink_Class(this, unlinkedClass));
}
}
return _types;
}
/**
* The linked representation of the compilation unit in the summary.
*/
LinkedUnit get _linkedUnit;
/**
* Search the unit for a top level element with the given [name].
* If no name is found, return the singleton instance of
* [UndefinedElementForLink].
*/
ReferenceableElementForLink getContainedName(name) {
if (_containedNames == null) {
_containedNames = <String, ReferenceableElementForLink>{};
// TODO(paulberry): what's the correct way to handle name conflicts?
for (ClassElementForLink_Class type in types) {
_containedNames[type.name] = type;
}
for (ClassElementForLink_Enum enm in enums) {
_containedNames[enm.name] = enm;
}
for (TopLevelVariableElementForLink variable in topLevelVariables) {
_containedNames[variable.name] = variable;
}
// TODO(paulberry): fill in other top level entities (typedefs
// and executables).
}
return _containedNames.putIfAbsent(
name, () => UndefinedElementForLink.instance);
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
/**
* Return the element referred to by the given [index] in
* [UnlinkedUnit.references]. If the reference is unresolved,
* return [UndefinedElementForLink.instance].
*/
ReferenceableElementForLink _resolveRef(int index) {
if (_references[index] == null) {
UnlinkedReference unlinkedReference = _unlinkedUnit.references[index];
LinkedReference linkedReference = _linkedUnit.references[index];
String name = unlinkedReference.name;
int containingReference = unlinkedReference.prefixReference;
if (containingReference != 0 &&
_linkedUnit.references[containingReference].kind !=
ReferenceKind.prefix) {
_references[index] =
_resolveRef(containingReference).getContainedName(name);
} else if (linkedReference.dependency == 0) {
_references[index] = enclosingElement.getContainedName(name);
} else {
LibraryElementForLink dependency =
enclosingElement._getDependency(linkedReference.dependency);
_references[index] = dependency.getContainedName(name);
}
}
return _references[index];
}
/**
* Resolve an [EntityRef] into a type. If the reference is
* unresolved, return [DynamicTypeImpl.instance].
*
* TODO(paulberry): or should we have a class representing an
* unresolved type, for consistency with the full element model?
*/
DartType _resolveTypeRef(
EntityRef type, TypeParameterizedElementForLink typeParameterContext,
{bool defaultVoid: false}) {
if (type == null) {
if (defaultVoid) {
return VoidTypeImpl.instance;
} else {
return DynamicTypeImpl.instance;
}
}
if (type.paramReference != 0) {
return typeParameterContext.getTypeParameterType(type.paramReference);
} else if (type.syntheticReturnType != null) {
// TODO(paulberry): implement.
throw new UnimplementedError();
} else if (type.implicitFunctionTypeIndices.isNotEmpty) {
// TODO(paulberry): implement.
throw new UnimplementedError();
} else {
DartType getTypeArgument(int i) {
if (i < type.typeArguments.length) {
return _resolveTypeRef(type.typeArguments[i], typeParameterContext);
} else {
return DynamicTypeImpl.instance;
}
}
ReferenceableElementForLink element = _resolveRef(type.reference);
return element.buildType(
getTypeArgument, type.implicitFunctionTypeIndices);
}
}
}
/**
* Element representing a compilation unit which is part of the build
* unit being linked.
*/
class CompilationUnitElementInBuildUnit extends CompilationUnitElementForLink {
@override
final LinkedUnitBuilder _linkedUnit;
@override
final LibraryElementInBuildUnit enclosingElement;
CompilationUnitElementInBuildUnit(this.enclosingElement,
UnlinkedUnit unlinkedUnit, this._linkedUnit, int unitNum)
: super(unlinkedUnit, unitNum);
@override
bool get isInBuildUnit => true;
@override
LibraryElementInBuildUnit get library => enclosingElement;
/**
* If this compilation unit already has a reference in its references table
* matching [dependency], [name], [numTypeParameters], [unitNum],
* [containingReference], and [kind], return its index. Otherwise add a new reference to
* the table and return its index.
*/
int addRawReference(String name,
{int dependency: 0,
int numTypeParameters: 0,
int unitNum: 0,
int containingReference: 0,
ReferenceKind kind: ReferenceKind.classOrEnum}) {
List<LinkedReferenceBuilder> linkedReferences = _linkedUnit.references;
List<UnlinkedReference> unlinkedReferences = _unlinkedUnit.references;
for (int i = 0; i < linkedReferences.length; i++) {
LinkedReferenceBuilder linkedReference = linkedReferences[i];
if (linkedReference.dependency == dependency &&
(i < unlinkedReferences.length
? unlinkedReferences[i].name
: linkedReference.name) ==
name &&
linkedReference.numTypeParameters == numTypeParameters &&
linkedReference.unit == unitNum &&
(i < unlinkedReferences.length
? unlinkedReferences[i].prefixReference
: linkedReference.containingReference) ==
containingReference &&
linkedReference.kind == kind) {
return i;
}
}
int result = linkedReferences.length;
linkedReferences.add(new LinkedReferenceBuilder(
dependency: dependency,
name: name,
numTypeParameters: numTypeParameters,
unit: unitNum,
containingReference: containingReference,
kind: kind));
return result;
}
/**
* If this compilation unit already has a reference in its references table
* to [element], return its index. Otherwise add a new reference to the table
* and return its index.
*/
int addReference(Element element) {
if (element is ClassElementForLink) {
return addRawReference(element.name,
dependency: library.addDependency(element.library),
numTypeParameters: element.typeParameters.length,
unitNum: element.enclosingElement.unitNum);
} else if (element is ExecutableElementForLink) {
// TODO(paulberry): will this code ever be executed for an executable
// element that's not inside a class?
assert(element.enclosingElement is ClassElementForLink_Class);
ReferenceKind kind;
switch (element._unlinkedExecutable.kind) {
case UnlinkedExecutableKind.functionOrMethod:
kind = ReferenceKind.method;
break;
case UnlinkedExecutableKind.setter:
kind = ReferenceKind.propertyAccessor;
break;
default:
// TODO(paulberry): implement other cases as necessary
throw new UnimplementedError('${element._unlinkedExecutable.kind}');
}
return addRawReference(element.name,
numTypeParameters: element.typeParameters.length,
containingReference: addReference(element.enclosingElement),
kind: kind);
}
// TODO(paulberry): implement other cases
throw new UnimplementedError('${element.runtimeType}');
}
/**
* Perform type inference and const cycle detection on this
* compilation unit.
*/
void link() {
if (library._linker.strongMode) {
new InstanceMemberInferrer(enclosingElement._linker.typeProvider,
enclosingElement.inheritanceManager)
.inferCompilationUnit(this);
}
for (ClassElementForLink classElement in types) {
classElement.link(this);
}
}
/**
* Throw away any information stored in the summary by a previous call to
* [link].
*/
void unlink() {
_linkedUnit.constCycles.clear();
_linkedUnit.references.length = _unlinkedUnit.references.length;
_linkedUnit.types.clear();
}
/**
* Store the fact that the given [slot] represents a constant constructor
* that is part of a cycle.
*/
void _storeConstCycle(int slot) {
_linkedUnit.constCycles.add(slot);
}
/**
* Store the given [linkedType] in the given [slot] of the this compilation
* unit's linked type list.
*/
void _storeLinkedType(int slot, DartType linkedType,
TypeParameterizedElementForLink typeParameterContext) {
if (slot != 0) {
if (linkedType != null && !linkedType.isDynamic) {
_linkedUnit.types.add(_createLinkedType(
linkedType, this, typeParameterContext,
slot: slot));
}
}
}
}
/**
* Element representing a compilation unit which is depended upon
* (either directly or indirectly) by the build unit being linked.
*
* TODO(paulberry): ensure that inferred types in dependencies are properly
* resynthesized.
*/
class CompilationUnitElementInDependency extends CompilationUnitElementForLink {
@override
final LinkedUnit _linkedUnit;
@override
final LibraryElementInDependency enclosingElement;
CompilationUnitElementInDependency(this.enclosingElement,
UnlinkedUnit unlinkedUnit, this._linkedUnit, int unitNum)
: super(unlinkedUnit, unitNum);
@override
bool get isInBuildUnit => false;
}
/**
* Instance of [ConstNode] representing a constant constructor.
*/
class ConstConstructorNode extends ConstNode {
/**
* The [ConstructorElement] to which this node refers.
*/
final ConstructorElementForLink constructorElement;
/**
* Once this node has been evaluated, indicates whether the
* constructor is free of constant evaluation cycles.
*/
bool isCycleFree = false;
ConstConstructorNode(this.constructorElement);
@override
List<ConstNode> computeDependencies() {
List<ConstNode> dependencies = <ConstNode>[];
void safeAddDependency(ConstNode target) {
if (target != null) {
dependencies.add(target);
}
}
UnlinkedExecutable unlinkedExecutable =
constructorElement._unlinkedExecutable;
ClassElementForLink_Class enclosingClass =
constructorElement.enclosingElement;
ConstructorElementForLink redirectedConstructor =
_getFactoryRedirectedConstructor();
if (redirectedConstructor != null) {
if (redirectedConstructor._constNode != null) {
safeAddDependency(redirectedConstructor._constNode);
}
} else if (unlinkedExecutable.isFactory) {
// Factory constructor, but getConstRedirectedConstructor returned
// null. This can happen if we're visiting one of the special external
// const factory constructors in the SDK, or if the code contains
// errors (such as delegating to a non-const constructor, or delegating
// to a constructor that can't be resolved). In any of these cases,
// we'll evaluate calls to this constructor without having to refer to
// any other constants. So we don't need to report any dependencies.
} else {
ClassElementForLink superClass = enclosingClass.supertype?.element;
bool defaultSuperInvocationNeeded = true;
for (UnlinkedConstructorInitializer constructorInitializer
in constructorElement._unlinkedExecutable.constantInitializers) {
if (constructorInitializer.kind ==
UnlinkedConstructorInitializerKind.superInvocation) {
defaultSuperInvocationNeeded = false;
if (superClass != null && !superClass.isObject) {
ConstructorElementForLink constructor = superClass
.getContainedName(constructorInitializer.name)
.asConstructor;
safeAddDependency(constructor?._constNode);
}
} else if (constructorInitializer.kind ==
UnlinkedConstructorInitializerKind.thisInvocation) {
defaultSuperInvocationNeeded = false;
ConstructorElementForLink constructor = constructorElement
.enclosingElement
.getContainedName(constructorInitializer.name)
.asConstructor;
safeAddDependency(constructor?._constNode);
}
CompilationUnitElementForLink compilationUnit =
constructorElement.enclosingElement.enclosingElement;
collectDependencies(
dependencies, constructorInitializer.expression, compilationUnit);
for (UnlinkedConst unlinkedConst in constructorInitializer.arguments) {
collectDependencies(dependencies, unlinkedConst, compilationUnit);
}
}
if (defaultSuperInvocationNeeded) {
// No explicit superconstructor invocation found, so we need to
// manually insert a reference to the implicit superconstructor.
if (superClass != null && !superClass.isObject) {
ConstructorElementForLink unnamedConstructor =
superClass.unnamedConstructor;
safeAddDependency(unnamedConstructor?._constNode);
}
}
for (FieldElementForLink field in enclosingClass.fields) {
// Note: non-static const isn't allowed but we handle it anyway so
// that we won't be confused by incorrect code.
if ((field.isFinal || field.isConst) && !field.isStatic) {
safeAddDependency(field.asConstVariable);
}
}
for (ParameterElementForLink parameterElement
in constructorElement.parameters) {
safeAddDependency(parameterElement._constNode);
}
}
return dependencies;
}
/**
* If [constructorElement] redirects to another constructor via a factory
* redirect, return the constructor it redirects to.
*/
ConstructorElementForLink _getFactoryRedirectedConstructor() {
EntityRef redirectedConstructor =
constructorElement._unlinkedExecutable.redirectedConstructor;
if (redirectedConstructor != null) {
return constructorElement.enclosingElement.enclosingElement
._resolveRef(redirectedConstructor.reference)
.asConstructor;
} else {
return null;
}
}
}
/**
* Specialization of [DependencyWalker] for detecting constant
* evaluation cycles.
*/
class ConstDependencyWalker extends DependencyWalker<ConstNode> {
@override
void evaluate(ConstNode v) {
if (v is ConstConstructorNode) {
v.isCycleFree = true;
}
v.isEvaluated = true;
}
@override
void evaluateScc(List<ConstNode> scc) {
for (ConstNode v in scc) {
if (v is ConstConstructorNode) {
v.isCycleFree = false;
}
v.isEvaluated = true;
}
}
}
/**
* Specialization of [Node] used to construct the constant evaluation
* dependency graph.
*/
abstract class ConstNode extends Node<ConstNode> {
@override
bool isEvaluated = false;
/**
* Collect the dependencies in [unlinkedConst] (which should be
* interpreted relative to [compilationUnit]) and store them in
* [dependencies].
*/
void collectDependencies(
List<ConstNode> dependencies,
UnlinkedConst unlinkedConst,
CompilationUnitElementForLink compilationUnit) {
if (unlinkedConst == null) {
return;
}
int refPtr = 0;
for (UnlinkedConstOperation operation in unlinkedConst.operations) {
switch (operation) {
case UnlinkedConstOperation.pushReference:
EntityRef ref = unlinkedConst.references[refPtr++];
ConstVariableNode variable =
compilationUnit._resolveRef(ref.reference).asConstVariable;
if (variable != null) {
dependencies.add(variable);
}
break;
case UnlinkedConstOperation.makeTypedList:
refPtr++;
break;
case UnlinkedConstOperation.makeTypedMap:
refPtr += 2;
break;
case UnlinkedConstOperation.invokeConstructor:
EntityRef ref = unlinkedConst.references[refPtr++];
ConstructorElementForLink element =
compilationUnit._resolveRef(ref.reference).asConstructor;
if (element?._constNode != null) {
dependencies.add(element._constNode);
}
break;
default:
break;
}
}
assert(refPtr == unlinkedConst.references.length);
}
}
/**
* Instance of [ConstNode] representing a parameter with a default
* value.
*/
class ConstParameterNode extends ConstNode {
/**
* The [ParameterElement] to which this node refers.
*/
final ParameterElementForLink parameterElement;
ConstParameterNode(this.parameterElement);
@override
List<ConstNode> computeDependencies() {
List<ConstNode> dependencies = <ConstNode>[];
collectDependencies(
dependencies,
parameterElement._unlinkedParam.defaultValue,
parameterElement.compilationUnit);
return dependencies;
}
}
/**
* Element representing a constructor resynthesized from a summary
* during linking.
*/
class ConstructorElementForLink extends ExecutableElementForLink
implements ConstructorElementImpl, ReferenceableElementForLink {
/**
* If this is a `const` constructor and the enclosing library is
* part of the build unit being linked, the constructor's node in
* the constant evaluation dependency graph. Otherwise `null`.
*/
ConstConstructorNode _constNode;
ConstructorElementForLink(ClassElementForLink_Class enclosingElement,
UnlinkedExecutable unlinkedExecutable)
: super(enclosingElement, unlinkedExecutable) {
if (enclosingElement.enclosingElement.isInBuildUnit &&
_unlinkedExecutable.constCycleSlot != 0) {
_constNode = new ConstConstructorNode(this);
}
}
@override
ConstructorElementForLink get asConstructor => this;
@override
ConstVariableNode get asConstVariable => null;
@override
DartType get asStaticType {
// Referring to a constructor directly is an error, so just use
// `dynamic`.
return DynamicTypeImpl.instance;
}
@override
TypeInferenceNode get asTypeInferenceNode => null;
@override
bool get isCycleFree {
if (!_constNode.isEvaluated) {
new ConstDependencyWalker().walk(_constNode);
}
return _constNode.isCycleFree;
}
@override
DartType buildType(DartType getTypeArgument(int i),
List<int> implicitFunctionTypeIndices) =>
DynamicTypeImpl.instance;
@override
ReferenceableElementForLink getContainedName(String name) =>
UndefinedElementForLink.instance;
/**
* Perform const cycle detection on this constructor.
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
if (_constNode != null && !isCycleFree) {
compilationUnit._storeConstCycle(_unlinkedExecutable.constCycleSlot);
}
// TODO(paulberry): call super.
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Instance of [ConstNode] representing a constant field or constant
* top level variable.
*/
class ConstVariableNode extends ConstNode {
/**
* The [FieldElement] or [TopLevelVariableElement] to which this
* node refers.
*/
final VariableElementForLink variableElement;
ConstVariableNode(this.variableElement);
@override
List<ConstNode> computeDependencies() {
List<ConstNode> dependencies = <ConstNode>[];
collectDependencies(
dependencies,
variableElement.unlinkedVariable.constExpr,
variableElement.compilationUnit);
return dependencies;
}
}
/**
* An instance of [DependencyWalker] contains the core algorithms for
* walking a dependency graph and evaluating nodes in a safe order.
*/
abstract class DependencyWalker<NodeType extends Node<NodeType>> {
/**
* Called by [walk] to evaluate a single non-cyclical node, after
* all that node's dependencies have been evaluated.
*/
void evaluate(NodeType v);
/**
* Called by [walk] to evaluate a strongly connected component
* containing one or more nodes. All dependencies of the strongly
* connected component have been evaluated.
*/
void evaluateScc(List<NodeType> scc);
/**
* Walk the dependency graph starting at [startingPoint], finding
* strongly connected components and evaluating them in a safe order
* by calling [evaluate] and [evaluateScc].
*
* This is an implementation of Tarjan's strongly connected
* components algorithm
* (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm).
*/
void walk(NodeType startingPoint) {
// TODO(paulberry): consider rewriting in a non-recursive way so
// that long dependency chains don't cause stack overflow.
// TODO(paulberry): in the event that an exception occurs during
// the walk, restore the state of the [Node] data structures so
// that further evaluation will be safe.
// The index which will be assigned to the next node that is
// freshly visited.
int index = 1;
// Stack of nodes which have been seen so far and whose strongly
// connected component is still being determined. Nodes are only
// popped off the stack when they are evaluated, so sometimes the
// stack contains nodes that were visited after the current node.
List<NodeType> stack = <NodeType>[];
void strongConnect(NodeType node) {
bool hasTrivialCycle = false;
// Assign the current node an index and add it to the stack. We
// haven't seen any of its dependencies yet, so set its lowLink
// to its index, indicating that so far it is the only node in
// its strongly connected component.
node.index = node.lowLink = index++;
stack.add(node);
// Consider the node's dependencies one at a time.
for (NodeType dependency in node.dependencies) {
// If the dependency has already been evaluated, it can't be
// part of this node's strongly connected component, so we can
// skip it.
if (dependency.isEvaluated) {
continue;
}
if (identical(node, dependency)) {
// If a node includes itself as a dependency, there is no need to
// explore the dependency further.
hasTrivialCycle = true;
} else if (dependency.index == 0) {
// The dependency hasn't been seen yet, so recurse on it.
strongConnect(dependency);
// If the dependency's lowLink refers to a node that was
// visited before the current node, that means that the
// current node, the dependency, and the node referred to by
// the dependency's lowLink are all part of the same
// strongly connected component, so we need to update the
// current node's lowLink accordingly.
if (dependency.lowLink < node.lowLink) {
node.lowLink = dependency.lowLink;
}
} else {
// The dependency has already been seen, so it is part of
// the current node's strongly connected component. If it
// was visited earlier than the current node's lowLink, then
// it is a new addition to the current node's strongly
// connected component, so we need to update the current
// node's lowLink accordingly.
if (dependency.index < node.lowLink) {
node.lowLink = dependency.index;
}
}
}
// If the current node's lowLink is the same as its index, then
// we have finished visiting a strongly connected component, so
// pop the stack and evaluate it before moving on.
if (node.lowLink == node.index) {
// The strongly connected component has only one node. If there is a
// cycle, it's a trivial one.
if (identical(stack.last, node)) {
stack.removeLast();
if (hasTrivialCycle) {
evaluateScc(<NodeType>[node]);
} else {
evaluate(node);
}
} else {
// There are multiple nodes in the strongly connected
// component.
List<NodeType> scc = <NodeType>[];
while (true) {
NodeType otherNode = stack.removeLast();
scc.add(otherNode);
if (identical(otherNode, node)) {
break;
}
}
evaluateScc(scc);
}
}
}
// Kick off the algorithm starting with the starting point.
strongConnect(startingPoint);
}
}
/**
* Base class for executable elements resynthesized from a summary during
* linking.
*/
abstract class ExecutableElementForLink extends Object
with TypeParameterizedElementForLink
implements ExecutableElementImpl {
/**
* The unlinked representation of the method in the summary.
*/
final UnlinkedExecutable _unlinkedExecutable;
DartType _declaredReturnType;
DartType _inferredReturnType;
FunctionTypeImpl _type;
List<TypeParameterElementForLink> _typeParameters;
List<ParameterElementForLink> _parameters;
/**
* TODO(paulberry): this won't always be a class element.
*/
@override
final ClassElementForLink_Class enclosingElement;
ExecutableElementForLink(this.enclosingElement, this._unlinkedExecutable);
@override
TypeParameterizedElementForLink get enclosingTypeParameterContext =>
enclosingElement;
@override
bool get hasImplicitReturnType => _unlinkedExecutable.returnType == null;
@override
bool get isStatic => _unlinkedExecutable.isStatic;
@override
bool get isSynthetic => false;
@override
LibraryElementForLink get library => enclosingElement.library;
@override
String get name => _unlinkedExecutable.name;
@override
List<ParameterElementForLink> get parameters {
if (_parameters == null) {
int numParameters = _unlinkedExecutable.parameters.length;
_parameters = new List<ParameterElementForLink>(numParameters);
for (int i = 0; i < numParameters; i++) {
UnlinkedParam unlinkedParam = _unlinkedExecutable.parameters[i];
_parameters[i] = new ParameterElementForLink(
this, unlinkedParam, this, enclosingElement.enclosingElement, i);
}
}
return _parameters;
}
@override
DartType get returnType {
if (_inferredReturnType != null) {
return _inferredReturnType;
} else if (_declaredReturnType == null) {
if (_unlinkedExecutable.returnType == null) {
if (_unlinkedExecutable.kind == UnlinkedExecutableKind.constructor) {
// TODO(paulberry): implement.
throw new UnimplementedError();
} else if (_unlinkedExecutable.kind == UnlinkedExecutableKind.setter &&
library._linker.strongMode) {
// In strong mode, setters without an explicit return type are
// considered to return `void`.
_declaredReturnType = VoidTypeImpl.instance;
} else {
_declaredReturnType = DynamicTypeImpl.instance;
}
} else {
_declaredReturnType = enclosingElement.enclosingElement
._resolveTypeRef(_unlinkedExecutable.returnType, this);
}
}
return _declaredReturnType;
}
@override
void set returnType(DartType inferredType) {
assert(_inferredReturnType == null);
_inferredReturnType = inferredType;
}
@override
FunctionTypeImpl get type => _type ??= new FunctionTypeImpl(this);
@override
List<UnlinkedTypeParam> get _unlinkedTypeParams =>
_unlinkedExecutable.typeParameters;
@override
bool isAccessibleIn(LibraryElement library) =>
!Identifier.isPrivateName(name) || identical(this.library, library);
/**
* Store the results of type inference for this method in [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
compilationUnit._storeLinkedType(
_unlinkedExecutable.inferredReturnTypeSlot, returnType, this);
for (ParameterElementForLink parameterElement in parameters) {
parameterElement.link(compilationUnit);
}
}
}
/**
* Element representing a field resynthesized from a summary during
* linking.
*/
abstract class FieldElementForLink
implements FieldElement, ReferenceableElementForLink {}
/**
* Specialization of [FieldElementForLink] for class fields.
*/
class FieldElementForLink_ClassField extends VariableElementForLink
implements FieldElementForLink {
@override
final ClassElementForLink_Class enclosingElement;
/**
* If this is an instance field, the type that was computed by
* [InstanceMemberInferrer].
*/
DartType _inferredInstanceType;
DartType _declaredType;
FieldElementForLink_ClassField(ClassElementForLink_Class enclosingElement,
UnlinkedVariable unlinkedVariable)
: enclosingElement = enclosingElement,
super(unlinkedVariable, enclosingElement.enclosingElement);
@override
bool get isStatic => unlinkedVariable.isStatic;
@override
DartType get type {
assert(!isStatic);
if (_inferredInstanceType != null) {
return _inferredInstanceType;
} else if (_declaredType == null) {
if (unlinkedVariable.type == null) {
_declaredType = DynamicTypeImpl.instance;
} else {
_declaredType = compilationUnit._resolveTypeRef(
unlinkedVariable.type, enclosingElement);
}
}
return _declaredType;
}
@override
void set type(DartType inferredType) {
assert(!isStatic);
assert(_inferredInstanceType == null);
_inferredInstanceType = inferredType;
}
/**
* Store the results of type inference for this field in
* [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
if (hasImplicitType) {
if (isStatic) {
TypeInferenceNode typeInferenceNode = this.asTypeInferenceNode;
if (typeInferenceNode != null) {
compilationUnit._storeLinkedType(unlinkedVariable.inferredTypeSlot,
typeInferenceNode.inferredType, enclosingElement);
}
} else {
compilationUnit._storeLinkedType(unlinkedVariable.inferredTypeSlot,
_inferredInstanceType, enclosingElement);
}
}
}
}
/**
* Specialization of [FieldElementForLink] for enum fields.
*/
class FieldElementForLink_EnumField extends FieldElementForLink
implements FieldElement {
/**
* The unlinked representation of the field in the summary, or `null` if this
* is an enum's `values` field.
*/
final UnlinkedEnumValue unlinkedEnumValue;
FieldElementForLink_EnumField(this.unlinkedEnumValue);
@override
ConstructorElementForLink get asConstructor => null;
@override
ConstVariableNode get asConstVariable {
// Even though enum fields are constants, there is no need to include them
// in the const dependency graph because they can't participate in a
// circularity.
return null;
}
@override
DartType get asStaticType {
// TODO(paulberry): implement.
throw new UnimplementedError();
}
@override
TypeInferenceNode get asTypeInferenceNode => null;
@override
bool get isStatic => true;
@override
bool get isSynthetic => false;
@override
String get name =>
unlinkedEnumValue == null ? 'values' : unlinkedEnumValue.name;
@override
DartType buildType(DartType getTypeArgument(int i),
List<int> implicitFunctionTypeIndices) =>
DynamicTypeImpl.instance;
@override
ReferenceableElementForLink getContainedName(String name) =>
UndefinedElementForLink.instance;
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Element representing a function-typed parameter resynthesied from a summary
* during linking.
*/
class FunctionElementForLink_FunctionTypedParam implements FunctionElement {
@override
final ParameterElementForLink enclosingElement;
/**
* The executable element containing this function-typed parameter.
*/
final Element enclosingExecutable;
/**
* The appropriate integer list to store in
* [EntityRef.implicitFunctionTypeIndices] to refer to this function-typed
* parameter.
*/
final List<int> implicitFunctionTypeIndices;
DartType _returnType;
FunctionElementForLink_FunctionTypedParam(this.enclosingElement,
this.enclosingExecutable, this.implicitFunctionTypeIndices);
@override
DartType get returnType {
if (_returnType == null) {
if (enclosingElement._unlinkedParam.type == null) {
_returnType = DynamicTypeImpl.instance;
} else {
_returnType = enclosingElement.compilationUnit._resolveTypeRef(
enclosingElement._unlinkedParam.type,
enclosingElement._typeParameterContext);
}
}
return _returnType;
}
@override
List<TypeParameterElement> get typeParameters => const [];
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Element representing the initializer expression of a variable.
*/
class FunctionElementForLink_Initializer implements FunctionElementImpl {
/**
* The variable for which this element is the initializer.
*/
final VariableElementForLink _variable;
FunctionElementForLink_Initializer(this._variable);
@override
DartType get returnType {
// If this is a variable whose type needs inferring, infer it.
TypeInferenceNode typeInferenceNode = _variable._typeInferenceNode;
if (typeInferenceNode != null) {
return typeInferenceNode.inferredType;
} else {
// There's no reason linking should need to access the type of
// this FunctionElement, since the variable doesn't need its
// type inferred.
assert(false);
// But for robustness, return the dynamic type.
return DynamicTypeImpl.instance;
}
}
@override
void set returnType(DartType newType) {
// TODO(paulberry): store inferred type.
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* An instance of [LibraryCycleForLink] represents a single library cycle
* discovered during linking; it consists of one or more libraries in the build
* unit being linked.
*
* This is a stub implementation; at the moment all libraries in the build unit
* being linked are considered to be part of the same library cycle. Hence
* there is just a singleton instance of this class.
* TODO(paulberry): replace this with a correct implementation.
*/
class LibraryCycleForLink {
static final LibraryCycleForLink instance = const LibraryCycleForLink._();
const LibraryCycleForLink._();
}
/**
* Element representing a library resynthesied from a summary during
* linking. The type parameter, [UnitElement], represents the type
* that will be used for the compilation unit elements.
*/
abstract class LibraryElementForLink<
UnitElement extends CompilationUnitElementForLink>
implements LibraryElement {
/**
* Pointer back to the linker.
*/
final _Linker _linker;
/**
* The absolute URI of this library.
*/
final Uri _absoluteUri;
List<UnitElement> _units;
final Map<String, ReferenceableElementForLink> _containedNames =
<String, ReferenceableElementForLink>{};
final List<LibraryElementForLink> _dependencies = <LibraryElementForLink>[];
UnlinkedUnit _definingUnlinkedUnit;
LibraryElementForLink(this._linker, this._absoluteUri) {
_dependencies.length = _linkedLibrary.dependencies.length;
}
/**
* Get the [UnlinkedUnit] for the defining compilation unit of this library.
*/
UnlinkedUnit get definingUnlinkedUnit =>
_definingUnlinkedUnit ??= _linker.getUnit(_absoluteUri.toString());
@override
Element get enclosingElement => null;
/**
* If this library is part of the build unit being linked, return the library
* cycle it is part of. Otherwise return `null`.
*/
LibraryCycleForLink get libraryCycleForLink;
@override
List<UnitElement> get units {
if (_units == null) {
UnlinkedUnit definingUnit = definingUnlinkedUnit;
_units = <UnitElement>[_makeUnitElement(definingUnit, 0)];
int numParts = definingUnit.parts.length;
for (int i = 0; i < numParts; i++) {
// TODO(paulberry): make sure we handle the case where Uri.parse fails.
// TODO(paulberry): make sure we handle the case where
// resolveRelativeUri fails.
UnlinkedUnit partUnit = _linker.getUnit(resolveRelativeUri(
_absoluteUri, Uri.parse(definingUnit.publicNamespace.parts[i]))
.toString());
_units.add(
_makeUnitElement(partUnit ?? new UnlinkedUnitBuilder(), i + 1));
}
}
return _units;
}
/**
* The linked representation of the library in the summary.
*/
LinkedLibrary get _linkedLibrary;
/**
* Search all the units for a top level element with the given
* [name]. If no name is found, return the singleton instance of
* [UndefinedElementForLink].
*/
ReferenceableElementForLink getContainedName(String name) =>
_containedNames.putIfAbsent(name, () {
for (UnitElement unit in units) {
ReferenceableElementForLink element = unit.getContainedName(name);
if (!identical(element, UndefinedElementForLink.instance)) {
return element;
}
}
return UndefinedElementForLink.instance;
});
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
/**
* Return the [LibraryElement] corresponding to the given dependency [index].
*/
LibraryElementForLink _getDependency(int index) {
return _dependencies[index] ??= _linker.getLibrary(resolveRelativeUri(
_absoluteUri, Uri.parse(_linkedLibrary.dependencies[index].uri)));
}
/**
* Create a [UnitElement] for one of the library's compilation
* units.
*/
UnitElement _makeUnitElement(UnlinkedUnit unlinkedUnit, int i);
}
/**
* Element representing a library which is part of the build unit
* being linked.
*/
class LibraryElementInBuildUnit
extends LibraryElementForLink<CompilationUnitElementInBuildUnit> {
@override
final LinkedLibraryBuilder _linkedLibrary;
InheritanceManager _inheritanceManager;
LibraryElementInBuildUnit(
_Linker linker, Uri absoluteUri, this._linkedLibrary)
: super(linker, absoluteUri);
/**
* Get the inheritance manager for this library (creating it if necessary).
*/
InheritanceManager get inheritanceManager =>
_inheritanceManager ??= new InheritanceManager(this);
@override
LibraryCycleForLink get libraryCycleForLink => LibraryCycleForLink.instance;
/**
* If this library already has a dependency in its dependencies table matching
* [library], return its index. Otherwise add a new dependency to table and
* return its index.
*/
int addDependency(LibraryElementForLink library) {
for (int i = 0; i < _linkedLibrary.dependencies.length; i++) {
if (identical(_getDependency(i), library)) {
return i;
}
}
int result = _linkedLibrary.dependencies.length;
_linkedLibrary.dependencies.add(new LinkedDependencyBuilder(
parts: library.definingUnlinkedUnit.publicNamespace.parts,
uri: library._absoluteUri.toString()));
_dependencies.add(library);
return result;
}
/**
* Perform type inference and const cycle detection on this library.
*/
void link() {
for (CompilationUnitElementInBuildUnit unit in units) {
unit.link();
}
}
/**
* Throw away any information stored in the summary by a previous call to
* [link].
*/
void unlink() {
_linkedLibrary.dependencies.length =
_linkedLibrary.numPrelinkedDependencies;
for (CompilationUnitElementInBuildUnit unit in units) {
unit.unlink();
}
}
@override
CompilationUnitElementInBuildUnit _makeUnitElement(
UnlinkedUnit unlinkedUnit, int i) =>
new CompilationUnitElementInBuildUnit(
this, unlinkedUnit, _linkedLibrary.units[i], i);
}
/**
* Element representing a library which is depended upon (either
* directly or indirectly) by the build unit being linked.
*/
class LibraryElementInDependency
extends LibraryElementForLink<CompilationUnitElementInDependency> {
@override
final LinkedLibrary _linkedLibrary;
LibraryElementInDependency(
_Linker linker, Uri absoluteUri, this._linkedLibrary)
: super(linker, absoluteUri);
@override
LibraryCycleForLink get libraryCycleForLink => null;
@override
CompilationUnitElementInDependency _makeUnitElement(
UnlinkedUnit unlinkedUnit, int i) =>
new CompilationUnitElementInDependency(
this, unlinkedUnit, _linkedLibrary.units[i], i);
}
/**
* Element representing a method resynthesized from a summary during linking.
*/
class MethodElementForLink extends ExecutableElementForLink
implements MethodElementImpl {
MethodElementForLink(ClassElementForLink_Class enclosingElement,
UnlinkedExecutable unlinkedExecutable)
: super(enclosingElement, unlinkedExecutable);
@override
ElementKind get kind => ElementKind.METHOD;
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Instances of [Node] represent nodes in a dependency graph. The
* type parameter, [NodeType], is the derived type (this affords some
* extra type safety by making it difficult to accidentally construct
* bridges between unrelated dependency graphs).
*/
abstract class Node<NodeType> {
/**
* Index used by Tarjan's strongly connected components algorithm.
* Zero means the node has not been visited yet; a nonzero value
* counts the order in which the node was visited.
*/
int index = 0;
/**
* Low link used by Tarjan's strongly connected components
* algorithm. This represents the smallest [index] of all the nodes
* in the strongly connected component to which this node belongs.
*/
int lowLink = 0;
List<NodeType> _dependencies;
/**
* Retrieve the dependencies of this node.
*/
List<NodeType> get dependencies => _dependencies ??= computeDependencies();
/**
* Indicates whether this node has been evaluated yet.
*/
bool get isEvaluated;
/**
* Compute the dependencies of this node.
*/
List<NodeType> computeDependencies();
}
/**
* Element used for references that result from trying to access a nonstatic
* member of an element that is not a container (e.g. accessing the "length"
* property of a constant).
*/
class NonstaticMemberElementForLink implements ReferenceableElementForLink {
/**
* If the thing from which a member was accessed is a constant, the
* associated [ConstNode]. Otherwise `null`.
*/
final ConstVariableNode _constNode;
NonstaticMemberElementForLink(this._constNode);
@override
ConstructorElementForLink get asConstructor => null;
@override
ConstVariableNode get asConstVariable => _constNode;
@override
DartType get asStaticType {
// TODO(paulberry): implement.
throw new UnimplementedError();
}
@override
TypeInferenceNode get asTypeInferenceNode {
// TODO(paulberry): implement.
throw new UnimplementedError();
}
@override
DartType buildType(DartType getTypeArgument(int i),
List<int> implicitFunctionTypeIndices) =>
DynamicTypeImpl.instance;
@override
ReferenceableElementForLink getContainedName(String name) => this;
}
/**
* Element representing a function or method parameter resynthesized
* from a summary during linking.
*/
class ParameterElementForLink implements ParameterElementImpl {
/**
* The unlinked representation of the parameter in the summary.
*/
final UnlinkedParam _unlinkedParam;
/**
* The context in which type parameters should be interpreted.
*/
final TypeParameterizedElementForLink _typeParameterContext;
/**
* If this parameter has a default value and the enclosing library
* is part of the build unit being linked, the parameter's node in
* the constant evaluation dependency graph. Otherwise `null`.
*/
ConstNode _constNode;
/**
* The compilation unit in which this parameter appears.
*/
final CompilationUnitElementForLink compilationUnit;
/**
* The index of this parameter within [enclosingElement]'s parameter list.
*/
final int _parameterIndex;
@override
final ExecutableElementForLink enclosingElement;
DartType _inferredType;
DartType _declaredType;
ParameterElementForLink(this.enclosingElement, this._unlinkedParam,
this._typeParameterContext, this.compilationUnit, this._parameterIndex) {
if (_unlinkedParam.defaultValue != null) {
_constNode = new ConstParameterNode(this);
}
}
@override
bool get hasImplicitType =>
!_unlinkedParam.isFunctionTyped && _unlinkedParam.type == null;
@override
String get name => _unlinkedParam.name;
@override
ParameterKind get parameterKind {
switch (_unlinkedParam.kind) {
case UnlinkedParamKind.required:
return ParameterKind.REQUIRED;
case UnlinkedParamKind.positional:
return ParameterKind.POSITIONAL;
case UnlinkedParamKind.named:
return ParameterKind.NAMED;
}
}
@override
DartType get type {
if (_inferredType != null) {
return _inferredType;
} else if (_declaredType == null) {
if (_unlinkedParam.isFunctionTyped) {
// TODO(paulberry): implement.
_declaredType = new FunctionTypeImpl(
new FunctionElementForLink_FunctionTypedParam(
this, enclosingElement, <int>[_parameterIndex]));
} else if (_unlinkedParam.type == null) {
_declaredType = DynamicTypeImpl.instance;
} else {
_declaredType = compilationUnit._resolveTypeRef(
_unlinkedParam.type, _typeParameterContext);
}
}
return _declaredType;
}
@override
void set type(DartType inferredType) {
assert(_inferredType == null);
_inferredType = inferredType;
}
/**
* Store the results of type inference for this parameter in
* [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
compilationUnit._storeLinkedType(
_unlinkedParam.inferredTypeSlot, _inferredType, _typeParameterContext);
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Element representing a getter or setter resynthesized from a summary during
* linking.
*/
class PropertyAccessorElementForLink extends ExecutableElementForLink
implements PropertyAccessorElementImpl {
@override
SyntheticVariableElementForLink variable;
PropertyAccessorElementForLink(ClassElementForLink_Class enclosingElement,
UnlinkedExecutable unlinkedExecutable, this.variable)
: super(enclosingElement, unlinkedExecutable);
@override
PropertyAccessorElementForLink get correspondingGetter => variable.getter;
@override
bool get isGetter =>
_unlinkedExecutable.kind == UnlinkedExecutableKind.getter;
@override
bool get isSetter =>
_unlinkedExecutable.kind == UnlinkedExecutableKind.setter;
@override
ElementKind get kind => _unlinkedExecutable.kind ==
UnlinkedExecutableKind.getter ? ElementKind.GETTER : ElementKind.SETTER;
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Abstract base class representing an element which can be the target
* of a reference.
*/
abstract class ReferenceableElementForLink {
/**
* If this element can be used in a constructor invocation context,
* return the associated constructor (which may be `this` or some
* other element). Otherwise return `null`.
*/
ConstructorElementForLink get asConstructor;
/**
* If this element can be used in a getter context to refer to a
* constant variable, return the [ConstVariableNode] for the
* constant value. Otherwise return `null`.
*/
ConstVariableNode get asConstVariable;
/**
* Return the static type of the entity referred to by thi element.
*/
DartType get asStaticType;
/**
* If this element can be used in a getter context as a type inference
* dependency, return the [TypeInferenceNode] for the inferred type.
* Otherwise return `null`.
*/
TypeInferenceNode get asTypeInferenceNode;
/**
* Return the type indicated by this element when it is used in a
* type instantiation context. If this element can't legally be
* instantiated as a type, return the dynamic type.
*/
DartType buildType(
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices);
/**
* If this element contains other named elements, return the
* contained element having the given [name]. If this element can't
* contain other named elements, or it doesn't contain an element
* with the given name, return the singleton of
* [UndefinedElementForLink].
*/
ReferenceableElementForLink getContainedName(String name);
}
/**
* Element representing a synthetic variable resynthesized from a summary during
* linking.
*/
class SyntheticVariableElementForLink implements PropertyInducingElementImpl {
PropertyAccessorElementForLink _getter;
PropertyAccessorElementForLink _setter;
@override
PropertyAccessorElementForLink get getter => _getter;
@override
PropertyAccessorElementForLink get setter => _setter;
@override
void set type(DartType inferredType) {}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Element representing a top level variable resynthesized from a
* summary during linking.
*/
class TopLevelVariableElementForLink extends VariableElementForLink
implements TopLevelVariableElement {
TopLevelVariableElementForLink(CompilationUnitElement enclosingElement,
UnlinkedVariable unlinkedVariable)
: super(unlinkedVariable, enclosingElement);
@override
bool get isStatic => true;
}
/**
* Specialization of [DependencyWalker] for performing type inferrence
* on static and top level variables.
*/
class TypeInferenceDependencyWalker
extends DependencyWalker<TypeInferenceNode> {
@override
void evaluate(TypeInferenceNode v) {
v.evaluate(false);
}
@override
void evaluateScc(List<TypeInferenceNode> scc) {
for (TypeInferenceNode v in scc) {
v.evaluate(true);
}
}
}
/**
* Specialization of [Node] used to construct the type inference dependency
* graph.
*/
class TypeInferenceNode extends Node<TypeInferenceNode> {
/**
* The [FieldElement] or [TopLevelVariableElement] to which this
* node refers.
*/
final VariableElementForLink variableElement;
/**
* If a type has been inferred for this node, the inferred type (may be
* `dynamic`). Otherwise `null`.
*/
DartType _inferredType;
TypeInferenceNode(this.variableElement);
/**
* Infer a type for this node if necessary, and return it.
*/
DartType get inferredType {
if (_inferredType == null) {
new TypeInferenceDependencyWalker().walk(this);
assert(_inferredType != null);
}
return _inferredType;
}
@override
bool get isEvaluated => _inferredType != null;
/**
* Collect the type inference dependencies in [unlinkedConst] (which should be
* interpreted relative to [compilationUnit]) and store them in
* [dependencies].
*/
void collectDependencies(
List<TypeInferenceNode> dependencies,
UnlinkedConst unlinkedConst,
CompilationUnitElementForLink compilationUnit) {
if (unlinkedConst == null) {
return;
}
int refPtr = 0;
for (UnlinkedConstOperation operation in unlinkedConst.operations) {
switch (operation) {
case UnlinkedConstOperation.pushReference:
EntityRef ref = unlinkedConst.references[refPtr++];
// TODO(paulberry): cache these resolved references for
// later use by evaluate().
TypeInferenceNode dependency =
compilationUnit._resolveRef(ref.reference).asTypeInferenceNode;
if (dependency != null) {
dependencies.add(dependency);
}
break;
case UnlinkedConstOperation.makeTypedList:
case UnlinkedConstOperation.invokeConstructor:
refPtr++;
break;
case UnlinkedConstOperation.makeTypedMap:
refPtr += 2;
break;
default:
break;
}
}
assert(refPtr == unlinkedConst.references.length);
}
@override
List<TypeInferenceNode> computeDependencies() {
List<TypeInferenceNode> dependencies = <TypeInferenceNode>[];
collectDependencies(
dependencies,
variableElement.unlinkedVariable.constExpr,
variableElement.compilationUnit);
return dependencies;
}
@override
void evaluate(bool inCycle) {
if (inCycle) {
_inferredType = DynamicTypeImpl.instance;
} else if (!variableElement.unlinkedVariable.constExpr.isValidConst) {
// TODO(paulberry): delete this case and fix errors.
throw new UnimplementedError();
} else {
// Perform RPN evaluation of the cycle, using a stack of
// inferred types.
List<DartType> stack = <DartType>[];
int refPtr = 0;
int intPtr = 0;
UnlinkedConst unlinkedConst = variableElement.unlinkedVariable.constExpr;
TypeProvider typeProvider =
variableElement.compilationUnit.enclosingElement._linker.typeProvider;
for (UnlinkedConstOperation operation in unlinkedConst.operations) {
switch (operation) {
case UnlinkedConstOperation.pushInt:
intPtr++;
stack.add(typeProvider.intType);
break;
case UnlinkedConstOperation.pushNull:
stack.add(BottomTypeImpl.instance);
break;
case UnlinkedConstOperation.pushReference:
EntityRef ref = unlinkedConst.references[refPtr++];
if (ref.paramReference != 0) {
stack.add(typeProvider.typeType);
} else {
// Synthetic function types can't be directly referred
// to by expressions.
assert(ref.syntheticReturnType == null);
// Nor can implicit function types derived from
// function-typed parameters.
assert(ref.implicitFunctionTypeIndices.isEmpty);
ReferenceableElementForLink element =
variableElement.compilationUnit._resolveRef(ref.reference);
TypeInferenceNode typeInferenceNode = element.asTypeInferenceNode;
if (typeInferenceNode != null) {
assert(typeInferenceNode.isEvaluated);
stack.add(typeInferenceNode._inferredType);
} else {
stack.add(element.asStaticType);
}
}
break;
case UnlinkedConstOperation.makeTypedList:
refPtr++;
stack.length -= unlinkedConst.ints[intPtr++];
// TODO(paulberry): implement.
stack.add(DynamicTypeImpl.instance);
break;
case UnlinkedConstOperation.makeTypedMap:
refPtr += 2;
// TODO(paulberry): implement.
throw new UnimplementedError('$operation');
case UnlinkedConstOperation.invokeConstructor:
// TODO(paulberry): don't just pop the args; use their types
// to infer the type of type arguments.
stack.length -=
unlinkedConst.ints[intPtr++] + unlinkedConst.ints[intPtr++];
EntityRef ref = unlinkedConst.references[refPtr++];
ConstructorElementForLink element = variableElement.compilationUnit
._resolveRef(ref.reference)
.asConstructor;
if (ref.typeArguments.isNotEmpty) {
// TODO(paulberry): handle type arguments
throw new UnimplementedError();
}
// TODO(paulberry): do we have to follow constructor
// redirections?
stack.add(element.enclosingElement.type);
break;
default:
// TODO(paulberry): implement.
throw new UnimplementedError('$operation');
}
}
assert(refPtr == unlinkedConst.references.length);
assert(intPtr == unlinkedConst.ints.length);
assert(stack.length == 1);
_inferredType = stack[0];
}
}
}
/**
* Element representing a type parameter resynthesized from a summary during
* linking.
*/
class TypeParameterElementForLink implements TypeParameterElement {
/**
* The unlinked representation of the type parameter in the summary.
*/
final UnlinkedTypeParam _unlinkedTypeParam;
/**
* The number of type parameters whose scope overlaps this one, and which are
* declared earlier in the file.
*/
final int nestingLevel;
TypeParameterTypeImpl _type;
TypeParameterElementForLink(this._unlinkedTypeParam, this.nestingLevel);
@override
String get name => _unlinkedTypeParam.name;
@override
TypeParameterTypeImpl get type => _type ??= new TypeParameterTypeImpl(this);
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Mixin representing an element which can have type parameters.
*/
abstract class TypeParameterizedElementForLink
implements TypeParameterizedElement {
List<TypeParameterType> _typeParameterTypes;
List<TypeParameterElementForLink> _typeParameters;
int _nestingLevel;
/**
* Get the type parameter context enclosing this one, if any.
*/
TypeParameterizedElementForLink get enclosingTypeParameterContext;
/**
* Find out how many type parameters are in scope in this context.
*/
int get typeParameterNestingLevel =>
_nestingLevel ??= _unlinkedTypeParams.length +
(enclosingTypeParameterContext?.typeParameterNestingLevel ?? 0);
List<TypeParameterElementForLink> get typeParameters {
if (_typeParameters == null) {
int enclosingNestingLevel =
enclosingTypeParameterContext?.typeParameterNestingLevel ?? 0;
int numTypeParameters = _unlinkedTypeParams.length;
_typeParameters =
new List<TypeParameterElementForLink>(numTypeParameters);
for (int i = 0; i < numTypeParameters; i++) {
_typeParameters[i] = new TypeParameterElementForLink(
_unlinkedTypeParams[i], enclosingNestingLevel + i);
}
}
return _typeParameters;
}
/**
* Get a list of [TypeParameterType] objects corresponding to the
* element's type parameters.
*/
List<TypeParameterType> get typeParameterTypes {
if (_typeParameterTypes == null) {
_typeParameterTypes = typeParameters
.map((TypeParameterElementForLink e) => e.type)
.toList();
}
return _typeParameterTypes;
}
/**
* Get the [UnlinkedTypeParam]s representing the type parameters declared by
* this element.
*/
List<UnlinkedTypeParam> get _unlinkedTypeParams;
/**
* Convert the given [index] into a type parameter type.
*/
TypeParameterType getTypeParameterType(int index) {
List<TypeParameterType> types = typeParameterTypes;
if (index <= types.length) {
return types[types.length - index];
} else if (enclosingTypeParameterContext != null) {
return enclosingTypeParameterContext
.getTypeParameterType(index - types.length);
} else {
// If we get here, it means that a summary contained a type parameter index
// that was out of range.
throw new RangeError('Invalid type parameter index');
}
}
}
class TypeProviderForLink implements TypeProvider {
final _Linker _linker;
InterfaceType _boolType;
InterfaceType _deprecatedType;
InterfaceType _doubleType;
InterfaceType _functionType;
InterfaceType _futureDynamicType;
InterfaceType _futureNullType;
InterfaceType _futureType;
InterfaceType _intType;
InterfaceType _iterableDynamicType;
InterfaceType _iterableType;
InterfaceType _listType;
InterfaceType _mapType;
InterfaceType _nullType;
InterfaceType _numType;
InterfaceType _objectType;
InterfaceType _stackTraceType;
InterfaceType _streamDynamicType;
InterfaceType _streamType;
InterfaceType _stringType;
InterfaceType _symbolType;
InterfaceType _typeType;
TypeProviderForLink(this._linker);
@override
InterfaceType get boolType =>
_boolType ??= _buildInterfaceType(_linker.coreLibrary, 'bool');
@override
DartType get bottomType => BottomTypeImpl.instance;
@override
InterfaceType get deprecatedType => _deprecatedType ??=
_buildInterfaceType(_linker.coreLibrary, 'Deprecated');
@override
InterfaceType get doubleType =>
_doubleType ??= _buildInterfaceType(_linker.coreLibrary, 'double');
@override
DartType get dynamicType => DynamicTypeImpl.instance;
@override
InterfaceType get functionType =>
_functionType ??= _buildInterfaceType(_linker.coreLibrary, 'Function');
@override
InterfaceType get futureDynamicType =>
_futureDynamicType ??= futureType.instantiate(<DartType>[dynamicType]);
@override
InterfaceType get futureNullType =>
_futureNullType ??= futureType.instantiate(<DartType>[nullType]);
@override
InterfaceType get futureType =>
_futureType ??= _buildInterfaceType(_linker.asyncLibrary, 'Future');
@override
InterfaceType get intType =>
_intType ??= _buildInterfaceType(_linker.coreLibrary, 'int');
@override
InterfaceType get iterableDynamicType => _iterableDynamicType ??=
iterableType.instantiate(<DartType>[dynamicType]);
@override
InterfaceType get iterableType =>
_iterableType ??= _buildInterfaceType(_linker.coreLibrary, 'Iterable');
@override
InterfaceType get listType =>
_listType ??= _buildInterfaceType(_linker.coreLibrary, 'List');
@override
InterfaceType get mapType =>
_mapType ??= _buildInterfaceType(_linker.coreLibrary, 'Map');
@override
List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[
nullType,
numType,
intType,
doubleType,
boolType,
stringType
];
@override
DartObjectImpl get nullObject {
// TODO(paulberry): implement if needed
throw new UnimplementedError();
}
@override
InterfaceType get nullType =>
_nullType ??= _buildInterfaceType(_linker.coreLibrary, 'Null');
@override
InterfaceType get numType =>
_numType ??= _buildInterfaceType(_linker.coreLibrary, 'num');
@override
InterfaceType get objectType =>
_objectType ??= _buildInterfaceType(_linker.coreLibrary, 'Object');
@override
InterfaceType get stackTraceType => _stackTraceType ??=
_buildInterfaceType(_linker.coreLibrary, 'StackTrace');
@override
InterfaceType get streamDynamicType =>
_streamDynamicType ??= streamType.instantiate(<DartType>[dynamicType]);
@override
InterfaceType get streamType =>
_streamType ??= _buildInterfaceType(_linker.asyncLibrary, 'Stream');
@override
InterfaceType get stringType =>
_stringType ??= _buildInterfaceType(_linker.coreLibrary, 'String');
@override
InterfaceType get symbolType =>
_symbolType ??= _buildInterfaceType(_linker.coreLibrary, 'Symbol');
@override
InterfaceType get typeType =>
_typeType ??= _buildInterfaceType(_linker.coreLibrary, 'Type');
@override
DartType get undefinedType => UndefinedTypeImpl.instance;
InterfaceType _buildInterfaceType(
LibraryElementForLink library, String name) {
return library
.getContainedName(name)
.buildType((int i) => DynamicTypeImpl.instance, const []);
}
}
/**
* Singleton element used for unresolved references.
*/
class UndefinedElementForLink implements ReferenceableElementForLink {
static const UndefinedElementForLink instance =
const UndefinedElementForLink._();
const UndefinedElementForLink._();
@override
ConstructorElementForLink get asConstructor => null;
@override
ConstVariableNode get asConstVariable => null;
@override
DartType get asStaticType => DynamicTypeImpl.instance;
@override
TypeInferenceNode get asTypeInferenceNode => null;
@override
DartType buildType(DartType getTypeArgument(int i),
List<int> implicitFunctionTypeIndices) =>
DynamicTypeImpl.instance;
@override
ReferenceableElementForLink getContainedName(String name) => this;
}
/**
* Element representing a top level variable resynthesized from a
* summary during linking.
*/
class VariableElementForLink
implements VariableElementImpl, ReferenceableElementForLink {
/**
* The unlinked representation of the variable in the summary.
*/
final UnlinkedVariable unlinkedVariable;
/**
* If this variable is declared `const` and the enclosing library is
* part of the build unit being linked, the variable's node in the
* constant evaluation dependency graph. Otherwise `null`.
*/
ConstNode _constNode;
/**
* If this variable has an initializer and an implicit type, and the enclosing
* library is part of the build unit being linked, the variable's node in the
* type inference dependency graph. Otherwise `null`.
*/
TypeInferenceNode _typeInferenceNode;
FunctionElementForLink_Initializer _initializer;
/**
* The compilation unit in which this variable appears.
*/
final CompilationUnitElementForLink compilationUnit;
VariableElementForLink(this.unlinkedVariable, this.compilationUnit) {
if (compilationUnit.isInBuildUnit && unlinkedVariable.constExpr != null) {
_constNode = new ConstVariableNode(this);
if (unlinkedVariable.type == null) {
_typeInferenceNode = new TypeInferenceNode(this);
}
}
}
@override
ConstructorElementForLink get asConstructor => null;
@override
ConstVariableNode get asConstVariable => _constNode;
@override
DartType get asStaticType {
// TODO(paulberry): implement.
throw new UnimplementedError();
}
@override
TypeInferenceNode get asTypeInferenceNode => _typeInferenceNode;
@override
bool get hasImplicitType => unlinkedVariable.type == null;
@override
FunctionElementForLink_Initializer get initializer {
if (unlinkedVariable.constExpr == null) {
return null;
} else {
return _initializer ??= new FunctionElementForLink_Initializer(this);
}
}
@override
bool get isConst => unlinkedVariable.isConst;
@override
bool get isFinal => unlinkedVariable.isFinal;
@override
bool get isStatic;
@override
bool get isSynthetic => false;
@override
String get name => unlinkedVariable.name;
@override
void set type(DartType newType) {
// TODO(paulberry): store inferred type.
}
@override
DartType buildType(DartType getTypeArgument(int i),
List<int> implicitFunctionTypeIndices) =>
DynamicTypeImpl.instance;
ReferenceableElementForLink getContainedName(String name) {
return new NonstaticMemberElementForLink(_constNode);
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Instances of [_Linker] contain the necessary information to link
* together a single build unit.
*/
class _Linker {
/**
* Callback to ask the client for a [LinkedLibrary] for a
* dependency.
*/
final GetDependencyCallback getDependency;
/**
* Callback to ask the client for an [UnlinkedUnit].
*/
final GetUnitCallback getUnit;
/**
* Map containing all library elements accessed during linking,
* whether they are part of the build unit being linked or whether
* they are dependencies.
*/
final Map<Uri, LibraryElementForLink> _libraries =
<Uri, LibraryElementForLink>{};
/**
* List of library elements for the libraries in the build unit
* being linked.
*/
final List<LibraryElementInBuildUnit> _librariesInBuildUnit =
<LibraryElementInBuildUnit>[];
/**
* Indicates whether type inference should use strong mode rules.
*/
final bool strongMode;
LibraryElementForLink _coreLibrary;
LibraryElementForLink _asyncLibrary;
TypeProviderForLink _typeProvider;
_Linker(Map<String, LinkedLibraryBuilder> linkedLibraries, this.getDependency,
this.getUnit, this.strongMode) {
// Create elements for the libraries to be linked. The rest of
// the element model will be created on demand.
linkedLibraries
.forEach((String absoluteUri, LinkedLibraryBuilder linkedLibrary) {
Uri uri = Uri.parse(absoluteUri);
_librariesInBuildUnit.add(_libraries[uri] =
new LibraryElementInBuildUnit(this, uri, linkedLibrary));
});
}
/**
* Get the library element for `dart:async`.
*/
LibraryElementForLink get asyncLibrary =>
_asyncLibrary ??= getLibrary(Uri.parse('dart:async'));
/**
* Get the library element for `dart:core`.
*/
LibraryElementForLink get coreLibrary =>
_coreLibrary ??= getLibrary(Uri.parse('dart:core'));
/**
* Get an instance of [TypeProvider] for use during linking.
*/
TypeProviderForLink get typeProvider =>
_typeProvider ??= new TypeProviderForLink(this);
/**
* Get the library element for the library having the given [uri].
*/
LibraryElementForLink getLibrary(Uri uri) => _libraries.putIfAbsent(
uri,
() => new LibraryElementInDependency(
this, uri, getDependency(uri.toString())));
/**
* Perform type inference and const cycle detection on all libraries
* in the build unit being linked.
*/
void link() {
// TODO(paulberry): link library cycles in appropriate dependency order.
for (LibraryElementInBuildUnit library in _librariesInBuildUnit) {
library.link();
}
// TODO(paulberry): set dependencies.
}
/**
* Throw away any information stored in the summary by a previous call to
* [link].
*/
void unlink() {
for (LibraryElementInBuildUnit library in _librariesInBuildUnit) {
library.unlink();
}
}
}