blob: 214861a7a3821c53366f1d77111ad5f2bcc357c2 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library summary_resynthesizer;
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/constant.dart';
import 'package:analyzer/src/generated/element_handle.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/generated/testing/ast_factory.dart';
import 'package:analyzer/src/generated/testing/token_factory.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary/idl.dart';
/**
* Implementation of [ElementResynthesizer] used when resynthesizing an element
* model from summaries.
*/
abstract class SummaryResynthesizer extends ElementResynthesizer {
/**
* The parent [SummaryResynthesizer] which is asked to resynthesize elements
* and get summaries before this resynthesizer attempts to do this.
* Can be `null`.
*/
final SummaryResynthesizer parent;
/**
* Source factory used to convert URIs to [Source] objects.
*/
final SourceFactory sourceFactory;
/**
* Cache of [Source] objects that have already been converted from URIs.
*/
final Map<String, Source> _sources = <String, Source>{};
/**
* The [TypeProvider] used to obtain core types (such as Object, int, List,
* and dynamic) during resynthesis.
*/
final TypeProvider typeProvider;
/**
* Indicates whether the summary should be resynthesized assuming strong mode
* semantics.
*/
final bool strongMode;
/**
* Map of compilation units resynthesized from summaries. The two map keys
* are the first two elements of the element's location (the library URI and
* the compilation unit URI).
*/
final Map<String, Map<String, CompilationUnitElement>> _resynthesizedUnits =
<String, Map<String, CompilationUnitElement>>{};
/**
* Map of top level elements resynthesized from summaries. The three map
* keys are the first three elements of the element's location (the library
* URI, the compilation unit URI, and the name of the top level declaration).
*/
final Map<String, Map<String, Map<String, Element>>> _resynthesizedElements =
<String, Map<String, Map<String, Element>>>{};
/**
* Map of libraries which have been resynthesized from summaries. The map
* key is the library URI.
*/
final Map<String, LibraryElement> _resynthesizedLibraries =
<String, LibraryElement>{};
SummaryResynthesizer(this.parent, AnalysisContext context, this.typeProvider,
this.sourceFactory, this.strongMode)
: super(context);
/**
* Number of libraries that have been resynthesized so far.
*/
int get resynthesisCount => _resynthesizedLibraries.length;
/**
* Perform delayed finalization of the `dart:core` and `dart:async` libraries.
*/
void finalizeCoreAsyncLibraries() {
(_resynthesizedLibraries['dart:core'] as LibraryElementImpl)
.createLoadLibraryFunction(typeProvider);
(_resynthesizedLibraries['dart:async'] as LibraryElementImpl)
.createLoadLibraryFunction(typeProvider);
}
@override
Element getElement(ElementLocation location) {
List<String> components = location.components;
String libraryUri = components[0];
// Ask the parent resynthesizer.
if (parent != null && parent._hasLibrarySummary(libraryUri)) {
return parent.getElement(location);
}
// Resynthesize locally.
if (components.length == 1) {
return getLibraryElement(libraryUri);
} else if (components.length == 2) {
Map<String, CompilationUnitElement> libraryMap =
_resynthesizedUnits[libraryUri];
if (libraryMap == null) {
getLibraryElement(libraryUri);
libraryMap = _resynthesizedUnits[libraryUri];
assert(libraryMap != null);
}
String unitUri = components[1];
CompilationUnitElement element = libraryMap[unitUri];
if (element == null) {
throw new Exception('Unit element not found in summary: $location');
}
return element;
} else if (components.length == 3 || components.length == 4) {
Map<String, Map<String, Element>> libraryMap =
_resynthesizedElements[libraryUri];
if (libraryMap == null) {
getLibraryElement(libraryUri);
libraryMap = _resynthesizedElements[libraryUri];
assert(libraryMap != null);
}
Map<String, Element> compilationUnitElements = libraryMap[components[1]];
Element element;
if (compilationUnitElements != null) {
element = compilationUnitElements[components[2]];
}
if (element != null && components.length == 4) {
String name = components[3];
Element parentElement = element;
if (parentElement is ClassElement) {
if (name.endsWith('?')) {
element =
parentElement.getGetter(name.substring(0, name.length - 1));
} else if (name.endsWith('=')) {
element =
parentElement.getSetter(name.substring(0, name.length - 1));
} else if (name.isEmpty) {
element = parentElement.unnamedConstructor;
} else {
element = parentElement.getField(name) ??
parentElement.getMethod(name) ??
parentElement.getNamedConstructor(name);
}
} else {
// The only elements that are currently retrieved using 4-component
// locations are class members.
throw new StateError(
'4-element locations not supported for ${element.runtimeType}');
}
}
if (element == null) {
throw new Exception('Element not found in summary: $location');
}
return element;
} else {
throw new UnimplementedError(location.toString());
}
}
/**
* Get the [LibraryElement] for the given [uri], resynthesizing it if it
* hasn't been resynthesized already.
*/
LibraryElement getLibraryElement(String uri) {
if (parent != null && parent._hasLibrarySummary(uri)) {
return parent.getLibraryElement(uri);
}
return _resynthesizedLibraries.putIfAbsent(uri, () {
LinkedLibrary serializedLibrary = _getLinkedSummaryOrThrow(uri);
List<UnlinkedUnit> serializedUnits = <UnlinkedUnit>[
_getUnlinkedSummaryOrThrow(uri)
];
Source librarySource = _getSource(uri);
for (String part in serializedUnits[0].publicNamespace.parts) {
Source partSource = sourceFactory.resolveUri(librarySource, part);
String partAbsUri = partSource.uri.toString();
serializedUnits.add(_getUnlinkedSummaryOrThrow(partAbsUri));
}
_LibraryResynthesizer libraryResynthesizer = new _LibraryResynthesizer(
this, serializedLibrary, serializedUnits, librarySource);
LibraryElement library = libraryResynthesizer.buildLibrary();
_resynthesizedUnits[uri] = libraryResynthesizer.resynthesizedUnits;
_resynthesizedElements[uri] = libraryResynthesizer.resynthesizedElements;
return library;
});
}
/**
* Return the [LinkedLibrary] for the given [uri] or `null` if it could not
* be found. Caller has already checked that `parent.hasLibrarySummary(uri)`
* returns `false`.
*/
LinkedLibrary getLinkedSummary(String uri);
/**
* Return the [UnlinkedUnit] for the given [uri] or `null` if it could not
* be found. Caller has already checked that `parent.hasLibrarySummary(uri)`
* returns `false`.
*/
UnlinkedUnit getUnlinkedSummary(String uri);
/**
* Return `true` if this resynthesizer can provide summaries of the libraries
* with the given [uri]. Caller has already checked that
* `parent.hasLibrarySummary(uri)` returns `false`.
*/
bool hasLibrarySummary(String uri);
/**
* Return the [LinkedLibrary] for the given [uri] or throw [StateError] if it
* could not be found.
*/
LinkedLibrary _getLinkedSummaryOrThrow(String uri) {
if (parent != null && parent._hasLibrarySummary(uri)) {
return parent._getLinkedSummaryOrThrow(uri);
}
LinkedLibrary summary = getLinkedSummary(uri);
if (summary != null) {
return summary;
}
throw new StateError('Unable to find linked summary: $uri');
}
/**
* Get the [Source] object for the given [uri].
*/
Source _getSource(String uri) {
return _sources.putIfAbsent(uri, () => sourceFactory.forUri(uri));
}
/**
* Return the [UnlinkedUnit] for the given [uri] or throw [StateError] if it
* could not be found.
*/
UnlinkedUnit _getUnlinkedSummaryOrThrow(String uri) {
if (parent != null && parent._hasLibrarySummary(uri)) {
return parent._getUnlinkedSummaryOrThrow(uri);
}
UnlinkedUnit summary = getUnlinkedSummary(uri);
if (summary != null) {
return summary;
}
throw new StateError('Unable to find unlinked summary: $uri');
}
/**
* Return `true` if this resynthesizer can provide summaries of the libraries
* with the given [uri].
*/
bool _hasLibrarySummary(String uri) {
if (parent != null && parent._hasLibrarySummary(uri)) {
return true;
}
return hasLibrarySummary(uri);
}
}
/**
* Builder of [Expression]s from [UnlinkedConst]s.
*/
class _ConstExprBuilder {
final _LibraryResynthesizer resynthesizer;
final UnlinkedConst uc;
int intPtr = 0;
int doublePtr = 0;
int stringPtr = 0;
int refPtr = 0;
final List<Expression> stack = <Expression>[];
_ConstExprBuilder(this.resynthesizer, this.uc);
Expression get expr => stack.single;
Expression build() {
if (uc.isInvalid) {
return AstFactory.identifier3(r'$$invalidConstExpr$$');
}
for (UnlinkedConstOperation operation in uc.operations) {
switch (operation) {
case UnlinkedConstOperation.pushNull:
_push(AstFactory.nullLiteral());
break;
// bool
case UnlinkedConstOperation.pushFalse:
_push(AstFactory.booleanLiteral(false));
break;
case UnlinkedConstOperation.pushTrue:
_push(AstFactory.booleanLiteral(true));
break;
// literals
case UnlinkedConstOperation.pushInt:
int value = uc.ints[intPtr++];
_push(AstFactory.integer(value));
break;
case UnlinkedConstOperation.pushLongInt:
int value = 0;
int count = uc.ints[intPtr++];
for (int i = 0; i < count; i++) {
int next = uc.ints[intPtr++];
value = value << 32 | next;
}
_push(AstFactory.integer(value));
break;
case UnlinkedConstOperation.pushDouble:
double value = uc.doubles[doublePtr++];
_push(AstFactory.doubleLiteral(value));
break;
case UnlinkedConstOperation.makeSymbol:
String component = uc.strings[stringPtr++];
_push(AstFactory.symbolLiteral([component]));
break;
// String
case UnlinkedConstOperation.pushString:
String value = uc.strings[stringPtr++];
_push(AstFactory.string2(value));
break;
case UnlinkedConstOperation.concatenate:
int count = uc.ints[intPtr++];
List<InterpolationElement> elements = <InterpolationElement>[];
for (int i = 0; i < count; i++) {
Expression expr = _pop();
InterpolationElement element = _newInterpolationElement(expr);
elements.insert(0, element);
}
_push(AstFactory.string(elements));
break;
// binary
case UnlinkedConstOperation.equal:
_pushBinary(TokenType.EQ_EQ);
break;
case UnlinkedConstOperation.notEqual:
_pushBinary(TokenType.BANG_EQ);
break;
case UnlinkedConstOperation.and:
_pushBinary(TokenType.AMPERSAND_AMPERSAND);
break;
case UnlinkedConstOperation.or:
_pushBinary(TokenType.BAR_BAR);
break;
case UnlinkedConstOperation.bitXor:
_pushBinary(TokenType.CARET);
break;
case UnlinkedConstOperation.bitAnd:
_pushBinary(TokenType.AMPERSAND);
break;
case UnlinkedConstOperation.bitOr:
_pushBinary(TokenType.BAR);
break;
case UnlinkedConstOperation.bitShiftLeft:
_pushBinary(TokenType.LT_LT);
break;
case UnlinkedConstOperation.bitShiftRight:
_pushBinary(TokenType.GT_GT);
break;
case UnlinkedConstOperation.add:
_pushBinary(TokenType.PLUS);
break;
case UnlinkedConstOperation.subtract:
_pushBinary(TokenType.MINUS);
break;
case UnlinkedConstOperation.multiply:
_pushBinary(TokenType.STAR);
break;
case UnlinkedConstOperation.divide:
_pushBinary(TokenType.SLASH);
break;
case UnlinkedConstOperation.floorDivide:
_pushBinary(TokenType.TILDE_SLASH);
break;
case UnlinkedConstOperation.modulo:
_pushBinary(TokenType.PERCENT);
break;
case UnlinkedConstOperation.greater:
_pushBinary(TokenType.GT);
break;
case UnlinkedConstOperation.greaterEqual:
_pushBinary(TokenType.GT_EQ);
break;
case UnlinkedConstOperation.less:
_pushBinary(TokenType.LT);
break;
case UnlinkedConstOperation.lessEqual:
_pushBinary(TokenType.LT_EQ);
break;
// prefix
case UnlinkedConstOperation.complement:
_pushPrefix(TokenType.TILDE);
break;
case UnlinkedConstOperation.negate:
_pushPrefix(TokenType.MINUS);
break;
case UnlinkedConstOperation.not:
_pushPrefix(TokenType.BANG);
break;
// conditional
case UnlinkedConstOperation.conditional:
Expression elseExpr = _pop();
Expression thenExpr = _pop();
Expression condition = _pop();
_push(
AstFactory.conditionalExpression(condition, thenExpr, elseExpr));
break;
// identical
case UnlinkedConstOperation.identical:
Expression second = _pop();
Expression first = _pop();
_push(AstFactory.methodInvocation(
null, 'identical', <Expression>[first, second]));
break;
// containers
case UnlinkedConstOperation.makeUntypedList:
_pushList(null);
break;
case UnlinkedConstOperation.makeTypedList:
TypeName itemType = _newTypeName();
_pushList(AstFactory.typeArgumentList(<TypeName>[itemType]));
break;
case UnlinkedConstOperation.makeUntypedMap:
_pushMap(null);
break;
case UnlinkedConstOperation.makeTypedMap:
TypeName keyType = _newTypeName();
TypeName valueType = _newTypeName();
_pushMap(AstFactory.typeArgumentList(<TypeName>[keyType, valueType]));
break;
case UnlinkedConstOperation.pushReference:
EntityRef ref = uc.references[refPtr++];
_ReferenceInfo info = resynthesizer.referenceInfos[ref.reference];
if (info.enclosing != null &&
info.enclosing.element != null &&
info.enclosing.element is! ClassElement) {
SimpleIdentifier prefix = AstFactory.identifier3(
info.enclosing.name)..staticElement = info.enclosing.element;
SimpleIdentifier name = AstFactory.identifier3(info.name)
..staticElement = info.element;
PrefixedIdentifier node = AstFactory.identifier(prefix, name);
_push(node);
} else {
SimpleIdentifier node = AstFactory.identifier3(info.name);
node.staticElement = info.element;
_push(node);
}
break;
case UnlinkedConstOperation.invokeConstructor:
_pushInstanceCreation();
break;
case UnlinkedConstOperation.length:
Expression target = _pop();
SimpleIdentifier property = AstFactory.identifier3('length');
property.staticElement =
resynthesizer._buildStringLengthPropertyAccessorElement();
_push(AstFactory.propertyAccess(target, property));
break;
case UnlinkedConstOperation.pushConstructorParameter:
String name = uc.strings[stringPtr++];
SimpleIdentifier identifier = AstFactory.identifier3(name);
identifier.staticElement = resynthesizer.currentConstructor.parameters
.firstWhere((parameter) => parameter.name == name,
orElse: () => throw new StateError(
'Unable to resolve constructor parameter: $name'));
_push(identifier);
break;
}
}
return stack.single;
}
TypeName _buildTypeAst(DartType type) {
if (type is DynamicTypeImpl) {
TypeName node = AstFactory.typeName4('dynamic');
node.type = type;
(node.name as SimpleIdentifier).staticElement = type.element;
return node;
} else if (type is InterfaceType) {
List<DartType> typeArguments = type.typeArguments;
List<TypeName> argumentNodes = typeArguments.every((a) => a.isDynamic)
? null
: typeArguments.map(_buildTypeAst).toList();
TypeName node = AstFactory.typeName4(type.name, argumentNodes);
node.type = type;
(node.name as SimpleIdentifier).staticElement = type.element;
return node;
}
throw new StateError('Unsupported type $type');
}
InterpolationElement _newInterpolationElement(Expression expr) {
if (expr is SimpleStringLiteral) {
return new InterpolationString(expr.literal, expr.value);
} else {
return new InterpolationExpression(
TokenFactory.tokenFromType(TokenType.STRING_INTERPOLATION_EXPRESSION),
expr,
TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
}
}
/**
* Convert the next reference to the [DartType] and return the AST
* corresponding to this type.
*/
TypeName _newTypeName() {
EntityRef typeRef = uc.references[refPtr++];
DartType type = resynthesizer.buildType(typeRef);
return _buildTypeAst(type);
}
Expression _pop() => stack.removeLast();
void _push(Expression expr) {
stack.add(expr);
}
void _pushBinary(TokenType operator) {
Expression right = _pop();
Expression left = _pop();
_push(AstFactory.binaryExpression(left, operator, right));
}
void _pushInstanceCreation() {
EntityRef ref = uc.references[refPtr++];
_ReferenceInfo info = resynthesizer.referenceInfos[ref.reference];
// prepare ConstructorElement
TypeName typeNode;
String constructorName;
ConstructorElement constructorElement;
if (info.element != null) {
if (info.element is ConstructorElement) {
constructorName = info.name;
} else if (info.element is ClassElement) {
constructorName = null;
} else {
throw new StateError('Unsupported element for invokeConstructor '
'${info.element?.runtimeType}');
}
InterfaceType definingType =
resynthesizer._createConstructorDefiningType(info, ref.typeArguments);
constructorElement =
resynthesizer._createConstructorElement(definingType, info);
typeNode = _buildTypeAst(definingType);
} else {
if (info.enclosing != null) {
if (info.enclosing.enclosing != null) {
PrefixedIdentifier typeName = AstFactory.identifier5(
info.enclosing.enclosing.name, info.enclosing.name);
typeName.prefix.staticElement = info.enclosing.enclosing.element;
typeName.identifier.staticElement = info.enclosing.element;
typeName.identifier.staticType = info.enclosing.type;
typeNode = AstFactory.typeName3(typeName);
typeNode.type = info.enclosing.type;
constructorName = info.name;
} else if (info.enclosing.element != null) {
SimpleIdentifier typeName =
AstFactory.identifier3(info.enclosing.name);
typeName.staticElement = info.enclosing.element;
typeName.staticType = info.enclosing.type;
typeNode = AstFactory.typeName3(typeName);
typeNode.type = info.enclosing.type;
constructorName = info.name;
} else {
typeNode = AstFactory.typeName3(
AstFactory.identifier5(info.enclosing.name, info.name));
constructorName = null;
}
} else {
typeNode = AstFactory.typeName4(info.name);
}
}
// prepare arguments
List<Expression> arguments;
{
int numNamedArgs = uc.ints[intPtr++];
int numPositionalArgs = uc.ints[intPtr++];
int numArgs = numNamedArgs + numPositionalArgs;
arguments = _removeTopItems(numArgs);
// add names to the named arguments
for (int i = 0; i < numNamedArgs; i++) {
String name = uc.strings[stringPtr++];
int index = numPositionalArgs + i;
arguments[index] = AstFactory.namedExpression2(name, arguments[index]);
}
}
// create ConstructorName
ConstructorName constructorNode;
if (constructorName != null) {
constructorNode = AstFactory.constructorName(typeNode, constructorName);
constructorNode.name.staticElement = constructorElement;
} else {
constructorNode = AstFactory.constructorName(typeNode, null);
}
constructorNode.staticElement = constructorElement;
// create InstanceCreationExpression
InstanceCreationExpression instanceCreation = AstFactory
.instanceCreationExpression(Keyword.CONST, constructorNode, arguments);
instanceCreation.staticElement = constructorElement;
_push(instanceCreation);
}
void _pushList(TypeArgumentList typeArguments) {
int count = uc.ints[intPtr++];
List<Expression> elements = <Expression>[];
for (int i = 0; i < count; i++) {
elements.insert(0, _pop());
}
_push(AstFactory.listLiteral2(Keyword.CONST, typeArguments, elements));
}
void _pushMap(TypeArgumentList typeArguments) {
int count = uc.ints[intPtr++];
List<MapLiteralEntry> entries = <MapLiteralEntry>[];
for (int i = 0; i < count; i++) {
Expression value = _pop();
Expression key = _pop();
entries.insert(0, AstFactory.mapLiteralEntry2(key, value));
}
_push(AstFactory.mapLiteral(Keyword.CONST, typeArguments, entries));
}
void _pushPrefix(TokenType operator) {
Expression operand = _pop();
_push(AstFactory.prefixExpression(operator, operand));
}
List<Expression> _removeTopItems(int count) {
int start = stack.length - count;
int end = stack.length;
List<Expression> items = stack.getRange(start, end).toList();
stack.removeRange(start, end);
return items;
}
}
/**
* The constructor element that has been resynthesized from a summary. The
* actual element won't be constructed until it is requested. But properties
* [displayName], [enclosingElement] and [name] can be used without creating
* the actual element.
*/
class _DeferredConstructorElement extends ConstructorElementHandle {
/**
* The type defining this constructor element. If [_isMember] is `false`,
* then the type parameters of [_definingType] are not guaranteed to be
* valid.
*/
final InterfaceType _definingType;
/**
* The constructor name.
*/
final String name;
factory _DeferredConstructorElement(InterfaceType definingType, String name) {
List<String> components = definingType.element.location.components.toList();
components.add(name);
ElementLocationImpl location = new ElementLocationImpl.con3(components);
return new _DeferredConstructorElement._(definingType, name, location);
}
_DeferredConstructorElement._(
this._definingType, this.name, ElementLocation location)
: super(null, location);
@override
Element get actualElement => enclosingElement.getNamedConstructor(name);
@override
AnalysisContext get context => _definingType.element.context;
@override
String get displayName => name;
@override
ClassElement get enclosingElement {
return _definingType.element;
}
}
/**
* Local function element representing the intializer for a variable that has
* been resynthesized from a summary. The actual element won't be constructed
* until it is requested. But properties [context] and [enclosingElement] can
* be used without creating the actual element.
*/
class _DeferredInitializerElement extends FunctionElementHandle {
/**
* The variable element containing this element.
*/
@override
final VariableElement enclosingElement;
_DeferredInitializerElement(this.enclosingElement) : super(null, null);
@override
FunctionElement get actualElement => enclosingElement.initializer;
@override
AnalysisContext get context => enclosingElement.context;
@override
ElementLocation get location => actualElement.location;
}
/**
* Local function element that has been resynthesized from a summary. The
* actual element won't be constructed until it is requested. But properties
* [context] and [enclosingElement] can be used without creating the actual
* element.
*/
class _DeferredLocalFunctionElement extends FunctionElementHandle {
/**
* The executable element containing this element.
*/
@override
final ExecutableElement enclosingElement;
/**
* The index of this function within [ExecutableElement.functions].
*/
final int _localIndex;
_DeferredLocalFunctionElement(this.enclosingElement, this._localIndex)
: super(null, null);
@override
FunctionElement get actualElement {
ExecutableElement enclosingElement = this.enclosingElement;
if (enclosingElement is PropertyAccessorElement &&
enclosingElement.isSynthetic) {
return enclosingElement.variable.initializer;
} else {
return enclosingElement.functions[_localIndex];
}
}
@override
AnalysisContext get context => enclosingElement.context;
@override
ElementLocation get location => actualElement.location;
}
/**
* Local variable element that has been resynthesized from a summary. The
* actual element won't be constructed until it is requested. But properties
* [context] and [enclosingElement] can be used without creating the actual
* element.
*/
class _DeferredLocalVariableElement extends LocalVariableElementHandle {
/**
* The executable element containing this element.
*/
@override
final ExecutableElement enclosingElement;
/**
* The index of this variable within [ExecutableElement.localVariables].
*/
final int _localIndex;
_DeferredLocalVariableElement(this.enclosingElement, this._localIndex)
: super(null, null);
@override
LocalVariableElement get actualElement =>
enclosingElement.localVariables[_localIndex];
@override
AnalysisContext get context => enclosingElement.context;
@override
ElementLocation get location => actualElement.location;
}
/**
* An instance of [_LibraryResynthesizer] is responsible for resynthesizing the
* elements in a single library from that library's summary.
*/
class _LibraryResynthesizer {
/**
* The [SummaryResynthesizer] which is being used to obtain summaries.
*/
final SummaryResynthesizer summaryResynthesizer;
/**
* Linked summary of the library to be resynthesized.
*/
final LinkedLibrary linkedLibrary;
/**
* Unlinked compilation units constituting the library to be resynthesized.
*/
final List<UnlinkedUnit> unlinkedUnits;
/**
* [Source] object for the library to be resynthesized.
*/
final Source librarySource;
/**
* Indicates whether [librarySource] is the `dart:core` library.
*/
bool isCoreLibrary;
/**
* Classes which should have their supertype set to "object" once
* resynthesis is complete. Only used if [isCoreLibrary] is `true`.
*/
List<ClassElementImpl> delayedObjectSubclasses = <ClassElementImpl>[];
/**
* [ElementHolder] into which resynthesized elements should be placed. This
* object is recreated afresh for each unit in the library, and is used to
* populate the [CompilationUnitElement].
*/
ElementHolder unitHolder;
/**
* The [LinkedUnit] from which elements are currently being resynthesized.
*/
LinkedUnit linkedUnit;
/**
* The [UnlinkedUnit] from which elements are currently being resynthesized.
*/
UnlinkedUnit unlinkedUnit;
/**
* Map from slot id to the corresponding [EntityRef] object for linked types
* (i.e. propagated and inferred types).
*/
Map<int, EntityRef> linkedTypeMap;
/**
* The [CompilationUnitElementImpl] for the compilation unit currently being
* resynthesized.
*/
CompilationUnitElementImpl currentCompilationUnit;
/**
* The [ConstructorElementImpl] for the constructor currently being
* resynthesized.
*/
ConstructorElementImpl currentConstructor;
/**
* Map of compilation unit elements that have been resynthesized so far. The
* key is the URI of the compilation unit.
*/
final Map<String, CompilationUnitElement> resynthesizedUnits =
<String, CompilationUnitElement>{};
/**
* Map of top level elements that have been resynthesized so far. The first
* key is the URI of the compilation unit; the second is the name of the top
* level element.
*/
final Map<String, Map<String, Element>> resynthesizedElements =
<String, Map<String, Element>>{};
/**
* Type parameters for the generic class, typedef, or executable currently
* being resynthesized, if any. This is a list of lists; if multiple
* entities with type parameters are nested (e.g. a generic executable inside
* a generic class), then the zeroth element of [currentTypeParameters]
* contains the type parameters for the outermost nested entity, and further
* elements contain the type parameters for entities that are more deeply
* nested. If we are not currently resynthesizing a class, typedef, or
* executable, then this is an empty list.
*/
final List<List<TypeParameterElement>> currentTypeParameters =
<List<TypeParameterElement>>[];
/**
* If a class is currently being resynthesized, map from field name to the
* corresponding field element. This is used when resynthesizing
* initializing formal parameters.
*/
Map<String, FieldElementImpl> fields;
/**
* If a class is currently being resynthesized, map from constructor name to
* the corresponding constructor element. This is used when resynthesizing
* constructor initializers.
*/
Map<String, ConstructorElementImpl> constructors;
/**
* List of [_ReferenceInfo] objects describing the references in the current
* compilation unit.
*/
List<_ReferenceInfo> referenceInfos;
_LibraryResynthesizer(this.summaryResynthesizer, this.linkedLibrary,
this.unlinkedUnits, this.librarySource) {
isCoreLibrary = librarySource.uri.toString() == 'dart:core';
}
/**
* Build the annotations for the given [element].
*/
void buildAnnotations(
ElementImpl element, List<UnlinkedConst> serializedAnnotations) {
if (serializedAnnotations.isNotEmpty) {
element.metadata = serializedAnnotations.map((UnlinkedConst a) {
ElementAnnotationImpl elementAnnotation =
new ElementAnnotationImpl(this.currentCompilationUnit);
Expression constExpr = _buildConstExpression(a);
if (constExpr is Identifier) {
elementAnnotation.element = constExpr.staticElement;
elementAnnotation.annotationAst = AstFactory.annotation(constExpr);
} else if (constExpr is InstanceCreationExpression) {
elementAnnotation.element = constExpr.staticElement;
Identifier typeName = constExpr.constructorName.type.name;
SimpleIdentifier constructorName = constExpr.constructorName.name;
if (typeName is SimpleIdentifier && constructorName != null) {
// E.g. `@cls.ctor()`. Since `cls.ctor` would have been parsed as
// a PrefixedIdentifier, we need to resynthesize it as one.
typeName = AstFactory.identifier(typeName, constructorName);
constructorName = null;
}
elementAnnotation.annotationAst = AstFactory.annotation2(
typeName, constructorName, constExpr.argumentList);
} else {
throw new StateError(
'Unexpected annotation type: ${constExpr.runtimeType}');
}
return elementAnnotation;
}).toList();
}
}
/**
* Resynthesize a [ClassElement] and place it in [unitHolder].
*/
void buildClass(UnlinkedClass serializedClass) {
ClassElementImpl classElement =
new ClassElementImpl(serializedClass.name, serializedClass.nameOffset);
classElement.hasBeenInferred = summaryResynthesizer.strongMode;
classElement.typeParameters =
buildTypeParameters(serializedClass.typeParameters);
classElement.abstract = serializedClass.isAbstract;
classElement.mixinApplication = serializedClass.isMixinApplication;
InterfaceTypeImpl correspondingType = new InterfaceTypeImpl(classElement);
if (serializedClass.supertype != null) {
classElement.supertype = buildType(serializedClass.supertype);
} else if (!serializedClass.hasNoSupertype) {
if (isCoreLibrary) {
delayedObjectSubclasses.add(classElement);
} else {
classElement.supertype = summaryResynthesizer.typeProvider.objectType;
}
}
classElement.interfaces =
serializedClass.interfaces.map(buildType).toList();
classElement.mixins = serializedClass.mixins.map(buildType).toList();
ElementHolder memberHolder = new ElementHolder();
fields = <String, FieldElementImpl>{};
for (UnlinkedVariable serializedVariable in serializedClass.fields) {
buildVariable(serializedVariable, memberHolder);
}
bool constructorFound = false;
constructors = <String, ConstructorElementImpl>{};
for (UnlinkedExecutable serializedExecutable
in serializedClass.executables) {
switch (serializedExecutable.kind) {
case UnlinkedExecutableKind.constructor:
constructorFound = true;
buildConstructor(
serializedExecutable, memberHolder, correspondingType);
break;
case UnlinkedExecutableKind.functionOrMethod:
case UnlinkedExecutableKind.getter:
case UnlinkedExecutableKind.setter:
if (serializedExecutable.isStatic) {
currentTypeParameters.removeLast();
}
buildExecutable(serializedExecutable, memberHolder);
if (serializedExecutable.isStatic) {
currentTypeParameters.add(classElement.typeParameters);
}
break;
}
}
if (!serializedClass.isMixinApplication) {
if (!constructorFound) {
// Synthesize implicit constructors.
ConstructorElementImpl constructor = new ConstructorElementImpl('', -1);
constructor.synthetic = true;
constructor.returnType = correspondingType;
constructor.type = new FunctionTypeImpl.elementWithNameAndArgs(
constructor, null, getCurrentTypeArguments(), false);
memberHolder.addConstructor(constructor);
}
classElement.constructors = memberHolder.constructors;
}
classElement.accessors = memberHolder.accessors;
classElement.fields = memberHolder.fields;
classElement.methods = memberHolder.methods;
correspondingType.typeArguments = getCurrentTypeArguments();
classElement.type = correspondingType;
buildDocumentation(classElement, serializedClass.documentationComment);
buildAnnotations(classElement, serializedClass.annotations);
resolveConstructorInitializers(classElement);
unitHolder.addType(classElement);
currentTypeParameters.removeLast();
assert(currentTypeParameters.isEmpty);
fields = null;
constructors = null;
}
/**
* Resynthesize a [NamespaceCombinator].
*/
NamespaceCombinator buildCombinator(UnlinkedCombinator serializedCombinator) {
if (serializedCombinator.shows.isNotEmpty) {
ShowElementCombinatorImpl combinator = new ShowElementCombinatorImpl();
// Note: we call toList() so that we don't retain a reference to the
// deserialized data structure.
combinator.shownNames = serializedCombinator.shows.toList();
combinator.offset = serializedCombinator.offset;
combinator.end = serializedCombinator.end;
return combinator;
} else {
HideElementCombinatorImpl combinator = new HideElementCombinatorImpl();
// Note: we call toList() so that we don't retain a reference to the
// deserialized data structure.
combinator.hiddenNames = serializedCombinator.hides.toList();
return combinator;
}
}
/**
* Resynthesize the [ConstructorInitializer] in context of
* [currentConstructor], which is used to resolve constructor parameter names.
*/
ConstructorInitializer buildConstantInitializer(
UnlinkedConstructorInitializer serialized) {
UnlinkedConstructorInitializerKind kind = serialized.kind;
String name = serialized.name;
List<Expression> arguments =
serialized.arguments.map(_buildConstExpression).toList();
switch (kind) {
case UnlinkedConstructorInitializerKind.field:
return AstFactory.constructorFieldInitializer(
false, name, _buildConstExpression(serialized.expression));
case UnlinkedConstructorInitializerKind.superInvocation:
return AstFactory.superConstructorInvocation2(
name.isNotEmpty ? name : null, arguments);
case UnlinkedConstructorInitializerKind.thisInvocation:
return AstFactory.redirectingConstructorInvocation2(
name.isNotEmpty ? name : null, arguments);
}
}
/**
* Resynthesize a [ConstructorElement] and place it in the given [holder].
* [classType] is the type of the class for which this element is a
* constructor.
*/
void buildConstructor(UnlinkedExecutable serializedExecutable,
ElementHolder holder, InterfaceType classType) {
assert(serializedExecutable.kind == UnlinkedExecutableKind.constructor);
currentConstructor = new ConstructorElementImpl(
serializedExecutable.name, serializedExecutable.nameOffset);
if (serializedExecutable.name.isEmpty) {
currentConstructor.nameEnd =
serializedExecutable.nameOffset + classType.name.length;
} else {
currentConstructor.nameEnd = serializedExecutable.nameEnd;
currentConstructor.periodOffset = serializedExecutable.periodOffset;
}
constructors[serializedExecutable.name] = currentConstructor;
currentConstructor.returnType = classType;
buildExecutableCommonParts(currentConstructor, serializedExecutable);
currentConstructor.factory = serializedExecutable.isFactory;
currentConstructor.const2 = serializedExecutable.isConst;
currentConstructor.constantInitializers = serializedExecutable
.constantInitializers
.map(buildConstantInitializer)
.toList();
if (serializedExecutable.isRedirectedConstructor) {
if (serializedExecutable.isFactory) {
EntityRef redirectedConstructor =
serializedExecutable.redirectedConstructor;
_ReferenceInfo info = referenceInfos[redirectedConstructor.reference];
List<EntityRef> typeArguments = redirectedConstructor.typeArguments;
currentConstructor.redirectedConstructor = _createConstructorElement(
_createConstructorDefiningType(info, typeArguments), info);
} else {
List<String> locationComponents =
currentCompilationUnit.location.components.toList();
locationComponents.add(classType.name);
locationComponents.add(serializedExecutable.redirectedConstructorName);
currentConstructor.redirectedConstructor =
new _DeferredConstructorElement._(
classType,
serializedExecutable.redirectedConstructorName,
new ElementLocationImpl.con3(locationComponents));
}
}
holder.addConstructor(currentConstructor);
currentConstructor = null;
}
/**
* Build the documentation for the given [element]. Does nothing if
* [serializedDocumentationComment] is `null`.
*/
void buildDocumentation(ElementImpl element,
UnlinkedDocumentationComment serializedDocumentationComment) {
if (serializedDocumentationComment != null) {
element.documentationComment = serializedDocumentationComment.text;
element.setDocRange(serializedDocumentationComment.offset,
serializedDocumentationComment.length);
}
}
/**
* Resynthesize the [ClassElement] corresponding to an enum, along with the
* associated fields and implicit accessors.
*/
void buildEnum(UnlinkedEnum serializedEnum) {
assert(!isCoreLibrary);
ClassElementImpl classElement =
new ClassElementImpl(serializedEnum.name, serializedEnum.nameOffset);
classElement.enum2 = true;
InterfaceType enumType = new InterfaceTypeImpl(classElement);
classElement.type = enumType;
classElement.supertype = summaryResynthesizer.typeProvider.objectType;
buildDocumentation(classElement, serializedEnum.documentationComment);
buildAnnotations(classElement, serializedEnum.annotations);
ElementHolder memberHolder = new ElementHolder();
// Build the 'index' field.
FieldElementImpl indexField = new FieldElementImpl('index', -1);
indexField.final2 = true;
indexField.synthetic = true;
indexField.type = summaryResynthesizer.typeProvider.intType;
memberHolder.addField(indexField);
buildImplicitAccessors(indexField, memberHolder);
// Build the 'values' field.
FieldElementImpl valuesField = new ConstFieldElementImpl('values', -1);
valuesField.synthetic = true;
valuesField.const3 = true;
valuesField.static = true;
valuesField.type = summaryResynthesizer.typeProvider.listType
.substitute4(<DartType>[enumType]);
memberHolder.addField(valuesField);
buildImplicitAccessors(valuesField, memberHolder);
// Build fields for all enum constants.
List<DartObjectImpl> constantValues = <DartObjectImpl>[];
for (int i = 0; i < serializedEnum.values.length; i++) {
UnlinkedEnumValue serializedEnumValue = serializedEnum.values[i];
String fieldName = serializedEnumValue.name;
ConstFieldElementImpl field =
new ConstFieldElementImpl(fieldName, serializedEnumValue.nameOffset);
buildDocumentation(field, serializedEnumValue.documentationComment);
field.const3 = true;
field.static = true;
field.type = enumType;
// Create a value for the constant.
Map<String, DartObjectImpl> fieldMap = <String, DartObjectImpl>{
fieldName: new DartObjectImpl(
summaryResynthesizer.typeProvider.intType, new IntState(i))
};
DartObjectImpl value =
new DartObjectImpl(enumType, new GenericState(fieldMap));
constantValues.add(value);
field.evaluationResult = new EvaluationResultImpl(value);
// Add the field.
memberHolder.addField(field);
buildImplicitAccessors(field, memberHolder);
}
// Build the value of the 'values' field.
valuesField.evaluationResult = new EvaluationResultImpl(
new DartObjectImpl(valuesField.type, new ListState(constantValues)));
// done
classElement.fields = memberHolder.fields;
classElement.accessors = memberHolder.accessors;
classElement.constructors = <ConstructorElement>[];
unitHolder.addEnum(classElement);
}
/**
* Resynthesize an [ExecutableElement] and place it in the given [holder].
*/
void buildExecutable(UnlinkedExecutable serializedExecutable,
[ElementHolder holder]) {
bool isTopLevel = holder == null;
if (holder == null) {
holder = unitHolder;
}
UnlinkedExecutableKind kind = serializedExecutable.kind;
String name = serializedExecutable.name;
if (kind == UnlinkedExecutableKind.setter) {
assert(name.endsWith('='));
name = name.substring(0, name.length - 1);
}
switch (kind) {
case UnlinkedExecutableKind.functionOrMethod:
if (isTopLevel) {
FunctionElementImpl executableElement =
new FunctionElementImpl(name, serializedExecutable.nameOffset);
buildExecutableCommonParts(executableElement, serializedExecutable);
holder.addFunction(executableElement);
} else {
MethodElementImpl executableElement =
new MethodElementImpl(name, serializedExecutable.nameOffset);
executableElement.abstract = serializedExecutable.isAbstract;
buildExecutableCommonParts(executableElement, serializedExecutable);
executableElement.static = serializedExecutable.isStatic;
holder.addMethod(executableElement);
}
break;
case UnlinkedExecutableKind.getter:
case UnlinkedExecutableKind.setter:
PropertyAccessorElementImpl executableElement =
new PropertyAccessorElementImpl(
name, serializedExecutable.nameOffset);
if (isTopLevel) {
executableElement.static = true;
} else {
executableElement.static = serializedExecutable.isStatic;
executableElement.abstract = serializedExecutable.isAbstract;
}
buildExecutableCommonParts(executableElement, serializedExecutable);
DartType type;
if (kind == UnlinkedExecutableKind.getter) {
executableElement.getter = true;
type = executableElement.returnType;
} else {
executableElement.setter = true;
type = executableElement.parameters[0].type;
}
holder.addAccessor(executableElement);
PropertyInducingElementImpl implicitVariable;
if (isTopLevel) {
implicitVariable = buildImplicitTopLevelVariable(name, kind, holder);
} else {
FieldElementImpl field = buildImplicitField(name, type, kind, holder);
field.static = serializedExecutable.isStatic;
implicitVariable = field;
}
executableElement.variable = implicitVariable;
if (kind == UnlinkedExecutableKind.getter) {
implicitVariable.getter = executableElement;
} else {
implicitVariable.setter = executableElement;
}
break;
default:
// The only other executable type is a constructor, and that is handled
// separately (in [buildConstructor]. So this code should be
// unreachable.
assert(false);
}
}
/**
* Handle the parts of an executable element that are common to constructors,
* functions, methods, getters, and setters.
*/
void buildExecutableCommonParts(ExecutableElementImpl executableElement,
UnlinkedExecutable serializedExecutable) {
executableElement.typeParameters =
buildTypeParameters(serializedExecutable.typeParameters);
executableElement.parameters =
serializedExecutable.parameters.map(buildParameter).toList();
if (serializedExecutable.kind == UnlinkedExecutableKind.constructor) {
// Caller handles setting the return type.
assert(serializedExecutable.returnType == null);
} else {
bool isSetter =
serializedExecutable.kind == UnlinkedExecutableKind.setter;
executableElement.returnType =
buildLinkedType(serializedExecutable.inferredReturnTypeSlot) ??
buildType(serializedExecutable.returnType,
defaultVoid: isSetter && summaryResynthesizer.strongMode);
executableElement.hasImplicitReturnType =
serializedExecutable.returnType == null;
}
executableElement.type = new FunctionTypeImpl.elementWithNameAndArgs(
executableElement, null, getCurrentTypeArguments(skipLevels: 1), false);
executableElement.external = serializedExecutable.isExternal;
buildDocumentation(
executableElement, serializedExecutable.documentationComment);
buildAnnotations(executableElement, serializedExecutable.annotations);
executableElement.functions =
serializedExecutable.localFunctions.map(buildLocalFunction).toList();
executableElement.labels =
serializedExecutable.localLabels.map(buildLocalLabel).toList();
executableElement.localVariables =
serializedExecutable.localVariables.map(buildLocalVariable).toList();
currentTypeParameters.removeLast();
}
/**
* Resynthesize an [ExportElement],
*/
ExportElement buildExport(UnlinkedExportPublic serializedExportPublic,
UnlinkedExportNonPublic serializedExportNonPublic) {
ExportElementImpl exportElement =
new ExportElementImpl(serializedExportNonPublic.offset);
String exportedLibraryUri = summaryResynthesizer.sourceFactory
.resolveUri(librarySource, serializedExportPublic.uri)
.uri
.toString();
exportElement.exportedLibrary = new LibraryElementHandle(
summaryResynthesizer,
new ElementLocationImpl.con3(<String>[exportedLibraryUri]));
exportElement.uri = serializedExportPublic.uri;
exportElement.combinators =
serializedExportPublic.combinators.map(buildCombinator).toList();
exportElement.uriOffset = serializedExportNonPublic.uriOffset;
exportElement.uriEnd = serializedExportNonPublic.uriEnd;
buildAnnotations(exportElement, serializedExportNonPublic.annotations);
return exportElement;
}
/**
* Build an [ElementHandle] referring to the entity referred to by the given
* [exportName].
*/
ElementHandle buildExportName(LinkedExportName exportName) {
String name = exportName.name;
if (exportName.kind == ReferenceKind.topLevelPropertyAccessor &&
!name.endsWith('=')) {
name += '?';
}
ElementLocationImpl location = new ElementLocationImpl.con3(
getReferencedLocationComponents(
exportName.dependency, exportName.unit, name));
switch (exportName.kind) {
case ReferenceKind.classOrEnum:
return new ClassElementHandle(summaryResynthesizer, location);
case ReferenceKind.typedef:
return new FunctionTypeAliasElementHandle(
summaryResynthesizer, location);
case ReferenceKind.topLevelFunction:
return new FunctionElementHandle(summaryResynthesizer, location);
case ReferenceKind.topLevelPropertyAccessor:
return new PropertyAccessorElementHandle(
summaryResynthesizer, location);
case ReferenceKind.constructor:
case ReferenceKind.function:
case ReferenceKind.propertyAccessor:
case ReferenceKind.method:
case ReferenceKind.length:
case ReferenceKind.prefix:
case ReferenceKind.unresolved:
case ReferenceKind.variable:
// Should never happen. Exported names never refer to import prefixes,
// and they always refer to defined top-level entities.
throw new StateError('Unexpected export name kind: ${exportName.kind}');
}
}
/**
* Build the export namespace for the library by aggregating together its
* [publicNamespace] and [exportNames].
*/
Namespace buildExportNamespace(
Namespace publicNamespace, List<LinkedExportName> exportNames) {
HashMap<String, Element> definedNames = new HashMap<String, Element>();
// Start by populating all the public names from [publicNamespace].
publicNamespace.definedNames.forEach((String name, Element element) {
definedNames[name] = element;
});
// Add all the names from [exportNames].
for (LinkedExportName exportName in exportNames) {
definedNames.putIfAbsent(
exportName.name, () => buildExportName(exportName));
}
return new Namespace(definedNames);
}
/**
* Build the implicit getter and setter associated with [element], and place
* them in [holder].
*/
void buildImplicitAccessors(
PropertyInducingElementImpl element, ElementHolder holder) {
String name = element.name;
DartType type = element.type;
PropertyAccessorElementImpl getter =
new PropertyAccessorElementImpl(name, element.nameOffset);
getter.getter = true;
getter.static = element.isStatic;
getter.synthetic = true;
getter.returnType = type;
getter.type = new FunctionTypeImpl(getter);
getter.variable = element;
getter.hasImplicitReturnType = element.hasImplicitType;
holder.addAccessor(getter);
element.getter = getter;
if (!(element.isConst || element.isFinal)) {
PropertyAccessorElementImpl setter =
new PropertyAccessorElementImpl(name, element.nameOffset);
setter.setter = true;
setter.static = element.isStatic;
setter.synthetic = true;
setter.parameters = <ParameterElement>[
new ParameterElementImpl('_$name', element.nameOffset)
..synthetic = true
..type = type
..parameterKind = ParameterKind.REQUIRED
];
setter.returnType = VoidTypeImpl.instance;
setter.type = new FunctionTypeImpl(setter);
setter.variable = element;
holder.addAccessor(setter);
element.setter = setter;
}
}
/**
* Build the implicit field associated with a getter or setter, and place it
* in [holder].
*/
FieldElementImpl buildImplicitField(String name, DartType type,
UnlinkedExecutableKind kind, ElementHolder holder) {
FieldElementImpl field = holder.getField(name);
if (field == null) {
field = new FieldElementImpl(name, -1);
field.synthetic = true;
field.final2 = kind == UnlinkedExecutableKind.getter;
field.type = type;
holder.addField(field);
return field;
} else {
// TODO(paulberry): what if the getter and setter have a type mismatch?
field.final2 = false;
return field;
}
}
/**
* Build the implicit top level variable associated with a getter or setter,
* and place it in [holder].
*/
PropertyInducingElementImpl buildImplicitTopLevelVariable(
String name, UnlinkedExecutableKind kind, ElementHolder holder) {
TopLevelVariableElementImpl variable = holder.getTopLevelVariable(name);
if (variable == null) {
variable = new TopLevelVariableElementImpl(name, -1);
variable.synthetic = true;
variable.final2 = kind == UnlinkedExecutableKind.getter;
holder.addTopLevelVariable(variable);
return variable;
} else {
// TODO(paulberry): what if the getter and setter have a type mismatch?
variable.final2 = false;
return variable;
}
}
/**
* Resynthesize an [ImportElement].
*/
ImportElement buildImport(UnlinkedImport serializedImport, int dependency) {
bool isSynthetic = serializedImport.isImplicit;
ImportElementImpl importElement =
new ImportElementImpl(isSynthetic ? -1 : serializedImport.offset);
String absoluteUri = summaryResynthesizer.sourceFactory
.resolveUri(librarySource, linkedLibrary.dependencies[dependency].uri)
.uri
.toString();
importElement.importedLibrary = new LibraryElementHandle(
summaryResynthesizer,
new ElementLocationImpl.con3(<String>[absoluteUri]));
if (isSynthetic) {
importElement.synthetic = true;
} else {
importElement.uri = serializedImport.uri;
importElement.uriOffset = serializedImport.uriOffset;
importElement.uriEnd = serializedImport.uriEnd;
importElement.deferred = serializedImport.isDeferred;
buildAnnotations(importElement, serializedImport.annotations);
}
importElement.prefixOffset = serializedImport.prefixOffset;
if (serializedImport.prefixReference != 0) {
UnlinkedReference serializedPrefix =
unlinkedUnits[0].references[serializedImport.prefixReference];
importElement.prefix = new PrefixElementImpl(
serializedPrefix.name, serializedImport.prefixOffset);
}
importElement.combinators =
serializedImport.combinators.map(buildCombinator).toList();
return importElement;
}
/**
* Main entry point. Resynthesize the [LibraryElement] and return it.
*/
LibraryElement buildLibrary() {
CompilationUnitElementImpl definingCompilationUnit =
new CompilationUnitElementImpl(librarySource.shortName);
prepareUnit(definingCompilationUnit, 0);
bool hasName = unlinkedUnits[0].libraryName.isNotEmpty;
LibraryElementImpl library = new LibraryElementImpl(
summaryResynthesizer.context,
unlinkedUnits[0].libraryName,
hasName ? unlinkedUnits[0].libraryNameOffset : -1,
unlinkedUnits[0].libraryNameLength);
buildDocumentation(library, unlinkedUnits[0].libraryDocumentationComment);
buildAnnotations(library, unlinkedUnits[0].libraryAnnotations);
library.definingCompilationUnit = definingCompilationUnit;
definingCompilationUnit.source = librarySource;
definingCompilationUnit.librarySource = librarySource;
List<CompilationUnitElement> parts = <CompilationUnitElement>[];
UnlinkedUnit unlinkedDefiningUnit = unlinkedUnits[0];
assert(unlinkedDefiningUnit.publicNamespace.parts.length + 1 ==
linkedLibrary.units.length);
for (int i = 1; i < linkedLibrary.units.length; i++) {
CompilationUnitElementImpl part = buildPart(
unlinkedDefiningUnit.publicNamespace.parts[i - 1],
unlinkedDefiningUnit.parts[i - 1],
unlinkedUnits[i]);
parts.add(part);
}
library.parts = parts;
List<ImportElement> imports = <ImportElement>[];
for (int i = 0; i < unlinkedDefiningUnit.imports.length; i++) {
imports.add(buildImport(unlinkedDefiningUnit.imports[i],
linkedLibrary.importDependencies[i]));
}
library.imports = imports;
List<ExportElement> exports = <ExportElement>[];
assert(unlinkedDefiningUnit.exports.length ==
unlinkedDefiningUnit.publicNamespace.exports.length);
for (int i = 0; i < unlinkedDefiningUnit.exports.length; i++) {
exports.add(buildExport(unlinkedDefiningUnit.publicNamespace.exports[i],
unlinkedDefiningUnit.exports[i]));
}
library.exports = exports;
populateUnit(definingCompilationUnit, 0);
finishUnit();
for (int i = 0; i < parts.length; i++) {
prepareUnit(parts[i], i + 1);
populateUnit(parts[i], i + 1);
finishUnit();
}
BuildLibraryElementUtils.patchTopLevelAccessors(library);
// Update delayed Object class references.
if (isCoreLibrary) {
ClassElement objectElement = library.getType('Object');
assert(objectElement != null);
for (ClassElementImpl classElement in delayedObjectSubclasses) {
classElement.supertype = objectElement.type;
}
}
// Compute namespaces.
library.publicNamespace =
new NamespaceBuilder().createPublicNamespaceForLibrary(library);
library.exportNamespace = buildExportNamespace(
library.publicNamespace, linkedLibrary.exportNames);
// Find the entry point. Note: we can't use element.isEntryPoint because
// that will trigger resynthesis of exported libraries.
Element entryPoint =
library.exportNamespace.get(FunctionElement.MAIN_FUNCTION_NAME);
if (entryPoint is FunctionElement) {
library.entryPoint = entryPoint;
}
// Create the synthetic element for `loadLibrary`.
// Until the client received dart:core and dart:async, we cannot do this,
// because the TypeProvider is not fully initialized. So, it is up to the
// Dart SDK client to initialize TypeProvider and finish the dart:core and
// dart:async libraries creation.
if (library.name != 'dart.core' && library.name != 'dart.async') {
library.createLoadLibraryFunction(summaryResynthesizer.typeProvider);
}
// Done.
return library;
}
/**
* Build the appropriate [DartType] object corresponding to a slot id in the
* [LinkedUnit.types] table.
*/
DartType buildLinkedType(int slot) {
if (slot == 0) {
// A slot id of 0 means there is no [DartType] object to build.
return null;
}
EntityRef type = linkedTypeMap[slot];
if (type == null) {
// A missing entry in [LinkedUnit.types] means there is no [DartType]
// stored in this slot.
return null;
}
return buildType(type);
}
/**
* Resynthesize a local [FunctionElement].
*/
FunctionElementImpl buildLocalFunction(
UnlinkedExecutable serializedExecutable) {
FunctionElementImpl element = new FunctionElementImpl(
serializedExecutable.name, serializedExecutable.nameOffset);
if (serializedExecutable.visibleOffset != 0) {
element.setVisibleRange(serializedExecutable.visibleOffset,
serializedExecutable.visibleLength);
}
buildExecutableCommonParts(element, serializedExecutable);
return element;
}
/**
* Resynthesize a [LabelElement].
*/
LabelElement buildLocalLabel(UnlinkedLabel serializedLabel) {
return new LabelElementImpl(
serializedLabel.name,
serializedLabel.nameOffset,
serializedLabel.isOnSwitchStatement,
serializedLabel.isOnSwitchMember);
}
/**
* Resynthesize a [LocalVariableElement].
*/
LocalVariableElement buildLocalVariable(UnlinkedVariable serializedVariable) {
LocalVariableElementImpl element;
if (serializedVariable.constExpr != null) {
ConstLocalVariableElementImpl constElement =
new ConstLocalVariableElementImpl(
serializedVariable.name, serializedVariable.nameOffset);
element = constElement;
constElement.constantInitializer =
_buildConstExpression(serializedVariable.constExpr);
} else {
element = new LocalVariableElementImpl(
serializedVariable.name, serializedVariable.nameOffset);
}
if (serializedVariable.visibleOffset != 0) {
element.setVisibleRange(
serializedVariable.visibleOffset, serializedVariable.visibleLength);
}
buildVariableCommonParts(element, serializedVariable);
return element;
}
/**
* Resynthesize a [ParameterElement].
*/
ParameterElement buildParameter(UnlinkedParam serializedParameter,
{bool synthetic: false}) {
ParameterElementImpl parameterElement;
int nameOffset = synthetic ? -1 : serializedParameter.nameOffset;
if (serializedParameter.isInitializingFormal) {
FieldFormalParameterElementImpl initializingParameter;
if (serializedParameter.kind == UnlinkedParamKind.required) {
initializingParameter = new FieldFormalParameterElementImpl(
serializedParameter.name, nameOffset);
} else {
DefaultFieldFormalParameterElementImpl defaultParameter =
new DefaultFieldFormalParameterElementImpl(
serializedParameter.name, nameOffset);
initializingParameter = defaultParameter;
if (serializedParameter.defaultValue != null) {
defaultParameter.constantInitializer =
_buildConstExpression(serializedParameter.defaultValue);
defaultParameter.defaultValueCode =
serializedParameter.defaultValueCode;
}
}
parameterElement = initializingParameter;
initializingParameter.field = fields[serializedParameter.name];
} else {
if (serializedParameter.kind == UnlinkedParamKind.required) {
parameterElement =
new ParameterElementImpl(serializedParameter.name, nameOffset);
} else {
DefaultParameterElementImpl defaultParameter =
new DefaultParameterElementImpl(
serializedParameter.name, nameOffset);
parameterElement = defaultParameter;
if (serializedParameter.defaultValue != null) {
defaultParameter.constantInitializer =
_buildConstExpression(serializedParameter.defaultValue);
defaultParameter.defaultValueCode =
serializedParameter.defaultValueCode;
}
}
}
parameterElement.synthetic = synthetic;
buildAnnotations(parameterElement, serializedParameter.annotations);
if (serializedParameter.isFunctionTyped) {
FunctionElementImpl parameterTypeElement =
new FunctionElementImpl('', -1);
parameterTypeElement.synthetic = true;
parameterElement.parameters =
serializedParameter.parameters.map(buildParameter).toList();
parameterTypeElement.enclosingElement = parameterElement;
parameterTypeElement.shareParameters(parameterElement.parameters);
parameterTypeElement.returnType = buildType(serializedParameter.type);
parameterElement.type = new FunctionTypeImpl.elementWithNameAndArgs(
parameterTypeElement, null, getCurrentTypeArguments(), false);
} else {
if (serializedParameter.isInitializingFormal &&
serializedParameter.type == null) {
// The type is inherited from the matching field.
parameterElement.type = fields[serializedParameter.name]?.type ??
summaryResynthesizer.typeProvider.dynamicType;
} else {
parameterElement.type =
buildLinkedType(serializedParameter.inferredTypeSlot) ??
buildType(serializedParameter.type);
}
parameterElement.hasImplicitType = serializedParameter.type == null;
}
buildVariableInitializer(parameterElement, serializedParameter.initializer);
switch (serializedParameter.kind) {
case UnlinkedParamKind.named:
parameterElement.parameterKind = ParameterKind.NAMED;
break;
case UnlinkedParamKind.positional:
parameterElement.parameterKind = ParameterKind.POSITIONAL;
break;
case UnlinkedParamKind.required:
parameterElement.parameterKind = ParameterKind.REQUIRED;
break;
}
if (serializedParameter.visibleOffset != 0) {
parameterElement.setVisibleRange(
serializedParameter.visibleOffset, serializedParameter.visibleLength);
}
return parameterElement;
}
/**
* Create, but do not populate, the [CompilationUnitElement] for a part other
* than the defining compilation unit.
*/
CompilationUnitElementImpl buildPart(
String uri, UnlinkedPart partDecl, UnlinkedUnit serializedPart) {
Source unitSource =
summaryResynthesizer.sourceFactory.resolveUri(librarySource, uri);
CompilationUnitElementImpl partUnit =
new CompilationUnitElementImpl(unitSource.shortName);
partUnit.uriOffset = partDecl.uriOffset;
partUnit.uriEnd = partDecl.uriEnd;
partUnit.source = unitSource;
partUnit.librarySource = librarySource;
partUnit.uri = uri;
buildAnnotations(partUnit, partDecl.annotations);
return partUnit;
}
/**
* Handle the parts that are common to top level variables and fields.
*/
void buildPropertyIntroducingElementCommonParts(
PropertyInducingElementImpl element,
UnlinkedVariable serializedVariable) {
buildVariableCommonParts(element, serializedVariable);
element.propagatedType =
buildLinkedType(serializedVariable.propagatedTypeSlot);
}
/**
* Build a [DartType] object based on a [EntityRef]. This [DartType]
* may refer to elements in other libraries than the library being
* deserialized, so handles are used to avoid having to deserialize other
* libraries in the process.
*/
DartType buildType(EntityRef type, {bool defaultVoid: false}) {
if (type == null) {
if (defaultVoid) {
return VoidTypeImpl.instance;
} else {
return summaryResynthesizer.typeProvider.dynamicType;
}
}
if (type.paramReference != 0) {
return getTypeParameterFromScope(type.paramReference);
} else if (type.syntheticReturnType != null) {
FunctionElementImpl element = new FunctionElementImpl('', -1);
element.synthetic = true;
element.parameters = type.syntheticParams
.map((UnlinkedParam param) => buildParameter(param, synthetic: true))
.toList();
element.returnType = buildType(type.syntheticReturnType);
FunctionTypeImpl result = new FunctionTypeImpl.elementWithNameAndArgs(
element, null, null, false);
element.type = result;
return result;
} else {
DartType getTypeArgument(int i) {
if (i < type.typeArguments.length) {
return buildType(type.typeArguments[i]);
} else {
return summaryResynthesizer.typeProvider.dynamicType;
}
}
_ReferenceInfo referenceInfo = referenceInfos[type.reference];
return referenceInfo.buildType(
getTypeArgument, type.implicitFunctionTypeIndices);
}
}
/**
* Resynthesize a [FunctionTypeAliasElement] and place it in the
* [unitHolder].
*/
void buildTypedef(UnlinkedTypedef serializedTypedef) {
FunctionTypeAliasElementImpl functionTypeAliasElement =
new FunctionTypeAliasElementImpl(
serializedTypedef.name, serializedTypedef.nameOffset);
functionTypeAliasElement.typeParameters =
buildTypeParameters(serializedTypedef.typeParameters);
functionTypeAliasElement.parameters =
serializedTypedef.parameters.map(buildParameter).toList();
functionTypeAliasElement.returnType =
buildType(serializedTypedef.returnType);
functionTypeAliasElement.type =
new FunctionTypeImpl.forTypedef(functionTypeAliasElement);
buildDocumentation(
functionTypeAliasElement, serializedTypedef.documentationComment);
buildAnnotations(functionTypeAliasElement, serializedTypedef.annotations);
unitHolder.addTypeAlias(functionTypeAliasElement);
currentTypeParameters.removeLast();
assert(currentTypeParameters.isEmpty);
}
/**
* Resynthesize a [TypeParameterElement], handling all parts of its except
* its bound.
*
* The bound is deferred until later since it may refer to other type
* parameters that have not been resynthesized yet. To handle the bound,
* call [finishTypeParameter].
*/
TypeParameterElement buildTypeParameter(
UnlinkedTypeParam serializedTypeParameter) {
TypeParameterElementImpl typeParameterElement =
new TypeParameterElementImpl(
serializedTypeParameter.name, serializedTypeParameter.nameOffset);
typeParameterElement.type = new TypeParameterTypeImpl(typeParameterElement);
buildAnnotations(typeParameterElement, serializedTypeParameter.annotations);
return typeParameterElement;
}
/**
* Build [TypeParameterElement]s corresponding to the type parameters in
* [serializedTypeParameters] and store them in [currentTypeParameters].
* Also return them.
*/
List<TypeParameterElement> buildTypeParameters(
List<UnlinkedTypeParam> serializedTypeParameters) {
List<TypeParameterElement> typeParameters =
serializedTypeParameters.map(buildTypeParameter).toList();
currentTypeParameters.add(typeParameters);
for (int i = 0; i < serializedTypeParameters.length; i++) {
finishTypeParameter(serializedTypeParameters[i], typeParameters[i]);
}
return typeParameters;
}
/**
* Resynthesize a [TopLevelVariableElement] or [FieldElement].
*/
void buildVariable(UnlinkedVariable serializedVariable,
[ElementHolder holder]) {
if (holder == null) {
TopLevelVariableElementImpl element;
if (serializedVariable.constExpr != null) {
ConstTopLevelVariableElementImpl constElement =
new ConstTopLevelVariableElementImpl(
serializedVariable.name, serializedVariable.nameOffset);
element = constElement;
constElement.constantInitializer =
_buildConstExpression(serializedVariable.constExpr);
} else {
element = new TopLevelVariableElementImpl(
serializedVariable.name, serializedVariable.nameOffset);
}
buildPropertyIntroducingElementCommonParts(element, serializedVariable);
unitHolder.addTopLevelVariable(element);
buildImplicitAccessors(element, unitHolder);
} else {
FieldElementImpl element;
if (serializedVariable.constExpr != null) {
ConstFieldElementImpl constElement = new ConstFieldElementImpl(
serializedVariable.name, serializedVariable.nameOffset);
element = constElement;
constElement.constantInitializer =
_buildConstExpression(serializedVariable.constExpr);
} else {
element = new FieldElementImpl(
serializedVariable.name, serializedVariable.nameOffset);
}
buildPropertyIntroducingElementCommonParts(element, serializedVariable);
element.static = serializedVariable.isStatic;
holder.addField(element);
buildImplicitAccessors(element, holder);
fields[element.name] = element;
}
}
/**
* Handle the parts that are common to variables.
*/
void buildVariableCommonParts(
VariableElementImpl element, UnlinkedVariable serializedVariable) {
element.type = buildLinkedType(serializedVariable.inferredTypeSlot) ??
buildType(serializedVariable.type);
element.const3 = serializedVariable.isConst;
element.final2 = serializedVariable.isFinal;
element.hasImplicitType = serializedVariable.type == null;
buildVariableInitializer(element, serializedVariable.initializer);
buildDocumentation(element, serializedVariable.documentationComment);
buildAnnotations(element, serializedVariable.annotations);
}
/**
* If the given [serializedInitializer] is not `null`, create the
* corresponding [FunctionElementImpl] and set it for the [variable].
*/
void buildVariableInitializer(
VariableElementImpl variable, UnlinkedExecutable serializedInitializer) {
if (serializedInitializer == null) {
return null;
}
FunctionElementImpl initializerElement =
buildLocalFunction(serializedInitializer);
initializerElement.synthetic = true;
variable.initializer = initializerElement;
}
/**
* Finish creating a [TypeParameterElement] by deserializing its bound.
*/
void finishTypeParameter(UnlinkedTypeParam serializedTypeParameter,
TypeParameterElementImpl typeParameterElement) {
if (serializedTypeParameter.bound != null) {
typeParameterElement.bound = buildType(serializedTypeParameter.bound);
}
}
/**
* Tear down data structures used during deserialization of a compilation
* unit.
*/
void finishUnit() {
unitHolder = null;
linkedUnit = null;
unlinkedUnit = null;
linkedTypeMap = null;
referenceInfos = null;
currentCompilationUnit = null;
}
/**
* Return a list of type arguments corresponding to [currentTypeParameters],
* skipping the innermost [skipLevels] nesting levels.
*
* Type parameters are listed in nesting order from innermost to outermost,
* and then in declaration order. So for instance if we are resynthesizing a
* method declared as `class C<T, U> { void m<V, W>() { ... } }`, then the
* type parameters will be returned in the order `[V, W, T, U]`.
*/
List<DartType> getCurrentTypeArguments({int skipLevels: 0}) {
assert(currentTypeParameters.length >= skipLevels);
List<DartType> result = <DartType>[];
for (int i = currentTypeParameters.length - 1 - skipLevels; i >= 0; i--) {
result.addAll(currentTypeParameters[i]
.map((TypeParameterElement param) => param.type));
}
return result;
}
/**
* Build the components of an [ElementLocationImpl] for the entity in the
* given [unit] of the dependency located at [dependencyIndex], and having
* the given [name].
*/
List<String> getReferencedLocationComponents(
int dependencyIndex, int unit, String name) {
if (dependencyIndex == 0) {
String referencedLibraryUri = librarySource.uri.toString();
String partUri;
if (unit != 0) {
String uri = unlinkedUnits[0].publicNamespace.parts[unit - 1];
Source partSource =
summaryResynthesizer.sourceFactory.resolveUri(librarySource, uri);
partUri = partSource.uri.toString();
} else {
partUri = referencedLibraryUri;
}
return <String>[referencedLibraryUri, partUri, name];
}
LinkedDependency dependency = linkedLibrary.dependencies[dependencyIndex];
Source referencedLibrarySource = summaryResynthesizer.sourceFactory
.resolveUri(librarySource, dependency.uri);
String referencedLibraryUri = referencedLibrarySource.uri.toString();
String partUri;
if (unit != 0) {
String uri = dependency.parts[unit - 1];
Source partSource = summaryResynthesizer.sourceFactory
.resolveUri(referencedLibrarySource, uri);
partUri = partSource.uri.toString();
} else {
partUri = referencedLibraryUri;
}
return <String>[referencedLibraryUri, partUri, name];
}
/**
* Get the type parameter from the surrounding scope whose De Bruijn index is
* [index].
*/
DartType getTypeParameterFromScope(int index) {
for (int i = currentTypeParameters.length - 1; i >= 0; i--) {
List<TypeParameterElement> paramsAtThisNestingLevel =
currentTypeParameters[i];
int numParamsAtThisNestingLevel = paramsAtThisNestingLevel.length;
if (index <= numParamsAtThisNestingLevel) {
return paramsAtThisNestingLevel[numParamsAtThisNestingLevel - index]
.type;
}
index -= numParamsAtThisNestingLevel;
}
throw new StateError('Type parameter not found');
}
/**
* Populate [referenceInfos] with the correct information for the current
* compilation unit.
*/
void populateReferenceInfos() {
int numLinkedReferences = linkedUnit.references.length;
int numUnlinkedReferences = unlinkedUnit.references.length;
referenceInfos = new List<_ReferenceInfo>(numLinkedReferences);
for (int i = 0; i < numLinkedReferences; i++) {
LinkedReference linkedReference = linkedUnit.references[i];
String name;
int containingReference;
if (i < numUnlinkedReferences) {
name = unlinkedUnit.references[i].name;
containingReference = unlinkedUnit.references[i].prefixReference;
} else {
name = linkedUnit.references[i].name;
containingReference = linkedUnit.references[i].containingReference;
}
_ReferenceInfo enclosingInfo =
containingReference != 0 ? referenceInfos[containingReference] : null;
Element element;
DartType type;
int numTypeParameters = linkedReference.numTypeParameters;
if (linkedReference.kind == ReferenceKind.unresolved) {
type = summaryResynthesizer.typeProvider.undefinedType;
element = null;
} else if (name == 'dynamic') {
type = summaryResynthesizer.typeProvider.dynamicType;
element = type.element;
} else if (name == 'void') {
type = VoidTypeImpl.instance;
element = type.element;
} else if (name == '*bottom*') {
type = BottomTypeImpl.instance;
element = null;
} else {
List<String> locationComponents;
if (enclosingInfo != null && enclosingInfo.element is ClassElement) {
String identifier = _getElementIdentifier(name, linkedReference.kind);
locationComponents =
enclosingInfo.element.location.components.toList();
locationComponents.add(identifier);
} else {
String identifier = _getElementIdentifier(name, linkedReference.kind);
locationComponents = getReferencedLocationComponents(
linkedReference.dependency, linkedReference.unit, identifier);
}
ElementLocation location =
new ElementLocationImpl.con3(locationComponents);
if (enclosingInfo != null) {
numTypeParameters += enclosingInfo.numTypeParameters;
}
switch (linkedReference.kind) {
case ReferenceKind.classOrEnum:
element = new ClassElementHandle(summaryResynthesizer, location);
break;
case ReferenceKind.constructor:
assert(location.components.length == 4);
element =
new ConstructorElementHandle(summaryResynthesizer, location);
break;
case ReferenceKind.length:
element = _buildStringLengthPropertyAccessorElement();
break;
case ReferenceKind.method:
assert(location.components.length == 4);
element = new MethodElementHandle(summaryResynthesizer, location);
break;
case ReferenceKind.propertyAccessor:
assert(location.components.length == 4);
element = new PropertyAccessorElementHandle(
summaryResynthesizer, location);
break;
case ReferenceKind.topLevelFunction:
assert(location.components.length == 3);
element = new FunctionElementHandle(summaryResynthesizer, location);
break;
case ReferenceKind.topLevelPropertyAccessor:
element = new PropertyAccessorElementHandle(
summaryResynthesizer, location);
break;
case ReferenceKind.typedef:
element = new FunctionTypeAliasElementHandle(
summaryResynthesizer, location);
break;
case ReferenceKind.variable:
Element enclosingElement = enclosingInfo.element;
if (enclosingElement is ExecutableElement) {
element = new _DeferredLocalVariableElement(
enclosingElement, linkedReference.localIndex);
} else {
throw new StateError('Unexpected element enclosing variable:'
' ${enclosingElement.runtimeType}');
}
break;
case ReferenceKind.function:
Element enclosingElement = enclosingInfo.element;
if (enclosingElement is VariableElement) {
element = new _DeferredInitializerElement(enclosingElement);
} else if (enclosingElement is ExecutableElement) {
element = new _DeferredLocalFunctionElement(
enclosingElement, linkedReference.localIndex);
} else {
throw new StateError('Unexpected element enclosing function:'
' ${enclosingElement.runtimeType}');
}
break;
case ReferenceKind.prefix:
case ReferenceKind.unresolved:
break;
}
}
referenceInfos[i] = new _ReferenceInfo(
enclosingInfo, name, element, type, numTypeParameters);
}
}
/**
* Populate a [CompilationUnitElement] by deserializing all the elements
* contained in it.
*/
void populateUnit(CompilationUnitElementImpl unit, int unitNum) {
unlinkedUnit.classes.forEach(buildClass);
unlinkedUnit.enums.forEach(buildEnum);
unlinkedUnit.executables.forEach(buildExecutable);
unlinkedUnit.typedefs.forEach(buildTypedef);
unlinkedUnit.variables.forEach(buildVariable);
String absoluteUri = unit.source.uri.toString();
unit.accessors = unitHolder.accessors;
unit.enums = unitHolder.enums;
unit.functions = unitHolder.functions;
List<FunctionTypeAliasElement> typeAliases = unitHolder.typeAliases;
for (FunctionTypeAliasElementImpl typeAlias in typeAliases) {
if (typeAlias.isSynthetic) {
typeAlias.enclosingElement = unit;
}
}
unit.typeAliases = typeAliases.where((e) => !e.isSynthetic).toList();
unit.types = unitHolder.types;
unit.topLevelVariables = unitHolder.topLevelVariables;
Map<String, Element> elementMap = <String, Element>{};
for (ClassElement cls in unit.types) {
elementMap[cls.name] = cls;
}
for (ClassElement cls in unit.enums) {
elementMap[cls.name] = cls;
}
for (FunctionTypeAliasElement typeAlias in unit.functionTypeAliases) {
elementMap[typeAlias.name] = typeAlias;
}
for (FunctionElement function in unit.functions) {
elementMap[function.name] = function;
}
for (PropertyAccessorElementImpl accessor in unit.accessors) {
elementMap[accessor.identifier] = accessor;
}
resynthesizedUnits[absoluteUri] = unit;
resynthesizedElements[absoluteUri] = elementMap;
assert(currentTypeParameters.isEmpty);
}
/**
* Set up data structures for deserializing a compilation unit.
*/
void prepareUnit(CompilationUnitElementImpl unit, int unitNum) {
linkedUnit = linkedLibrary.units[unitNum];
unlinkedUnit = unlinkedUnits[unitNum];
linkedTypeMap = <int, EntityRef>{};
currentCompilationUnit = unit;
for (EntityRef t in linkedUnit.types) {
linkedTypeMap[t.slot] = t;
}
populateReferenceInfos();
unitHolder = new ElementHolder();
}
/**
* Constructor initializers can reference fields and other constructors of
* the same class, including forward references. So, we need to delay
* resolution until after class elements are built.
*/
void resolveConstructorInitializers(ClassElementImpl classElement) {
for (ConstructorElementImpl constructor in constructors.values) {
for (ConstructorInitializer initializer
in constructor.constantInitializers) {
if (initializer is ConstructorFieldInitializer) {
SimpleIdentifier nameNode = initializer.fieldName;
nameNode.staticElement = fields[nameNode.name];
} else if (initializer is SuperConstructorInvocation) {
SimpleIdentifier nameNode = initializer.constructorName;
ConstructorElement element = new _DeferredConstructorElement(
classElement.supertype, nameNode?.name ?? '');
initializer.staticElement = element;
nameNode?.staticElement = element;
} else if (initializer is RedirectingConstructorInvocation) {
SimpleIdentifier nameNode = initializer.constructorName;
ConstructorElement element = constructors[nameNode?.name ?? ''];
initializer.staticElement = element;
nameNode?.staticElement = element;
}
}
}
}
Expression _buildConstExpression(UnlinkedConst uc) {
return new _ConstExprBuilder(this, uc).build();
}
/**
* Return the new handle of the `String.length` getter element.
*/
PropertyAccessorElementHandle _buildStringLengthPropertyAccessorElement() =>
new PropertyAccessorElementHandle(
summaryResynthesizer,
new ElementLocationImpl.con3(
<String>['dart:core', 'dart:core', 'String', 'length?']));
/**
* Return the defining type for a [ConstructorElement] by applying
* [typeArgumentRefs] to the given linked [info].
*/
InterfaceType _createConstructorDefiningType(
_ReferenceInfo info, List<EntityRef> typeArgumentRefs) {
bool isClass = info.element is ClassElement;
_ReferenceInfo classInfo = isClass ? info : info.enclosing;
List<DartType> typeArguments = typeArgumentRefs.map(buildType).toList();
return classInfo.buildType((i) {
if (i < typeArguments.length) {
return typeArguments[i];
} else {
return DynamicTypeImpl.instance;
}
}, const <int>[]);
}
/**
* Return the [ConstructorElement] corresponding to the given linked [info],
* using the [classType] which has already been computed (e.g. by
* [_createConstructorDefiningType]). Both cases when [info] is a
* [ClassElement] and [ConstructorElement] are supported.
*/
ConstructorElement _createConstructorElement(
InterfaceType classType, _ReferenceInfo info) {
bool isClass = info.element is ClassElement;
String name = isClass ? '' : info.name;
_DeferredConstructorElement element =
new _DeferredConstructorElement(classType, name);
if (info.numTypeParameters != 0) {
return new ConstructorMember(element, classType);
} else {
return element;
}
}
/**
* If the given [kind] is a top-level or class member property accessor, and
* the given [name] does not end with `=`, i.e. does not denote a setter,
* return the getter identifier by appending `?`.
*/
static String _getElementIdentifier(String name, ReferenceKind kind) {
if (kind == ReferenceKind.topLevelPropertyAccessor ||
kind == ReferenceKind.propertyAccessor) {
if (!name.endsWith('=')) {
return name + '?';
}
}
return name;
}
}
/**
* Data structure used during resynthesis to record all the information that is
* known about how to resynthesize a single entry in [LinkedUnit.references]
* (and its associated entry in [UnlinkedUnit.references], if it exists).
*/
class _ReferenceInfo {
/**
* The enclosing [_ReferenceInfo], or `null` for top-level elements.
*/
final _ReferenceInfo enclosing;
/**
* The name of the entity referred to by this reference.
*/
final String name;
/**
* The element referred to by this reference, or `null` if there is no
* associated element (e.g. because it is a reference to an undefined
* entity).
*/
final Element element;
/**
* If this reference refers to a non-generic type, the type it refers to.
* Otherwise `null`.
*/
DartType type;
/**
* The number of type parameters accepted by the entity referred to by this
* reference, or zero if it doesn't accept any type parameters.
*/
final int numTypeParameters;
/**
* Create a new [_ReferenceInfo] object referring to an element called [name]
* via the element handle [element], and having [numTypeParameters] type
* parameters.
*
* For the special types `dynamic` and `void`, [specialType] should point to
* the type itself. Otherwise, pass `null` and the type will be computed
* when appropriate.
*/
_ReferenceInfo(this.enclosing, this.name, this.element, DartType specialType,
this.numTypeParameters) {
if (specialType != null) {
type = specialType;
} else {
type = _buildType((_) => DynamicTypeImpl.instance, const []);
}
}
/**
* Build a [DartType] corresponding to the result of applying some type
* arguments to the entity referred to by this [_ReferenceInfo]. The type
* arguments are retrieved by calling [getTypeArgument].
*
* If [implicitFunctionTypeIndices] is not empty, a [DartType] should be
* created which refers to a function type implicitly defined by one of the
* element's parameters. [implicitFunctionTypeIndices] is interpreted as in
* [EntityRef.implicitFunctionTypeIndices].
*
* If the entity referred to by this [_ReferenceInfo] is not a type, `null`
* is returned.
*/
DartType buildType(
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
DartType result =
(numTypeParameters == 0 && implicitFunctionTypeIndices.isEmpty)
? type
: _buildType(getTypeArgument, implicitFunctionTypeIndices);
if (result == null) {
// TODO(paulberry): figure out how to handle this case (which should
// only occur in the event of erroneous code).
throw new UnimplementedError();
}
return result;
}
/**
* If this reference refers to a type, build a [DartType] which instantiates
* it with type arguments returned by [getTypeArgument]. Otherwise return
* `null`.
*
* If [implicitFunctionTypeIndices] is not null, a [DartType] should be
* created which refers to a function type implicitly defined by one of the
* element's parameters. [implicitFunctionTypeIndices] is interpreted as in
* [EntityRef.implicitFunctionTypeIndices].
*/
DartType _buildType(
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
ElementHandle element = this.element; // To allow type promotion
if (element is ClassElementHandle) {
return new InterfaceTypeImpl.elementWithNameAndArgs(element, name,
_buildTypeArguments(numTypeParameters, getTypeArgument));
} else if (element is FunctionTypeAliasElementHandle) {
return new FunctionTypeImpl.elementWithNameAndArgs(
element,
name,
_buildTypeArguments(numTypeParameters, getTypeArgument),
numTypeParameters != 0);
} else if (element is FunctionTypedElement) {
int numTypeArguments;
FunctionTypedElementComputer computer;
if (implicitFunctionTypeIndices.isNotEmpty) {
numTypeArguments = numTypeParameters;
computer = () {
FunctionTypedElement element = this.element;
for (int index in implicitFunctionTypeIndices) {
element = element.parameters[index].type.element;
}
return element;
};
} else {
// For a type that refers to a generic executable, the type arguments are
// not supposed to include the arguments to the executable itself.
numTypeArguments = enclosing == null ? 0 : enclosing.numTypeParameters;
computer = () => this.element;
}
// TODO(paulberry): Is it a bug that we have to pass `false` for
// isInstantiated?
return new DeferredFunctionTypeImpl(computer, null,
_buildTypeArguments(numTypeArguments, getTypeArgument), false);
} else {
return null;
}
}
/**
* Build a list of type arguments having length [numTypeArguments] where each
* type argument is obtained by calling [getTypeArgument].
*/
List<DartType> _buildTypeArguments(
int numTypeArguments, DartType getTypeArgument(int i)) {
List<DartType> typeArguments = const <DartType>[];
if (numTypeArguments != 0) {
typeArguments = <DartType>[];
for (int i = 0; i < numTypeArguments; i++) {
typeArguments.add(getTypeArgument(i));
}
}
return typeArguments;
}
}