blob: 673e9d8410266f28f5f9805dd778e04e8eed91d1 [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/ast/token.dart' show TokenType;
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/dart/resolver/inheritance_manager.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/static_type_analyzer.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';
import 'package:front_end/src/dependency_walker.dart';
bool isIncrementOrDecrement(UnlinkedExprAssignOperator operator) {
switch (operator) {
case UnlinkedExprAssignOperator.prefixDecrement:
case UnlinkedExprAssignOperator.prefixIncrement:
case UnlinkedExprAssignOperator.postfixDecrement:
case UnlinkedExprAssignOperator.postfixIncrement:
return true;
default:
return false;
}
}
/**
* 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,
GetDeclaredVariable getDeclaredVariable,
bool strong) {
Map<String, LinkedLibraryBuilder> linkedLibraries =
setupForLink(libraryUris, getUnit, getDeclaredVariable);
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();
}
/**
* Prepare to link together the build unit consisting of [libraryUris], using
* [getUnit] to fetch the [UnlinkedUnit] objects from both this build unit and
* other build units.
*
* The libraries are prelinked, and 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> setupForLink(Set<String> libraryUris,
GetUnitCallback getUnit, GetDeclaredVariable getDeclaredVariable) {
Map<String, LinkedLibraryBuilder> linkedLibraries =
<String, LinkedLibraryBuilder>{};
for (String absoluteUri in libraryUris) {
linkedLibraries[absoluteUri] = prelink(
absoluteUri,
getUnit(absoluteUri),
getUnit,
(String absoluteUri) => getUnit(absoluteUri)?.publicNamespace,
getDeclaredVariable);
}
return linkedLibraries;
}
/**
* 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,
TypeParameterizedElementMixin typeParameterContext,
{int slot}) {
EntityRefBuilder result = new EntityRefBuilder(slot: slot);
if (type is InterfaceType) {
ClassElementForLink element = type.element;
result.reference = compilationUnit.addReference(element);
_storeTypeArguments(
type.typeArguments, result, compilationUnit, typeParameterContext);
return result;
} else if (type is DynamicTypeImpl) {
result.reference = compilationUnit.addRawReference('dynamic');
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) {
TypeParameterElementImpl element = type.element;
if (typeParameterContext.isTypeParameterInScope(element)) {
result.paramReference =
typeParameterContext.typeParameterNestingLevel - element.nestingLevel;
} else {
throw new StateError('The type parameter $type (in ${element?.location}) '
'is out of scope on ${typeParameterContext?.location}.');
}
return result;
} else if (type is FunctionType) {
Element element = type.element;
if (element is FunctionElementForLink_FunctionTypedParam) {
result.reference =
compilationUnit.addReference(element.typeParameterContext);
result.implicitFunctionTypeIndices = element.implicitFunctionTypeIndices;
_storeTypeArguments(
type.typeArguments, result, compilationUnit, typeParameterContext);
return result;
}
if (element is TopLevelFunctionElementForLink) {
result.reference = compilationUnit.addReference(element);
_storeTypeArguments(
type.typeArguments, result, compilationUnit, typeParameterContext);
return result;
}
if (element is MethodElementForLink) {
result.reference = compilationUnit.addReference(element);
_storeTypeArguments(
type.typeArguments, result, compilationUnit, typeParameterContext);
return result;
}
if (element is FunctionTypeAliasElementForLink) {
result.reference = compilationUnit.addReference(element);
_storeTypeArguments(
type.typeArguments, result, compilationUnit, typeParameterContext);
return result;
}
if (element is FunctionElement && element.enclosingElement == null) {
// Element is a synthetic function element that was generated on the fly
// to represent a type that has no associated source code location.
result.syntheticReturnType = _createLinkedType(
element.returnType, compilationUnit, typeParameterContext);
result.entityKind =
element.returnType?.element is GenericFunctionTypeElement
? EntityRefKind.genericFunctionType
: EntityRefKind.syntheticFunction;
result.syntheticParams = element.parameters
.map((ParameterElement param) => _serializeSyntheticParam(
param, compilationUnit, typeParameterContext))
.toList();
return result;
}
if (element is FunctionElement) {
// Element is a local function inside another executable.
result.reference = compilationUnit.addReference(element);
// TODO(paulberry): do I need to store type arguments?
return result;
}
// TODO(paulberry): implement other cases.
throw new UnimplementedError('${element.runtimeType}');
}
// TODO(paulberry): implement other cases.
throw new UnimplementedError('${type.runtimeType}');
}
DartType _dynamicIfNull(DartType type) {
if (type == null || type.isBottom || type.isDartCoreNull) {
return DynamicTypeImpl.instance;
}
return type;
}
/**
* Create an [UnlinkedParam] representing the given [parameter], which should be
* a parameter of a synthetic function type (e.g. one produced during type
* inference as a result of computing the least upper bound of two function
* types).
*/
UnlinkedParamBuilder _serializeSyntheticParam(
ParameterElement parameter,
CompilationUnitElementInBuildUnit compilationUnit,
TypeParameterizedElementMixin typeParameterContext) {
UnlinkedParamBuilder b = new UnlinkedParamBuilder();
b.name = parameter.name;
switch (parameter.parameterKind) {
case ParameterKind.REQUIRED:
b.kind = UnlinkedParamKind.required;
break;
case ParameterKind.POSITIONAL:
b.kind = UnlinkedParamKind.positional;
break;
case ParameterKind.NAMED:
b.kind = UnlinkedParamKind.named;
break;
}
DartType type = parameter.type;
if (!parameter.hasImplicitType) {
if (type is FunctionType && type.element.isSynthetic) {
b.isFunctionTyped = true;
b.type = _createLinkedType(
type.returnType, compilationUnit, typeParameterContext);
b.parameters = type.parameters
.map((parameter) => _serializeSyntheticParam(
parameter, compilationUnit, typeParameterContext))
.toList();
} else {
b.type = _createLinkedType(type, compilationUnit, typeParameterContext);
}
}
return b;
}
/**
* Store the given [typeArguments] in [encodedType], using [compilationUnit] and
* [typeParameterContext] to serialize them.
*/
void _storeTypeArguments(
List<DartType> typeArguments,
EntityRefBuilder encodedType,
CompilationUnitElementInBuildUnit compilationUnit,
TypeParameterizedElementMixin typeParameterContext) {
int count = typeArguments.length;
List<EntityRefBuilder> encodedTypeArguments =
new List<EntityRefBuilder>(count);
for (int i = 0; i < count; i++) {
encodedTypeArguments[i] = _createLinkedType(
typeArguments[i], compilationUnit, typeParameterContext);
}
encodedType.typeArguments = encodedTypeArguments;
}
/**
* 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);
/**
* Stub implementation of [AnalysisOptions] used during linking.
*/
class AnalysisOptionsForLink implements AnalysisOptions {
final Linker _linker;
AnalysisOptionsForLink(this._linker);
@override
bool get strongMode => _linker.strongMode;
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Element representing a class or enum resynthesized from a summary
* during linking.
*/
abstract class ClassElementForLink extends Object
with ReferenceableElementForLink
implements AbstractClassElementImpl {
Map<String, ReferenceableElementForLink> _containedNames;
@override
final CompilationUnitElementForLink enclosingElement;
/// TODO(brianwilkerson) This appears to be unused and might be removable.
bool hasBeenInferred;
ClassElementForLink(CompilationUnitElementForLink enclosingElement)
: enclosingElement = enclosingElement,
hasBeenInferred = !enclosingElement.isInBuildUnit;
@override
List<PropertyAccessorElementForLink> get accessors;
@override
ClassElementForLink get asClass => this;
@override
ConstructorElementForLink get asConstructor => unnamedConstructor;
@override
DartType get asStaticType =>
enclosingElement.enclosingElement._linker.typeProvider.typeType;
@override
List<ConstructorElementForLink> get constructors;
@override
CompilationUnitElementImpl get enclosingUnit => enclosingElement;
@override
List<FieldElementForLink> get fields;
/**
* Indicates whether this is the core class `Object`.
*/
bool get isObject;
@override
LibraryElementForLink get library => enclosingElement.library;
@override
List<MethodElementForLink> get methods;
@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 (PropertyAccessorElementForLink accessor in accessors) {
_containedNames[accessor.name] = accessor;
}
for (MethodElementForLink method in methods) {
_containedNames[method.name] = method;
}
}
return _containedNames.putIfAbsent(
name, () => UndefinedElementForLink.instance);
}
@override
FieldElement getField(String name) {
for (FieldElement fieldElement in fields) {
if (name == fieldElement.name) {
return fieldElement;
}
}
return null;
}
@override
PropertyAccessorElement getGetter(String getterName) {
for (PropertyAccessorElement accessor in accessors) {
if (accessor.isGetter && accessor.name == getterName) {
return accessor;
}
}
return null;
}
@override
MethodElement getMethod(String methodName) {
for (MethodElement method in methods) {
if (method.name == methodName) {
return method;
}
}
return null;
}
/**
* 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 TypeParameterizedElementMixin
implements ClassElementImpl {
/**
* 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_Executable accessor =
new PropertyAccessorElementForLink_Executable(enclosingElement,
this, unlinkedExecutable, syntheticVariable);
_accessors.add(accessor);
if (unlinkedExecutable.kind == UnlinkedExecutableKind.getter) {
syntheticVariable._getter = accessor;
} else {
syntheticVariable._setter = accessor;
}
}
}
for (FieldElementForLink_ClassField field in fields) {
_accessors.add(field.getter);
if (!field.isConst && !field.isFinal) {
_accessors.add(field.setter);
}
}
}
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));
}
}
if (_constructors.isEmpty) {
_unnamedConstructorComputed = true;
_unnamedConstructor = new ConstructorElementForLink_Synthetic(this);
_constructors.add(_unnamedConstructor);
}
}
return _constructors;
}
@override
ContextForLink get context => enclosingUnit.context;
@override
String get displayName => _unlinkedClass.name;
@override
TypeParameterizedElementMixin 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
String get identifier => name;
@override
List<InterfaceType> get interfaces => _interfaces ??=
_unlinkedClass.interfaces.map(_computeInterfaceType).toList();
@override
bool get isEnum => false;
@override
bool get isMixinApplication => _unlinkedClass.isMixinApplication;
@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
InterfaceType get type =>
_type ??= buildType((int i) => typeParameterTypes[i], null);
@override
List<UnlinkedTypeParam> get unlinkedTypeParams =>
_unlinkedClass.typeParameters;
@override
ConstructorElementForLink get unnamedConstructor {
if (!_unnamedConstructorComputed) {
for (ConstructorElementForLink constructor in constructors) {
if (constructor.name.isEmpty) {
_unnamedConstructor = constructor;
break;
}
}
_unnamedConstructorComputed = true;
}
return _unnamedConstructor;
}
@override
int get version => 0;
@override
DartType buildType(
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
int numTypeParameters = _unlinkedClass.typeParameters.length;
if (numTypeParameters != 0) {
List<DartType> typeArguments =
new List<DartType>.generate(numTypeParameters, getTypeArgument);
if (typeArguments.contains(null)) {
return context.typeSystem.instantiateToBounds(this.type);
} else {
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 (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);
}
}
}
@override
String toString() => '$enclosingElement.$name';
/**
* Convert [typeRef] into an [InterfaceType].
*/
InterfaceType _computeInterfaceType(EntityRef typeRef) {
if (typeRef != null) {
DartType type = enclosingElement.resolveTypeRef(this, typeRef);
if (type is InterfaceType && !type.element.isEnum) {
return type;
}
// In the event that the `typeRef` 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
implements EnumElementImpl {
/**
* The unlinked representation of the enum in the summary.
*/
final UnlinkedEnum _unlinkedEnum;
InterfaceType _type;
List<FieldElementForLink> _fields;
List<PropertyAccessorElementForLink> _accessors;
DartType _valuesType;
ClassElementForLink_Enum(
CompilationUnitElementForLink enclosingElement, this._unlinkedEnum)
: super(enclosingElement);
@override
List<PropertyAccessorElementForLink> get accessors {
if (_accessors == null) {
_accessors = <PropertyAccessorElementForLink>[];
for (FieldElementForLink field in fields) {
_accessors.add(field.getter);
}
}
return _accessors;
}
@override
List<ConstructorElementForLink> get constructors => const [];
@override
String get displayName => _unlinkedEnum.name;
@override
List<FieldElementForLink> get fields {
if (_fields == null) {
_fields = <FieldElementForLink>[];
_fields.add(new FieldElementForLink_EnumField_values(this));
for (UnlinkedEnumValue value in _unlinkedEnum.values) {
_fields.add(new FieldElementForLink_EnumField_value(this, value));
}
_fields.add(new FieldElementForLink_EnumField_index(this));
}
return _fields;
}
@override
List<InterfaceType> get interfaces => const [];
@override
bool get isEnum => true;
@override
bool get isObject => false;
@override
List<MethodElementForLink> get methods => const [];
@override
List<InterfaceType> get mixins => const [];
@override
String get name => _unlinkedEnum.name;
@override
InterfaceType get supertype => library._linker.typeProvider.objectType;
@override
InterfaceType get type => _type ??= new InterfaceTypeImpl(this);
@override
List<TypeParameterElement> get typeParameters => const [];
@override
ConstructorElementForLink get unnamedConstructor => null;
/**
* Get the type of the enum's static member `values`.
*/
DartType get valuesType =>
_valuesType ??= library._linker.typeProvider.listType.instantiate([type]);
@override
DartType buildType(DartType getTypeArgument(int i),
List<int> implicitFunctionTypeIndices) =>
type;
@override
void link(CompilationUnitElementInBuildUnit compilationUnit) {}
@override
String toString() => '$enclosingElement.$name';
}
/**
* Element representing a compilation unit resynthesized from a
* summary during linking.
*/
abstract class CompilationUnitElementForLink
implements CompilationUnitElementImpl, ResynthesizerContext {
/**
* 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;
/**
* The absolute URI of this compilation unit.
*/
final String _absoluteUri;
List<ClassElementForLink_Class> _types;
Map<String, ReferenceableElementForLink> _containedNames;
List<TopLevelVariableElementForLink> _topLevelVariables;
List<ClassElementForLink_Enum> _enums;
List<TopLevelFunctionElementForLink> _functions;
List<PropertyAccessorElementForLink> _accessors;
List<FunctionTypeAliasElementForLink> _functionTypeAliases;
/**
* Index of this unit in the list of units in the enclosing library.
*/
final int unitNum;
CompilationUnitElementForLink(UnlinkedUnit unlinkedUnit, this.unitNum,
int numReferences, this._absoluteUri)
: _references = new List<ReferenceableElementForLink>(numReferences),
_unlinkedUnit = unlinkedUnit;
@override
List<PropertyAccessorElementForLink> get accessors {
if (_accessors == null) {
_accessors = <PropertyAccessorElementForLink>[];
Map<String, SyntheticVariableElementForLink> syntheticVariables =
<String, SyntheticVariableElementForLink>{};
for (UnlinkedExecutable unlinkedExecutable in _unlinkedUnit.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_Executable accessor =
new PropertyAccessorElementForLink_Executable(
this, null, unlinkedExecutable, syntheticVariable);
_accessors.add(accessor);
if (unlinkedExecutable.kind == UnlinkedExecutableKind.getter) {
syntheticVariable._getter = accessor;
} else {
syntheticVariable._setter = accessor;
}
}
}
for (TopLevelVariableElementForLink variable in topLevelVariables) {
_accessors.add(variable.getter);
if (!variable.isConst && !variable.isFinal) {
_accessors.add(variable.setter);
}
}
}
return _accessors;
}
@override
ContextForLink get context => library.context;
@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;
}
@override
List<TopLevelFunctionElementForLink> get functions {
if (_functions == null) {
_functions = <TopLevelFunctionElementForLink>[];
for (UnlinkedExecutable executable in _unlinkedUnit.executables) {
if (executable.kind == UnlinkedExecutableKind.functionOrMethod) {
_functions.add(new TopLevelFunctionElementForLink(this, executable));
}
}
}
return _functions;
}
@override
List<FunctionTypeAliasElementForLink> get functionTypeAliases =>
_functionTypeAliases ??= _unlinkedUnit.typedefs.map((UnlinkedTypedef t) {
if (t.style == TypedefStyle.functionType) {
return new FunctionTypeAliasElementForLink(this, t);
} else if (t.style == TypedefStyle.genericFunctionType) {
return new GenericTypeAliasElementForLink(this, t);
}
}).toList();
@override
String get identifier => _absoluteUri;
/**
* Indicates whether this compilation element is part of the build unit
* currently being linked.
*/
bool get isInBuildUnit;
/**
* Determine whether type inference is complete in this compilation unit.
*/
bool get isTypeInferenceComplete {
LibraryCycleForLink libraryCycleForLink = library.libraryCycleForLink;
if (libraryCycleForLink == null) {
return true;
} else {
return libraryCycleForLink._node.isEvaluated;
}
}
@override
LibraryElementForLink get library => enclosingElement;
@override
ResynthesizerContext get resynthesizerContext => this;
@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 (TopLevelFunctionElementForLink function in functions) {
_containedNames[function.name] = function;
}
for (PropertyAccessorElementForLink accessor in accessors) {
_containedNames[accessor.name] = accessor;
}
for (FunctionTypeAliasElementForLink functionTypeAlias
in functionTypeAliases) {
_containedNames[functionTypeAlias.name] = functionTypeAlias;
}
// TODO(paulberry): fill in other top level entities (typedefs
// and executables).
}
return _containedNames.putIfAbsent(
name, () => UndefinedElementForLink.instance);
}
/**
* Compute the type referred to by the given linked type [slot] (interpreted
* in [context]). If there is no inferred type in the
* given slot, `dynamic` is returned.
*/
DartType getLinkedType(ElementImpl context, int slot);
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
/**
* Return the class element for the constructor referred to by the given
* [index] in [UnlinkedUnit.references]. If the reference is unresolved,
* return [UndefinedElementForLink.instance].
*/
ReferenceableElementForLink resolveConstructorClassRef(int index) {
LinkedReference linkedReference = _linkedUnit.references[index];
if (linkedReference.kind == ReferenceKind.classOrEnum) {
return resolveRef(index);
}
if (index < _unlinkedUnit.references.length) {
UnlinkedReference unlinkedReference = _unlinkedUnit.references[index];
return resolveRef(unlinkedReference.prefixReference);
}
return UndefinedElementForLink.instance;
}
/**
* 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 =
index < _unlinkedUnit.references.length
? _unlinkedUnit.references[index]
: null;
LinkedReference linkedReference = _linkedUnit.references[index];
String name = unlinkedReference == null
? linkedReference.name
: unlinkedReference.name;
int containingReference = unlinkedReference == null
? linkedReference.containingReference
: unlinkedReference.prefixReference;
if (containingReference != 0 &&
_linkedUnit.references[containingReference].kind !=
ReferenceKind.prefix) {
if (linkedReference.kind == ReferenceKind.function) {
// Local function
_references[index] = resolveRef(containingReference)
.getLocalFunction(linkedReference.localIndex) ??
UndefinedElementForLink.instance;
} else {
_references[index] =
resolveRef(containingReference).getContainedName(name);
}
} else if (linkedReference.dependency == 0) {
if (name == 'void') {
_references[index] = enclosingElement._linker.voidElement;
} else if (name == '*bottom*') {
_references[index] = enclosingElement._linker.bottomElement;
} else if (name == 'dynamic') {
_references[index] = enclosingElement._linker.dynamicElement;
} else {
_references[index] = enclosingElement.getContainedName(name);
}
} else {
LibraryElementForLink dependency =
enclosingElement._getDependency(linkedReference.dependency);
_references[index] = dependency.getContainedName(name);
}
}
return _references[index];
}
@override
DartType resolveTypeRef(ElementImpl context, EntityRef type,
{bool defaultVoid: false,
bool instantiateToBoundsAllowed: true,
bool declaredType: false}) {
if (type == null) {
if (defaultVoid) {
return VoidTypeImpl.instance;
} else {
return DynamicTypeImpl.instance;
}
}
if (type.paramReference != 0) {
return context.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(context, type.typeArguments[i]);
} else if (!instantiateToBoundsAllowed) {
// Do not allow buildType to instantiate the bounds; force dynamic.
return DynamicTypeImpl.instance;
} else {
return null;
}
}
ReferenceableElementForLink element = resolveRef(type.reference);
return element.buildType(
getTypeArgument, type.implicitFunctionTypeIndices);
}
}
@override
String toString() => enclosingElement.toString();
}
/**
* 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,
String absoluteUri)
: super(
unlinkedUnit, unitNum, unlinkedUnit.references.length, absoluteUri);
@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,
int localIndex: 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];
int candidateContainingReference = i < unlinkedReferences.length
? unlinkedReferences[i].prefixReference
: linkedReference.containingReference;
if (candidateContainingReference != 0 &&
linkedReferences[candidateContainingReference].kind ==
ReferenceKind.prefix) {
// We don't need to match containing references when they are prefixes,
// since the relevant information is in linkedReference.dependency.
candidateContainingReference = 0;
}
if (linkedReference.dependency == dependency &&
(i < unlinkedReferences.length
? unlinkedReferences[i].name
: linkedReference.name) ==
name &&
linkedReference.numTypeParameters == numTypeParameters &&
linkedReference.unit == unitNum &&
candidateContainingReference == containingReference &&
linkedReference.kind == kind &&
linkedReference.localIndex == localIndex) {
return i;
}
}
int result = linkedReferences.length;
linkedReferences.add(new LinkedReferenceBuilder(
dependency: dependency,
name: name,
numTypeParameters: numTypeParameters,
unit: unitNum,
containingReference: containingReference,
kind: kind,
localIndex: localIndex));
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 FunctionTypeAliasElementForLink) {
return addRawReference(element.name,
dependency: library.addDependency(element.library),
numTypeParameters: element.typeParameters.length,
unitNum: element.enclosingElement.unitNum,
kind: ReferenceKind.typedef);
} else if (element is FunctionElementForLink_Initializer) {
return addRawReference('',
containingReference: addReference(element.enclosingElement),
kind: ReferenceKind.function,
localIndex: 0);
} else if (element is FunctionElementForLink_Local_NonSynthetic) {
ExecutableElementForLink parent = element.enclosingElement;
int localIndex = parent.functions.indexOf(element);
assert(localIndex != -1);
return addRawReference(element.name,
containingReference: addReference(parent),
kind: ReferenceKind.function,
localIndex: localIndex);
} else if (element is ExecutableElementForLink_NonLocal) {
ClassElementForLink_Class enclosingClass = element.enclosingClass;
ReferenceKind kind;
switch (element._unlinkedExecutable.kind) {
case UnlinkedExecutableKind.functionOrMethod:
kind = enclosingClass != null
? ReferenceKind.method
: ReferenceKind.topLevelFunction;
break;
case UnlinkedExecutableKind.setter:
kind = ReferenceKind.propertyAccessor;
break;
default:
// TODO(paulberry): implement other cases as necessary
throw new UnimplementedError('${element._unlinkedExecutable.kind}');
}
if (enclosingClass == null) {
return addRawReference(element.name,
numTypeParameters: element.typeParameters.length,
dependency:
library.addDependency(element.library as LibraryElementForLink),
unitNum: element.compilationUnit.unitNum,
kind: kind);
} else {
return addRawReference(element.name,
numTypeParameters: element.typeParameters.length,
containingReference: addReference(enclosingClass),
kind: kind);
}
} else if (element is FunctionElementForLink_Initializer) {
return addRawReference('',
containingReference: addReference(element.enclosingElement),
kind: ReferenceKind.function);
} else if (element is TopLevelVariableElementForLink) {
return addRawReference(element.name,
dependency: library.addDependency(element.library),
unitNum: element.compilationUnit.unitNum,
kind: ReferenceKind.topLevelPropertyAccessor);
} else if (element is FieldElementForLink_ClassField) {
ClassElementForLink_Class enclosingClass = element.enclosingElement;
// Note: even if the class has type parameters, we don't need to set
// numTypeParameters because numTypeParameters does not count type
// parameters of parent elements (see
// [LinkedReference.numTypeParameters]).
return addRawReference(element.name,
containingReference: addReference(enclosingClass),
kind: ReferenceKind.propertyAccessor);
}
// TODO(paulberry): implement other cases
throw new UnimplementedError('${element.runtimeType}');
}
@override
DartType getLinkedType(ElementImpl context, int slot) {
// This method should only be called on compilation units that come from
// dependencies, never on compilation units that are part of the current
// build unit.
throw new StateError(
'Linker tried to access linked type from current build unit');
}
/**
* Perform type inference and const cycle detection on this
* compilation unit.
*/
void link() {
if (library._linker.strongMode) {
new InstanceMemberInferrer(enclosingElement._linker.typeProvider,
enclosingElement.inheritanceManager, new Set<FieldElement>())
.inferCompilationUnit(this);
for (TopLevelVariableElementForLink variable in topLevelVariables) {
variable.link(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.parametersInheritingCovariant.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 fact that the given [slot] represents a parameter that inherits
* `@covariant` behavior.
*/
void _storeInheritsCovariant(int slot) {
_linkedUnit.parametersInheritingCovariant.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,
TypeParameterizedElementMixin typeParameterContext) {
if (slot != 0) {
if (linkedType != null && !linkedType.isDynamic) {
_linkedUnit.types.add(_createLinkedType(
linkedType, this, typeParameterContext,
slot: slot));
}
}
}
/**
* Store the given error [error] in the given [slot].
*/
void _storeLinkedTypeError(int slot, TopLevelInferenceErrorBuilder error) {
if (slot != 0) {
if (error != null) {
error.slot = slot;
_linkedUnit.topLevelInferenceErrors.add(error);
}
}
}
}
/**
* 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;
List<EntityRef> _linkedTypeRefs;
@override
final LibraryElementInDependency enclosingElement;
CompilationUnitElementInDependency(
this.enclosingElement,
UnlinkedUnit unlinkedUnit,
LinkedUnit linkedUnit,
int unitNum,
String absoluteUri)
: _linkedUnit = linkedUnit,
super(
unlinkedUnit, unitNum, linkedUnit.references.length, absoluteUri) {
// Make one pass through the linked types to determine the lengths for
// _linkedTypeRefs and _linkedTypes. TODO(paulberry): add an int to the
// summary to make this unnecessary.
int maxLinkedTypeSlot = 0;
for (EntityRef ref in _linkedUnit.types) {
if (ref.slot > maxLinkedTypeSlot) {
maxLinkedTypeSlot = ref.slot;
}
}
// Initialize _linkedTypeRefs.
_linkedTypeRefs = new List<EntityRef>(maxLinkedTypeSlot + 1);
for (EntityRef ref in _linkedUnit.types) {
_linkedTypeRefs[ref.slot] = ref;
}
}
@override
bool get isInBuildUnit => false;
@override
DartType getLinkedType(ElementImpl context, int slot) {
if (slot < _linkedTypeRefs.length) {
return resolveTypeRef(context, _linkedTypeRefs[slot]);
} else {
return DynamicTypeImpl.instance;
}
}
}
/**
* 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
.enclosingClass
.getContainedName(constructorInitializer.name)
.asConstructor;
safeAddDependency(constructor?._constNode);
}
CompilationUnitElementForLink compilationUnit =
constructorElement.enclosingElement.enclosingElement;
collectDependencies(
dependencies, constructorInitializer.expression, compilationUnit);
for (UnlinkedExpr 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.getter.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.compilationUnit
.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,
UnlinkedExpr unlinkedConst,
CompilationUnitElementForLink compilationUnit) {
if (unlinkedConst == null) {
return;
}
int refPtr = 0;
int intPtr = 0;
for (UnlinkedExprOperation operation in unlinkedConst.operations) {
switch (operation) {
case UnlinkedExprOperation.pushInt:
intPtr++;
break;
case UnlinkedExprOperation.pushLongInt:
int numInts = unlinkedConst.ints[intPtr++];
intPtr += numInts;
break;
case UnlinkedExprOperation.concatenate:
intPtr++;
break;
case UnlinkedExprOperation.pushReference:
EntityRef ref = unlinkedConst.references[refPtr++];
ConstVariableNode variable =
compilationUnit.resolveRef(ref.reference).asConstVariable;
if (variable != null) {
dependencies.add(variable);
}
break;
case UnlinkedExprOperation.makeUntypedList:
case UnlinkedExprOperation.makeUntypedMap:
intPtr++;
break;
case UnlinkedExprOperation.assignToRef:
refPtr++;
break;
case UnlinkedExprOperation.invokeMethodRef:
EntityRef ref = unlinkedConst.references[refPtr++];
ConstVariableNode variable =
compilationUnit.resolveRef(ref.reference).asConstVariable;
if (variable != null) {
dependencies.add(variable);
}
intPtr += 2;
int numTypeArguments = unlinkedConst.ints[intPtr++];
refPtr += numTypeArguments;
break;
case UnlinkedExprOperation.invokeMethod:
intPtr += 2;
int numTypeArguments = unlinkedConst.ints[intPtr++];
refPtr += numTypeArguments;
break;
case UnlinkedExprOperation.makeTypedList:
refPtr++;
intPtr++;
break;
case UnlinkedExprOperation.makeTypedMap:
refPtr += 2;
intPtr++;
break;
case UnlinkedExprOperation.invokeConstructor:
EntityRef ref = unlinkedConst.references[refPtr++];
ConstructorElementForLink element =
compilationUnit.resolveRef(ref.reference).asConstructor;
if (element?._constNode != null) {
dependencies.add(element._constNode);
}
intPtr += 2;
break;
case UnlinkedExprOperation.typeCast:
case UnlinkedExprOperation.typeCheck:
refPtr++;
break;
case UnlinkedExprOperation.pushLocalFunctionReference:
intPtr += 2;
break;
default:
break;
}
}
assert(refPtr == unlinkedConst.references.length);
assert(intPtr == unlinkedConst.ints.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.initializer?.bodyExpr,
parameterElement.compilationUnit);
return dependencies;
}
}
/**
* Element representing a constructor resynthesized from a summary
* during linking.
*/
class ConstructorElementForLink extends ExecutableElementForLink_NonLocal
with ReferenceableElementForLink
implements ConstructorElementImpl {
/**
* 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 enclosingClass,
UnlinkedExecutable unlinkedExecutable)
: super(enclosingClass.enclosingElement, enclosingClass,
unlinkedExecutable) {
if (enclosingClass.enclosingElement.isInBuildUnit &&
_unlinkedExecutable != null &&
_unlinkedExecutable.constCycleSlot != 0) {
_constNode = new ConstConstructorNode(this);
}
}
@override
ConstructorElementForLink get asConstructor => this;
@override
ClassElementImpl get enclosingElement => super.enclosingClass;
@override
String get identifier => name;
@override
bool get isCycleFree {
if (!_constNode.isEvaluated) {
new ConstDependencyWalker().walk(_constNode);
}
return _constNode.isCycleFree;
}
@override
DartType get returnType => enclosingElement.type;
@override
List<TypeParameterElement> get typeParameters => const [];
/**
* 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);
}
/**
* A synthetic constructor.
*/
class ConstructorElementForLink_Synthetic extends ConstructorElementForLink {
ConstructorElementForLink_Synthetic(
ClassElementForLink_Class enclosingElement)
: super(enclosingElement, null);
@override
String get name => '';
@override
List<ParameterElement> get parameters => const <ParameterElement>[];
}
/**
* 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.initializer?.bodyExpr,
variableElement.compilationUnit);
return dependencies;
}
}
/**
* Stub implementation of [AnalysisContext] which provides just those methods
* needed during linking.
*/
class ContextForLink implements AnalysisContext {
final Linker _linker;
ContextForLink(this._linker);
@override
AnalysisOptionsForLink get analysisOptions => _linker.analysisOptions;
@override
TypeSystem get typeSystem => _linker.typeSystem;
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Base class for executable elements resynthesized from a summary during
* linking.
*/
abstract class ExecutableElementForLink extends Object
with TypeParameterizedElementMixin, ParameterParentElementForLink
implements ExecutableElementImpl {
/**
* The unlinked representation of the method in the summary.
*/
final UnlinkedExecutable _unlinkedExecutable;
DartType _declaredReturnType;
DartType _inferredReturnType;
FunctionTypeImpl _type;
String _name;
String _displayName;
final CompilationUnitElementForLink compilationUnit;
ExecutableElementForLink(this.compilationUnit, this._unlinkedExecutable);
@override
ContextForLink get context => compilationUnit.context;
/**
* If the executable element had an explicitly declared return type, return
* it. Otherwise return `null`.
*/
DartType get declaredReturnType {
if (_unlinkedExecutable.returnType == null) {
return null;
} else {
return _declaredReturnType ??=
compilationUnit.resolveTypeRef(this, _unlinkedExecutable.returnType);
}
}
@override
String get displayName {
if (_displayName == null) {
_displayName = _unlinkedExecutable.name;
if (_unlinkedExecutable.kind == UnlinkedExecutableKind.setter) {
_displayName = _displayName.substring(0, _displayName.length - 1);
}
}
return _displayName;
}
@override
CompilationUnitElementImpl get enclosingUnit => compilationUnit;
@override
bool get hasImplicitReturnType => _unlinkedExecutable.returnType == null;
@override
List<int> get implicitFunctionTypeIndices => const <int>[];
/**
* Return the inferred return type of the executable element. Should only be
* called if no return type was explicitly declared.
*/
DartType get inferredReturnType {
// We should only try to infer a return type when none is explicitly
// declared.
assert(_unlinkedExecutable.returnType == null);
if (Linker._initializerTypeInferenceCycle != null &&
Linker._initializerTypeInferenceCycle ==
compilationUnit.library.libraryCycleForLink) {
// We are currently computing the type of an initializer expression in the
// current library cycle, so type inference results should be ignored.
return _computeDefaultReturnType();
}
if (_inferredReturnType == null) {
if (_unlinkedExecutable.kind == UnlinkedExecutableKind.constructor) {
// TODO(paulberry): implement.
throw new UnimplementedError();
} else if (compilationUnit.isInBuildUnit) {
_inferredReturnType = _computeDefaultReturnType();
} else {
_inferredReturnType = compilationUnit.getLinkedType(
this, _unlinkedExecutable.inferredReturnTypeSlot);
}
}
return _inferredReturnType;
}
@override
bool get isStatic => _unlinkedExecutable.isStatic;
@override
bool get isSynthetic => false;
@override
LibraryElement get library => enclosingElement.library;
@override
String get name {
if (_name == null) {
_name = _unlinkedExecutable.name;
if (_name == '-' && _unlinkedExecutable.parameters.isEmpty) {
_name = 'unary-';
}
}
return _name;
}
@override
DartType get returnType => declaredReturnType ?? inferredReturnType;
@override
void set returnType(DartType inferredType) {
assert(_inferredReturnType == null);
_inferredReturnType = inferredType;
}
@override
FunctionTypeImpl get type => _type ??= new FunctionTypeImpl(this);
@override
TypeParameterizedElementMixin get typeParameterContext => this;
@override
List<UnlinkedParam> get unlinkedParameters => _unlinkedExecutable.parameters;
@override
List<UnlinkedTypeParam> get unlinkedTypeParams =>
_unlinkedExecutable.typeParameters;
@override
bool isAccessibleIn(LibraryElement library) =>
!Identifier.isPrivateName(name) || identical(this.library, library);
/**
* Compute the default return type for this type of executable element (if no
* return type is declared and strong mode type inference cannot infer a
* better return type).
*/
DartType _computeDefaultReturnType() {
if (_unlinkedExecutable.kind == UnlinkedExecutableKind.setter &&
(library as LibraryElementForLink)._linker.strongMode) {
// In strong mode, setters without an explicit return type are
// considered to return `void`.
return VoidTypeImpl.instance;
} else {
return DynamicTypeImpl.instance;
}
}
}
/**
* Base class for executable elements that are resynthesized from a summary
* during linking and are not local functions.
*/
abstract class ExecutableElementForLink_NonLocal
extends ExecutableElementForLink {
/**
* Return the class in which this executable appears, maybe `null` for a
* top-level function.
*/
final ClassElementForLink_Class enclosingClass;
ExecutableElementForLink_NonLocal(
CompilationUnitElementForLink compilationUnit,
this.enclosingClass,
UnlinkedExecutable unlinkedExecutable)
: super(compilationUnit, unlinkedExecutable);
@override
Element get enclosingElement => enclosingClass ?? compilationUnit;
@override
TypeParameterizedElementMixin get enclosingTypeParameterContext =>
enclosingClass;
/**
* Store the results of type inference for this method in [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
if (_unlinkedExecutable.returnType == null) {
compilationUnit._storeLinkedType(
_unlinkedExecutable.inferredReturnTypeSlot, inferredReturnType, this);
}
for (ParameterElementForLink parameterElement in parameters) {
parameterElement.link(compilationUnit);
}
}
}
class ExprTypeComputer {
final FunctionElementForLink_Local function;
final CompilationUnitElementForLink unit;
final LibraryElementForLink library;
final Linker linker;
final TypeProvider typeProvider;
final UnlinkedExpr unlinkedConst;
final List<DartType> stack = <DartType>[];
int intPtr = 0;
int refPtr = 0;
int strPtr = 0;
int assignmentOperatorPtr = 0;
TopLevelInferenceErrorKind errorKind;
factory ExprTypeComputer(FunctionElementForLink_Local functionElement) {
CompilationUnitElementForLink unit = functionElement.compilationUnit;
LibraryElementForLink library = unit.enclosingElement;
Linker linker = library._linker;
TypeProvider typeProvider = linker.typeProvider;
UnlinkedExpr unlinkedConst = functionElement._unlinkedExecutable.bodyExpr;
return new ExprTypeComputer._(
functionElement, unit, library, linker, typeProvider, unlinkedConst);
}
ExprTypeComputer._(this.function, this.unit, this.library, this.linker,
this.typeProvider, this.unlinkedConst);
DartType compute() {
if (unlinkedConst == null) {
// No function body was stored for this function, so we can't infer its
// return type. Assume `dynamic`.
return DynamicTypeImpl.instance;
}
// If no operations, we cannot compute the type. Assume `dynamic`.
if (unlinkedConst.operations.isEmpty) {
return DynamicTypeImpl.instance;
}
// Perform RPN evaluation of the constant, using a stack of inferred types.
for (UnlinkedExprOperation operation in unlinkedConst.operations) {
switch (operation) {
case UnlinkedExprOperation.pushInt:
intPtr++;
stack.add(typeProvider.intType);
break;
case UnlinkedExprOperation.pushLongInt:
int numInts = _getNextInt();
intPtr += numInts;
stack.add(typeProvider.intType);
break;
case UnlinkedExprOperation.pushDouble:
stack.add(typeProvider.doubleType);
break;
case UnlinkedExprOperation.pushTrue:
case UnlinkedExprOperation.pushFalse:
stack.add(typeProvider.boolType);
break;
case UnlinkedExprOperation.pushString:
strPtr++;
stack.add(typeProvider.stringType);
break;
case UnlinkedExprOperation.concatenate:
stack.length -= _getNextInt();
stack.add(typeProvider.stringType);
break;
case UnlinkedExprOperation.makeSymbol:
strPtr++;
stack.add(typeProvider.symbolType);
break;
case UnlinkedExprOperation.pushNull:
stack.add(typeProvider.nullType);
break;
case UnlinkedExprOperation.pushSuper:
stack.add(DynamicTypeImpl.instance);
break;
case UnlinkedExprOperation.pushThis:
stack.add(DynamicTypeImpl.instance);
break;
case UnlinkedExprOperation.pushReference:
try {
_doPushReference();
} on _InferenceFailedError {
errorKind = TopLevelInferenceErrorKind.instanceGetter;
return DynamicTypeImpl.instance;
}
break;
case UnlinkedExprOperation.extractProperty:
try {
_doExtractProperty();
} on _InferenceFailedError {
errorKind = TopLevelInferenceErrorKind.instanceGetter;
return DynamicTypeImpl.instance;
}
break;
case UnlinkedExprOperation.invokeConstructor:
_doInvokeConstructor();
break;
case UnlinkedExprOperation.makeUntypedList:
_doMakeUntypedList();
break;
case UnlinkedExprOperation.makeUntypedMap:
_doMakeUntypedMap();
break;
case UnlinkedExprOperation.makeTypedList:
_doMakeTypedList();
break;
case UnlinkedExprOperation.makeTypedMap:
_doMakeTypeMap();
break;
case UnlinkedExprOperation.not:
stack.length -= 1;
stack.add(typeProvider.boolType);
break;
case UnlinkedExprOperation.complement:
_computePrefixExpressionType('~');
break;
case UnlinkedExprOperation.negate:
_computePrefixExpressionType('unary-');
break;
case UnlinkedExprOperation.and:
case UnlinkedExprOperation.or:
case UnlinkedExprOperation.equal:
case UnlinkedExprOperation.notEqual:
stack.length -= 2;
stack.add(typeProvider.boolType);
break;
case UnlinkedExprOperation.bitXor:
_computeBinaryExpressionType(TokenType.CARET);
break;
case UnlinkedExprOperation.bitAnd:
_computeBinaryExpressionType(TokenType.AMPERSAND);
break;
case UnlinkedExprOperation.bitOr:
_computeBinaryExpressionType(TokenType.BAR);
break;
case UnlinkedExprOperation.bitShiftRight:
_computeBinaryExpressionType(TokenType.GT_GT);
break;
case UnlinkedExprOperation.bitShiftLeft:
_computeBinaryExpressionType(TokenType.LT_LT);
break;
case UnlinkedExprOperation.add:
_computeBinaryExpressionType(TokenType.PLUS);
break;
case UnlinkedExprOperation.subtract:
_computeBinaryExpressionType(TokenType.MINUS);
break;
case UnlinkedExprOperation.multiply:
_computeBinaryExpressionType(TokenType.STAR);
break;
case UnlinkedExprOperation.divide:
_computeBinaryExpressionType(TokenType.SLASH);
break;
case UnlinkedExprOperation.floorDivide:
_computeBinaryExpressionType(TokenType.TILDE_SLASH);
break;
case UnlinkedExprOperation.greater:
_computeBinaryExpressionType(TokenType.GT);
break;
case UnlinkedExprOperation.less:
_computeBinaryExpressionType(TokenType.LT);
break;
case UnlinkedExprOperation.greaterEqual:
_computeBinaryExpressionType(TokenType.GT_EQ);
break;
case UnlinkedExprOperation.lessEqual:
_computeBinaryExpressionType(TokenType.LT_EQ);
break;
case UnlinkedExprOperation.modulo:
_computeBinaryExpressionType(TokenType.PERCENT);
break;
case UnlinkedExprOperation.conditional:
_doConditional();
break;
case UnlinkedExprOperation.assignToIndex:
case UnlinkedExprOperation.assignToProperty:
case UnlinkedExprOperation.assignToRef:
errorKind = TopLevelInferenceErrorKind.assignment;
return DynamicTypeImpl.instance;
case UnlinkedExprOperation.await:
_doAwait();
break;
case UnlinkedExprOperation.extractIndex:
_doExtractIndex();
break;
case UnlinkedExprOperation.invokeMethodRef:
try {
_doInvokeMethodRef();
} on _InferenceFailedError {
errorKind = TopLevelInferenceErrorKind.instanceGetter;
return DynamicTypeImpl.instance;
}
break;
case UnlinkedExprOperation.invokeMethod:
_doInvokeMethod();
break;
case UnlinkedExprOperation.cascadeSectionBegin:
stack.add(stack.last);
break;
case UnlinkedExprOperation.cascadeSectionEnd:
stack.removeLast();
break;
case UnlinkedExprOperation.typeCast:
stack.removeLast();
DartType type = _getNextTypeRef();
stack.add(type);
break;
case UnlinkedExprOperation.typeCheck:
stack.removeLast();
refPtr++;
stack.add(typeProvider.boolType);
break;
case UnlinkedExprOperation.throwException:
stack.removeLast();
stack.add(BottomTypeImpl.instance);
break;
case UnlinkedExprOperation.pushLocalFunctionReference:
int popCount = _getNextInt();
assert(popCount == 0); // TODO(paulberry): handle the nonzero case.
stack.add(function.functions[_getNextInt()].type);
break;
case UnlinkedExprOperation.pushParameter:
stack.add(_findParameterType(_getNextString()));
break;
case UnlinkedExprOperation.ifNull:
_doIfNull();
break;
default:
// TODO(paulberry): implement.
throw new UnimplementedError('$operation');
}
}
assert(intPtr == unlinkedConst.ints.length);
assert(refPtr == unlinkedConst.references.length);
assert(strPtr == unlinkedConst.strings.length);
assert(assignmentOperatorPtr == unlinkedConst.assignmentOperators.length);
assert(stack.length == 1);
return stack[0];
}
void _computeBinaryExpressionType(TokenType operator) {
DartType right = stack.removeLast();
DartType left = stack.removeLast();
_pushBinaryOperatorType(left, operator, right);
}
void _computePrefixExpressionType(String operatorName) {
DartType operand = stack.removeLast();
if (operand is InterfaceType) {
MethodElement method =
operand.lookUpInheritedMethod(operatorName, library: library);
if (method != null) {
DartType type = method.returnType;
stack.add(type);
return;
}
}
stack.add(DynamicTypeImpl.instance);
}
void _doAwait() {
DartType type = stack.removeLast();
DartType typeArgument = type?.flattenFutures(linker.typeSystem);
typeArgument = _dynamicIfNull(typeArgument);
stack.add(typeArgument);
}
void _doConditional() {
DartType elseType = stack.removeLast();
DartType thenType = stack.removeLast();
stack.removeLast();
DartType type = _leastUpperBound(thenType, elseType);
type = _dynamicIfNull(type);
stack.add(type);
}
void _doExtractIndex() {
stack.removeLast(); // index
DartType target = stack.removeLast();
stack.add(() {
if (target is InterfaceType) {
MethodElement method =
target.lookUpInheritedMethod('[]', library: library);
if (method != null) {
return method.returnType;
}
}
return DynamicTypeImpl.instance;
}());
}
void _doExtractProperty() {
DartType target = stack.removeLast();
if (target.isDynamic) {
target = typeProvider.objectType;
}
String propertyName = _getNextString();
stack.add(() {
if (target is InterfaceType) {
ExecutableElement element = target
.lookUpInheritedGetterOrMethod(propertyName, library: library);
if (element != null) {
_throwIfInstanceFieldOrAccessor(element);
if (element is PropertyAccessorElement) {
return element.returnType;
} else {
// Method tear-off
return element.type;
}
}
}
return DynamicTypeImpl.instance;
}());
}
void _doIfNull() {
DartType secondType = stack.removeLast();
DartType firstType = stack.removeLast();
DartType type = _leastUpperBound(firstType, secondType);
type = _dynamicIfNull(type);
stack.add(type);
}
void _doInvokeConstructor() {
int numNamed = unlinkedConst.ints[intPtr++];
int numPositional = unlinkedConst.ints[intPtr++];
List<String> namedArgNames = _getNextStrings(numNamed);
List<DartType> namedArgTypeList = _popList(numNamed);
List<DartType> positionalArgTypes = _popList(numPositional);
EntityRef ref = _getNextRef();
ReferenceableElementForLink refElement = unit.resolveRef(ref.reference);
ConstructorElementForLink constructorElement = refElement.asConstructor;
if (constructorElement != null) {
stack.add(() {
if (ref.typeArguments.isNotEmpty) {
return constructorElement.enclosingClass.buildType((int i) {
if (i < ref.typeArguments.length) {
return unit.resolveTypeRef(function, ref.typeArguments[i]);
} else {
return null;
}
}, const <int>[]);
} else {
FunctionType rawType = StaticTypeAnalyzer
.constructorToGenericFunctionType(constructorElement);
FunctionType inferredType = _inferExecutableType(
rawType,
numNamed,
numPositional,
namedArgNames,
namedArgTypeList,
positionalArgTypes, const <DartType>[]);
if (inferredType == null || identical(inferredType, rawType)) {
inferredType = linker.typeSystem.instantiateToBounds(rawType);
}
return inferredType.returnType;
}
}());
} else {
ClassElementForLink classElement =
unit.resolveConstructorClassRef(ref.reference).asClass;
DartType inferredType;
if (classElement != null) {
InterfaceType rawType = classElement.type;
inferredType = linker.typeSystem.instantiateToBounds(rawType);
} else {
inferredType = DynamicTypeImpl.instance;
}
stack.add(inferredType);
}
}
void _doInvokeMethod() {
int numNamed = unlinkedConst.ints[intPtr++];
int numPositional = unlinkedConst.ints[intPtr++];
List<String> namedArgNames = _getNextStrings(numNamed);
List<DartType> namedArgTypeList = _popList(numNamed);
List<DartType> positionalArgTypes = _popList(numPositional);
// TODO(scheglov) if we pushed target and method name first, we might be
// able to move work with arguments in _inferExecutableType()
String methodName = _getNextString();
List<DartType> typeArguments = _getTypeArguments();
DartType target = stack.removeLast();
if (target.isDynamic) {
target = typeProvider.objectType;
}
stack.add(() {
if (target is InterfaceType) {
MethodElement method =
target.lookUpInheritedMethod(methodName, library: library);
FunctionType rawType = method?.type;
FunctionType inferredType = _inferExecutableType(
rawType,
numNamed,
numPositional,
namedArgNames,
namedArgTypeList,
positionalArgTypes,
typeArguments);
if (inferredType != null) {
return inferredType.returnType;
}
}
return DynamicTypeImpl.instance;
}());
}
void _doInvokeMethodRef() {
int numNamed = _getNextInt();
int numPositional = _getNextInt();
List<String> namedArgNames = _getNextStrings(numNamed);
List<DartType> namedArgTypeList = _popList(numNamed);
List<DartType> positionalArgTypes = _popList(numPositional);
EntityRef ref = _getNextRef();
ReferenceableElementForLink element = unit.resolveRef(ref.reference);
_throwIfInstanceFieldOrAccessor(element);
List<DartType> typeArguments = _getTypeArguments();
stack.add(() {
DartType rawType = element.asStaticType;
if (rawType is FunctionType) {
FunctionType inferredType = _inferExecutableType(
rawType,
numNamed,
numPositional,
namedArgNames,
namedArgTypeList,
positionalArgTypes,
typeArguments);
if (inferredType != null) {
return inferredType.returnType;
}
}
return DynamicTypeImpl.instance;
}());
}
void _doMakeTypedList() {
DartType itemType = _getNextTypeRef();
stack.length -= _getNextInt();
stack.add(typeProvider.listType.instantiate(<DartType>[itemType]));
}
void _doMakeTypeMap() {
DartType keyType = _getNextTypeRef();
DartType valueType = _getNextTypeRef();
stack.length -= 2 * _getNextInt();
stack.add(typeProvider.mapType.instantiate(<DartType>[keyType, valueType]));
}
void _doMakeUntypedList() {
int numItems = _getNextInt();
DartType itemType = numItems == 0
? DynamicTypeImpl.instance
: _popList(numItems).reduce(_leastUpperBound);
itemType = _dynamicIfNull(itemType);
stack.add(typeProvider.listType.instantiate(<DartType>[itemType]));
}
void _doMakeUntypedMap() {
int numEntries = _getNextInt();
List<DartType> keysValues = _popList(2 * numEntries);
DartType keyType = null;
DartType valueType = null;
for (int i = 0; i < 2 * numEntries; i++) {
DartType type = keysValues[i];
if (i.isEven) {
keyType = keyType == null ? type : _leastUpperBound(keyType, type);
} else {
valueType =
valueType == null ? type : _leastUpperBound(valueType, type);
}
}
keyType = _dynamicIfNull(keyType);
valueType = _dynamicIfNull(valueType);
stack.add(typeProvider.mapType.instantiate(<DartType>[keyType, valueType]));
}
void _doPushReference() {
EntityRef ref = _getNextRef();
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 = unit.resolveRef(ref.reference);
_throwIfInstanceFieldOrAccessor(element);
stack.add(element.asStaticType);
}
}
/**
* Find the parameter in scope called [parameterName] and return its type.
*/
DartType _findParameterType(String parameterName) {
FunctionElementForLink_Local f = this.function;
while (true) {
for (ParameterElement parameter in f.parameters) {
if (parameter.name == parameterName) {
return parameter.type;
}
}
Element parent = f.enclosingElement;
if (parent is FunctionElementForLink_Local) {
f = parent;
} else {
// Parameter not found. This should never happen in a well-formed
// summary.
assert(false);
return DynamicTypeImpl.instance;
}
}
}
int _getNextInt() {
return unlinkedConst.ints[intPtr++];
}
EntityRef _getNextRef() => unlinkedConst.references[refPtr++];
String _getNextString() {
return unlinkedConst.strings[strPtr++];
}
List<String> _getNextStrings(int n) {
List<String> result = new List<String>(n);
for (int i = 0; i < n; i++) {
result[i] = _getNextString();
}
return result;
}
DartType _getNextTypeRef() {
EntityRef ref = _getNextRef();
return unit.resolveTypeRef(function, ref);
}
List<DartType> _getTypeArguments() {
int numTypeArguments = _getNextInt();
List<DartType> typeArguments = new List<DartType>(numTypeArguments);
for (int i = 0; i < numTypeArguments; i++) {
typeArguments[i] = _getNextTypeRef();
}
return typeArguments;
}
FunctionType _inferExecutableType(
FunctionType rawMethodType,
int numNamedArguments,
int numPositionalArguments,
List<String> namedArgNames,
List<DartType> namedArgTypeList,
List<DartType> positionalArgTypes,
List<DartType> typeArguments) {
TypeSystem ts = linker.typeSystem;
if (rawMethodType != null) {
if (rawMethodType.typeFormals.isNotEmpty && typeArguments.isNotEmpty) {
Element methodElement = rawMethodType.element;
if (methodElement is TypeParameterizedElement &&
methodElement.typeParameters.length == typeArguments.length) {
return rawMethodType.instantiate(typeArguments);
}
} else if (rawMethodType.typeFormals.isNotEmpty &&
ts is StrongTypeSystemImpl) {
// Prepare the named argument types map.
Map<String, DartType> namedArgTypes = <String, DartType>{};
for (int i = 0; i < numNamedArguments; i++) {
String name = namedArgNames[i];
DartType type = namedArgTypeList[i];
namedArgTypes[name] = type;
}
// Fill parameters and the corresponding arguments.
List<ParameterElement> parameters = <ParameterElement>[];
List<DartType> argumentTypes = <DartType>[];
int positionalIndex = 0;
int numRequiredParameters = 0;
for (ParameterElement parameter in rawMethodType.parameters) {
if (parameter.parameterKind == ParameterKind.REQUIRED) {
numRequiredParameters++;
if (numRequiredParameters > numPositionalArguments) {
return null;
}
parameters.add(parameter);
argumentTypes.add(positionalArgTypes[positionalIndex]);
positionalIndex++;
} else if (parameter.parameterKind == ParameterKind.POSITIONAL) {
if (positionalIndex < numPositionalArguments) {
parameters.add(parameter);
argumentTypes.add(positionalArgTypes[positionalIndex]);
positionalIndex++;
}
} else if (parameter.parameterKind == ParameterKind.NAMED) {
DartType namedArgumentType = namedArgTypes[parameter.name];
if (namedArgumentType != null) {
parameters.add(parameter);
argumentTypes.add(namedArgumentType);
}
}
}
// Perform inference.
FunctionType inferred = ts.inferGenericFunctionOrType(
rawMethodType, parameters, argumentTypes, null);
return inferred;
}
}
// Not a generic function type, use the raw type.
return rawMethodType;
}
DartType _leastUpperBound(DartType s, DartType t) {
return linker.typeSystem.getLeastUpperBound(s, t);
}
List<DartType> _popList(int n) {
List<DartType> result = stack.sublist(stack.length - n, stack.length);
stack.length -= n;
return result;
}
void _pushBinaryOperatorType(
DartType left, TokenType operator, DartType right) {
if (left is InterfaceType) {
MethodElement method =
left.lookUpInheritedMethod(operator.lexeme, library: library);
if (method != null) {
DartType type = method.returnType;
type = linker.typeSystem
.refineBinaryExpressionType(left, operator, right, type);
stack.add(type);
return;
}
}
stack.add(DynamicTypeImpl.instance);
}
void _throwIfInstanceFieldOrAccessor(Object element) {
if (element is NonstaticMemberElementForLink &&
element.hasInstanceGetterReference ||
element is FieldElement && !element.isStatic ||
element is PropertyAccessorElement && !element.isStatic) {
throw new _InferenceFailedError(
'Instance fields cannot be used for type inference.');
}
}
}
/**
* Element representing a field resynthesized from a summary during
* linking.
*/
abstract class FieldElementForLink implements FieldElement {
@override
PropertyAccessorElementForLink get getter;
@override
PropertyAccessorElementForLink get setter;
}
/**
* 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] (if any). Otherwise `null`.
*/
DartType _inferredInstanceType;
TopLevelInferenceErrorBuilder _inferenceError;
FieldElementForLink_ClassField(ClassElementForLink_Class enclosingElement,
UnlinkedVariable unlinkedVariable)
: enclosingElement = enclosingElement,
super(unlinkedVariable, enclosingElement.enclosingElement);
@override
bool get isStatic => unlinkedVariable.isStatic;
@override
void set type(DartType inferredType) {
assert(!isStatic);
assert(_inferredInstanceType == null);
_inferredInstanceType = inferredType;
}
@override
TypeParameterizedElementMixin get _typeParameterContext => enclosingElement;
/**
* Store the results of type inference for this field in
* [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
if (hasImplicitType) {
compilationUnit._storeLinkedType(
unlinkedVariable.inferredTypeSlot,
isStatic ? inferredType : _inferredInstanceType,
_typeParameterContext);
compilationUnit._storeLinkedTypeError(
unlinkedVariable.inferredTypeSlot, _inferenceError);
if (initializer != null) {
compilationUnit._storeLinkedTypeError(
unlinkedVariable.inferredTypeSlot, initializer._inferenceError);
initializer.link(compilationUnit);
}
}
}
void setInferenceError(TopLevelInferenceErrorBuilder error) {
assert(_inferenceError == null);
_inferenceError = error;
}
@override
String toString() => '$enclosingElement.$name';
}
/**
* Specialization of [FieldElementForLink] for enum fields.
*/
class FieldElementForLink_EnumField extends FieldElementForLink
implements FieldElement {
PropertyAccessorElementForLink_EnumField _getter;
@override
final ClassElementForLink_Enum enclosingElement;
FieldElementForLink_EnumField(this.enclosingElement);
@override
PropertyAccessorElementForLink_EnumField get getter =>
_getter ??= new PropertyAccessorElementForLink_EnumField(this);
@override
bool get isSynthetic => false;
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
String toString() => '$enclosingElement.$name';
}
/**
* Specialization of [FieldElementForLink] for the 'index' enum field.
*/
class FieldElementForLink_EnumField_index
extends FieldElementForLink_EnumField {
FieldElementForLink_EnumField_index(ClassElementForLink_Enum enclosingElement)
: super(enclosingElement);
@override
bool get isStatic => false;
@override
String get name => 'index';
@override
DartType get type =>
enclosingElement.enclosingElement.library._linker.typeProvider.intType;
}
/**
* Specialization of [FieldElementForLink] for enum fields.
*/
class FieldElementForLink_EnumField_value
extends FieldElementForLink_EnumField {
/**
* The unlinked representation of the field in the summary.
*/
final UnlinkedEnumValue unlinkedEnumValue;
FieldElementForLink_EnumField_value(
ClassElementForLink_Enum enclosingElement, this.unlinkedEnumValue)
: super(enclosingElement);
@override
bool get isStatic => true;
@override
String get name => unlinkedEnumValue.name;
@override
DartType get type => enclosingElement.type;
}
/**
* Specialization of [FieldElementForLink] for the 'values' enum field.
*/
class FieldElementForLink_EnumField_values
extends FieldElementForLink_EnumField {
FieldElementForLink_EnumField_values(
ClassElementForLink_Enum enclosingElement)
: super(enclosingElement);
@override
bool get isStatic => true;
@override
String get name => 'values';
@override
DartType get type => enclosingElement.valuesType;
}
class FieldFormalParameterElementForLink extends ParameterElementForLink
implements FieldFormalParameterElement {
FieldElement _field;
DartType _type;
FieldFormalParameterElementForLink(
ParameterParentElementForLink enclosingElement,
UnlinkedParam unlinkedParam,
TypeParameterizedElementMixin typeParameterContext,
CompilationUnitElementForLink compilationUnit,
int parameterIndex)
: super(enclosingElement, unlinkedParam, typeParameterContext,
compilationUnit, parameterIndex);
@override
FieldElement get field {
if (_field == null) {
Element enclosingConstructor = enclosingElement;
if (enclosingConstructor is ConstructorElement) {
Element enclosingClass = enclosingConstructor.enclosingElement;
if (enclosingClass is ClassElement) {
FieldElement field = enclosingClass.getField(_unlinkedParam.name);
if (field != null && !field.isSynthetic) {
_field = field;
}
}
}
}
return _field;
}
@override
bool get isInitializingFormal => true;
@override
DartType get type {
return _type ??= field?.type ?? DynamicTypeImpl.instance;
}
}
/**
* Element representing a function-typed parameter resynthesied from a summary
* during linking.
*/
class FunctionElementForLink_FunctionTypedParam extends Object
with ParameterParentElementForLink
implements FunctionElement {
@override
final ParameterElementForLink enclosingElement;
@override
final TypeParameterizedElementMixin typeParameterContext;
@override
final List<UnlinkedParam> unlinkedParameters;
DartType _returnType;
List<int> _implicitFunctionTypeIndices;
FunctionElementForLink_FunctionTypedParam(this.enclosingElement,
this.typeParameterContext, this.unlinkedParameters);
@override
List<int> get implicitFunctionTypeIndices {
if (_implicitFunctionTypeIndices == null) {
_implicitFunctionTypeIndices = enclosingElement
.enclosingElement.implicitFunctionTypeIndices
.toList();
_implicitFunctionTypeIndices.add(enclosingElement._parameterIndex);
}
return _implicitFunctionTypeIndices;
}
@override
DartType get returnType {
if (_returnType == null) {
if (enclosingElement._unlinkedParam.type == null) {
_returnType = DynamicTypeImpl.instance;
} else {
_returnType = enclosingElement.compilationUnit.resolveTypeRef(
enclosingElement, enclosingElement._unlinkedParam.type);
}
}
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 extends Object
with ReferenceableElementForLink, TypeParameterizedElementMixin
implements FunctionElementForLink_Local {
/**
* The variable for which this element is the initializer.
*/
final VariableElementForLink _variable;
/**
* The type inference node for this function, or `null` if it hasn't been
* computed yet.
*/
TypeInferenceNode _typeInferenceNode;
List<FunctionElementForLink_Local_NonSynthetic> _functions;
DartType _inferredReturnType;
TopLevelInferenceErrorBuilder _inferenceError;
FunctionElementForLink_Initializer(this._variable);
@override
TypeInferenceNode get asTypeInferenceNode =>
_typeInferenceNode ??= new TypeInferenceNode(this);
@override
CompilationUnitElementForLink get compilationUnit =>
_variable.compilationUnit;
@override
VariableElementForLink get enclosingElement => _variable;
TypeParameterizedElementMixin get enclosingTypeParameterContext =>
_variable.enclosingElement is ClassElementForLink
? _variable.enclosingElement
: null;
@override
CompilationUnitElementForLink get enclosingUnit => _variable.compilationUnit;
@override
List<FunctionElementForLink_Local_NonSynthetic> get functions =>
_functions ??= _variable.unlinkedVariable.initializer.localFunctions
.map((UnlinkedExecutable ex) =>
new FunctionElementForLink_Local_NonSynthetic(
_variable.compilationUnit, this, ex))
.toList();
@override
String get identifier => '';
@override
bool get isAsynchronous => _unlinkedExecutable.isAsynchronous;
@override
DartType get returnType {
// If this is a variable whose type needs inferring, infer it.
if (_variable.hasImplicitType) {
return _variable.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) {
// InstanceMemberInferrer stores the new type both here and on the variable
// element. We don't need to record both values, so we ignore it here.
}
@override
TypeParameterizedElementMixin get typeParameterContext => this;
@override
List<UnlinkedTypeParam> get unlinkedTypeParams => const [];
@override
bool get _hasTypeBeenInferred => _inferredReturnType != null;
@override
UnlinkedExecutable get _unlinkedExecutable =>
_variable.unlinkedVariable.initializer;
@override
FunctionElementForLink_Local getLocalFunction(int index) {
List<FunctionElementForLink_Local_NonSynthetic> functions = this.functions;
return index < functions.length ? functions[index] : null;
}
/**
* Store the results of type inference for this initializer in
* [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
compilationUnit._storeLinkedType(_unlinkedExecutable.inferredReturnTypeSlot,
_inferredReturnType, typeParameterContext);
for (FunctionElementForLink_Local_NonSynthetic function in functions) {
function.link(compilationUnit);
}
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
void _setInferenceError(TopLevelInferenceErrorBuilder error) {
assert(!_hasTypeBeenInferred);
_inferenceError = error;
}
@override
void _setInferredType(DartType type) {
assert(!_hasTypeBeenInferred);
_inferredReturnType = type;
_variable._inferredType = _dynamicIfNull(type);
}
}
/**
* Element representing a local function (possibly a closure).
*/
abstract class FunctionElementForLink_Local
implements
ExecutableElementForLink,
FunctionElementImpl,
ReferenceableElementForLink {
/**
* Indicates whether type inference has completed for this function.
*/
bool get _hasTypeBeenInferred;
/**
* Stores the given [error] as the type inference error for this function.
* Should only be called if [_hasTypeBeenInferred] is `false`.
*/
void _setInferenceError(TopLevelInferenceErrorBuilder error);
/**
* Stores the given [type] as the inferred return type for this function.
* Should only be called if [_hasTypeBeenInferred] is `false`.
*/
void _setInferredType(DartType type);
}
/**
* Element representing a local function (possibly a closure) inside another
* executable.
*/
class FunctionElementForLink_Local_NonSynthetic extends ExecutableElementForLink
with ReferenceableElementForLink
implements FunctionElementForLink_Local {
@override
final ExecutableElementForLink enclosingElement;
List<FunctionElementForLink_Local_NonSynthetic> _functions;
/**
* The type inference node for this function, or `null` if it hasn't been
* computed yet.
*/
TypeInferenceNode _typeInferenceNode;
FunctionElementForLink_Local_NonSynthetic(
CompilationUnitElementForLink compilationUnit,
this.enclosingElement,
UnlinkedExecutable unlinkedExecutable)
: super(compilationUnit, unlinkedExecutable);
@override
TypeInferenceNode get asTypeInferenceNode =>
_typeInferenceNode ??= new TypeInferenceNode(this);
@override
TypeParameterizedElementMixin get enclosingTypeParameterContext =>
enclosingElement;
@override
List<FunctionElementForLink_Local_NonSynthetic> get functions =>
_functions ??= _unlinkedExecutable.localFunctions
.map((UnlinkedExecutable ex) =>
new FunctionElementForLink_Local_NonSynthetic(
compilationUnit, this, ex))
.toList();
@override
String get identifier {
String identifier = _unlinkedExecutable.name;
Element enclosing = this.enclosingElement;
if (enclosing is ExecutableElement) {
int id =
ElementImpl.findElementIndexUsingIdentical(enclosing.functions, this);
identifier += "@$id";
}
return identifier;
}
@override
bool get isAsynchronous => _unlinkedExecutable.isAsynchronous;
@override
bool get _hasTypeBeenInferred => _inferredReturnType != null;
@override
DartType buildType(
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
assert(implicitFunctionTypeIndices.isEmpty);
return type;
}
@override
FunctionElementForLink_Local getLocalFunction(int index) {
List<FunctionElementForLink_Local_NonSynthetic> functions = this.functions;
return index < functions.length ? functions[index] : null;
}
/**
* Store the results of type inference for this function in [compilationUnit].
*/
void link(CompilationUnitElementInBuildUnit compilationUnit) {
if (_unlinkedExecutable.returnType == null) {
compilationUnit._storeLinkedType(
_unlinkedExecutable.inferredReturnTypeSlot, inferredReturnType, this);
}
for (FunctionElementForLink_Local_NonSynthetic function in functions) {
function.link(compilationUnit);
}
}
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
void _setInferenceError(TopLevelInferenceErrorBuilder error) {}
@override
void _setInferredType(DartType type) {
// TODO(paulberry): store the inferred return type in the summary.
assert(!_hasTypeBeenInferred);
_inferredReturnType = _dynamicIfNull(type);