Chromium Code Reviews| Index: pkg/analyzer/lib/src/summary/resynthesize.dart |
| diff --git a/pkg/analyzer/lib/src/summary/resynthesize.dart b/pkg/analyzer/lib/src/summary/resynthesize.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fdb87ca2b3fe672424294271f171025325b9bde9 |
| --- /dev/null |
| +++ b/pkg/analyzer/lib/src/summary/resynthesize.dart |
| @@ -0,0 +1,941 @@ |
| +// 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 'package:analyzer/analyzer.dart'; |
| +import 'package:analyzer/src/generated/element.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/summary/format.dart'; |
| + |
| +/** |
| + * Callback used by [SummaryResynthesizer] to obtain the summary for a given |
| + * URI. |
| + */ |
| +typedef PrelinkedLibrary GetSummaryCallback(String uri); |
| + |
| +/** |
| + * Specialization of [FunctionTypeImpl] used for function types resynthesized |
| + * from summaries. |
| + */ |
| +class ResynthesizedFunctionTypeImpl extends FunctionTypeImpl |
| + with ResynthesizedType { |
| + final SummaryResynthesizer summaryResynthesizer; |
| + |
| + ResynthesizedFunctionTypeImpl( |
| + FunctionTypeAliasElement element, String name, this.summaryResynthesizer) |
| + : super.elementWithName(element, name); |
| + |
| + int get _numTypeParameters { |
| + FunctionTypeAliasElement element = this.element; |
| + return element.typeParameters.length; |
| + } |
| +} |
| + |
| +/** |
| + * Specialization of [InterfaceTypeImpl] used for interface types resynthesized |
| + * from summaries. |
| + */ |
| +class ResynthesizedInterfaceTypeImpl extends InterfaceTypeImpl |
| + with ResynthesizedType { |
| + final SummaryResynthesizer summaryResynthesizer; |
| + |
| + ResynthesizedInterfaceTypeImpl( |
| + ClassElement element, String name, this.summaryResynthesizer) |
| + : super.elementWithName(element, name); |
| + |
| + int get _numTypeParameters => element.typeParameters.length; |
| +} |
| + |
| +/** |
| + * Common code for types resynthesized from summaries. This code takes care of |
| + * filling in the appropriate number of copies of `dynamic` when it is queried |
| + * for type parameters on a bare type reference (i.e. it converts `List` to |
| + * `List<dynamic>`). |
| + */ |
| +abstract class ResynthesizedType implements DartType { |
| + /** |
| + * The type arguments, if known. Otherwise `null`. |
| + */ |
| + List<DartType> _typeArguments; |
| + |
| + SummaryResynthesizer get summaryResynthesizer; |
| + |
| + List<DartType> get typeArguments { |
| + if (_typeArguments == null) { |
| + // Default to replicating "dynamic" as many times as the class element |
| + // requires. |
| + _typeArguments = new List<DartType>.filled( |
| + _numTypeParameters, summaryResynthesizer.typeProvider.dynamicType); |
| + } |
| + return _typeArguments; |
| + } |
| + |
| + int get _numTypeParameters; |
| +} |
| + |
| +/** |
| + * Implementation of [ElementResynthesizer] used when resynthesizing an element |
| + * model from summaries. |
| + */ |
| +class SummaryResynthesizer extends ElementResynthesizer { |
| + /** |
| + * Callback used to obtain the summary for a given URI. |
| + */ |
| + final GetSummaryCallback getSummary; |
| + |
| + /** |
| + * 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. |
| + * |
| + * TODO(paulberry): will this create a chicken-and-egg problem when trying to |
| + * resynthesize the core library from summaries? |
| + */ |
| + final TypeProvider typeProvider; |
| + |
| + /** |
| + * 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( |
| + AnalysisContext context, this.getSummary, this.sourceFactory) |
| + : super(context), |
| + typeProvider = context.typeProvider; |
| + |
| + /** |
| + * Number of libraries that have been resynthesized so far. |
| + */ |
| + int get resynthesisCount => _resynthesizedLibraries.length; |
| + |
| + @override |
| + Element getElement(ElementLocation location) { |
| + if (location.components.length == 1) { |
| + return getLibraryElement(location.components[0]); |
| + } else if (location.components.length == 3) { |
| + String uri = location.components[0]; |
| + Map<String, Map<String, Element>> libraryMap = |
| + _resynthesizedElements[uri]; |
| + if (libraryMap == null) { |
| + getLibraryElement(uri); |
| + libraryMap = _resynthesizedElements[uri]; |
| + assert(libraryMap != null); |
| + } |
| + Element element = libraryMap[location.components[1]] |
|
scheglov
2015/12/16 01:46:49
Can this expression be null?
Paul Berry
2015/12/16 16:34:20
Good point. It could happen, if files are updated
|
| + [location.components[2]]; |
| + if (element == null) { |
| + throw new Exception('Failed to resynthesize $location'); |
| + } |
| + assert(element != null); |
| + 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) { |
| + return _resynthesizedLibraries.putIfAbsent(uri, () { |
| + PrelinkedLibrary serializedLibrary = getSummary(uri); |
| + _LibraryResynthesizer libraryResynthesizer = |
| + new _LibraryResynthesizer(this, serializedLibrary, _getSource(uri)); |
| + LibraryElement library = libraryResynthesizer.buildLibrary(); |
| + _resynthesizedElements[uri] = libraryResynthesizer.resummarizedElements; |
| + return library; |
| + }); |
| + } |
| + |
| + /** |
| + * Get the [Source] object for the given [uri]. |
| + */ |
| + Source _getSource(String uri) { |
| + return _sources.putIfAbsent(uri, () => sourceFactory.forUri(uri)); |
| + } |
| +} |
| + |
| +/** |
| + * 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; |
| + |
| + /** |
| + * The library to be resynthesized. |
| + */ |
| + final PrelinkedLibrary prelinkedLibrary; |
| + |
| + /** |
| + * [Source] object for the library to be resynthesized. |
| + */ |
| + final Source librarySource; |
| + |
| + /** |
| + * [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 [PrelinkedUnit] from which elements are currently being resynthesized. |
| + */ |
| + PrelinkedUnit prelinkedUnit; |
| + |
| + /** |
| + * 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>> resummarizedElements = |
| + <String, Map<String, Element>>{}; |
| + |
| + /** |
| + * Type parameters for the class or typedef currently being resynthesized. |
| + * |
| + * TODO(paulberry): extend this to do the right thing for generic methods. |
| + */ |
| + List<TypeParameterElement> currentTypeParameters; |
| + |
| + _LibraryResynthesizer(this.summaryResynthesizer, |
| + PrelinkedLibrary serializedLibrary, this.librarySource) |
| + : prelinkedLibrary = serializedLibrary; |
| + |
| + /** |
| + * Resynthesize a [ClassElement] and place it in [unitHolder]. |
| + */ |
| + void buildClass(UnlinkedClass serializedClass) { |
| + try { |
| + currentTypeParameters = |
| + serializedClass.typeParameters.map(buildTypeParameter).toList(); |
| + for (int i = 0; i < serializedClass.typeParameters.length; i++) { |
| + finishTypeParameter( |
| + serializedClass.typeParameters[i], currentTypeParameters[i]); |
| + } |
| + ClassElementImpl classElement; |
| + if (serializedClass.isMixinApplication) { |
| + classElement = new ClassElementImpl(serializedClass.name, -1); |
| + classElement.setModifier(Modifier.MIXIN_APPLICATION, true); |
| + } else { |
| + classElement = new ClassElementImpl(serializedClass.name, -1); |
|
Brian Wilkerson
2015/12/16 00:29:07
Unless I'm missing something, this line is inside
Paul Berry
2015/12/16 16:34:20
Oops, you're right. Fixed.
|
| + } |
| + InterfaceTypeImpl correspondingType = new InterfaceTypeImpl(classElement); |
| + if (serializedClass.supertype != null) { |
| + classElement.supertype = buildType(serializedClass.supertype); |
| + } else { |
| + // TODO(paulberry): don't make Object point to itself. |
| + classElement.supertype = summaryResynthesizer.typeProvider.objectType; |
| + } |
| + classElement.interfaces = |
| + serializedClass.interfaces.map(buildType).toList(); |
| + classElement.mixins = serializedClass.mixins.map(buildType).toList(); |
| + classElement.typeParameters = currentTypeParameters; |
| + ElementHolder memberHolder = new ElementHolder(); |
| + bool constructorFound = false; |
| + for (UnlinkedExecutable serializedExecutable |
| + in serializedClass.executables) { |
| + switch (serializedExecutable.kind) { |
| + case UnlinkedExecutableKind.constructor: |
| + constructorFound = true; |
| + buildConstructor(serializedExecutable, memberHolder); |
| + break; |
| + case UnlinkedExecutableKind.functionOrMethod: |
| + case UnlinkedExecutableKind.getter: |
| + case UnlinkedExecutableKind.setter: |
| + buildExecutable(serializedExecutable, memberHolder); |
| + break; |
| + } |
| + } |
| + for (UnlinkedVariable serializedVariable in serializedClass.fields) { |
| + buildVariable(serializedVariable, memberHolder); |
| + } |
| + if (!serializedClass.isMixinApplication) { |
| + if (!constructorFound) { |
| + // Synthesize implicit constructors. |
| + ConstructorElementImpl constructor = |
| + new ConstructorElementImpl('', -1); |
| + constructor.setModifier(Modifier.SYNTHETIC, true); |
| + constructor.returnType = correspondingType; |
| + constructor.type = new FunctionTypeImpl(constructor); |
| + memberHolder.addConstructor(constructor); |
| + } |
| + classElement.constructors = memberHolder.constructors; |
| + } |
| + classElement.accessors = memberHolder.accessors; |
| + classElement.fields = memberHolder.fields; |
| + classElement.methods = memberHolder.methods; |
| + correspondingType.typeArguments = |
| + currentTypeParameters.map((param) => param.type).toList(); |
| + classElement.type = correspondingType; |
| + unitHolder.addType(classElement); |
| + } finally { |
| + currentTypeParameters = null; |
| + } |
| + } |
| + |
| + /** |
| + * Resynthesize a [NamespaceCombinator]. |
| + */ |
| + NamespaceCombinator buildCombinator(UnlinkedCombinator serializedCombinator) { |
| + if (serializedCombinator.shows.isNotEmpty) { |
| + ShowElementCombinatorImpl combinator = new ShowElementCombinatorImpl(); |
| + combinator.shownNames = serializedCombinator.shows |
| + .map((UnlinkedCombinatorName n) => n.name) |
| + .toList(); |
| + return combinator; |
| + } else { |
| + HideElementCombinatorImpl combinator = new HideElementCombinatorImpl(); |
| + combinator.hiddenNames = serializedCombinator.hides |
| + .map((UnlinkedCombinatorName n) => n.name) |
| + .toList(); |
| + return combinator; |
| + } |
| + } |
| + |
| + /** |
| + * Resynthesize a [ConstructorElement] and place it in the given [holder]. |
| + */ |
| + void buildConstructor( |
| + UnlinkedExecutable serializedExecutable, ElementHolder holder) { |
| + assert(serializedExecutable.kind == UnlinkedExecutableKind.constructor); |
| + ConstructorElementImpl constructorElement = |
| + new ConstructorElementImpl(serializedExecutable.name, -1); |
| + buildExecutableCommonParts(constructorElement, serializedExecutable); |
| + if (serializedExecutable.isFactory) { |
| + constructorElement.setModifier(Modifier.FACTORY, true); |
|
scheglov
2015/12/16 01:46:49
ConstructorElementImpl has a setter "factory".
...
Paul Berry
2015/12/16 16:34:20
Good point. I'll do a follow-up commit that repla
|
| + } |
| + if (serializedExecutable.isConst) { |
| + constructorElement.setModifier(Modifier.CONST, true); |
| + } |
| + holder.addConstructor(constructorElement); |
| + } |
| + |
| + /** |
| + * Resynthesize the [ClassElement] corresponding to an enum, along with the |
| + * associated fields and implicit accessors. |
| + */ |
| + void buildEnum(UnlinkedEnum serializedEnum) { |
| + ClassElementImpl classElement = |
| + new ClassElementImpl(serializedEnum.name, -1); |
|
Brian Wilkerson
2015/12/16 00:29:07
I assume that we'll get offsets at some point.
Paul Berry
2015/12/16 16:34:20
Yes, that's the plan. I've added a TODO comment t
|
| + classElement.setModifier(Modifier.ENUM, true); |
| + InterfaceType enumType = new InterfaceTypeImpl(classElement); |
| + classElement.type = enumType; |
| + classElement.supertype = summaryResynthesizer.typeProvider.objectType; |
| + ElementHolder memberHolder = new ElementHolder(); |
| + FieldElementImpl indexField = new FieldElementImpl('index', -1); |
| + indexField.setModifier(Modifier.FINAL, true); |
| + indexField.setModifier(Modifier.SYNTHETIC, true); |
| + indexField.type = summaryResynthesizer.typeProvider.intType; |
| + memberHolder.addField(indexField); |
| + buildImplicitAccessors(indexField, memberHolder); |
| + FieldElementImpl valuesField = new ConstFieldElementImpl('values', -1); |
| + valuesField.setModifier(Modifier.SYNTHETIC, true); |
| + valuesField.setModifier(Modifier.CONST, true); |
| + valuesField.setModifier(Modifier.STATIC, true); |
| + valuesField.type = summaryResynthesizer.typeProvider.listType |
| + .substitute4(<DartType>[enumType]); |
| + memberHolder.addField(valuesField); |
| + buildImplicitAccessors(valuesField, memberHolder); |
| + for (UnlinkedEnumValue serializedEnumValue in serializedEnum.values) { |
| + ConstFieldElementImpl valueField = |
| + new ConstFieldElementImpl(serializedEnumValue.name, -1); |
| + valueField.setModifier(Modifier.CONST, true); |
| + valueField.setModifier(Modifier.STATIC, true); |
| + valueField.type = enumType; |
| + memberHolder.addField(valueField); |
| + buildImplicitAccessors(valueField, memberHolder); |
| + } |
| + 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; |
| + } |
| + String name = serializedExecutable.name; |
| + if (name.endsWith('=')) { |
| + name = name.substring(0, name.length - 1); |
| + } |
| + UnlinkedExecutableKind kind = serializedExecutable.kind; |
| + switch (kind) { |
| + case UnlinkedExecutableKind.functionOrMethod: |
| + if (isTopLevel) { |
| + FunctionElementImpl executableElement = |
| + new FunctionElementImpl(name, -1); |
| + buildExecutableCommonParts(executableElement, serializedExecutable); |
| + holder.addFunction(executableElement); |
| + } else { |
| + MethodElementImpl executableElement = new MethodElementImpl(name, -1); |
| + buildExecutableCommonParts(executableElement, serializedExecutable); |
| + executableElement.setModifier( |
| + Modifier.STATIC, serializedExecutable.isStatic); |
| + holder.addMethod(executableElement); |
| + } |
| + break; |
| + case UnlinkedExecutableKind.getter: |
| + case UnlinkedExecutableKind.setter: |
| + PropertyAccessorElementImpl executableElement = |
| + new PropertyAccessorElementImpl(name, -1); |
| + if (isTopLevel) { |
| + executableElement.setModifier(Modifier.STATIC, true); |
| + } else { |
| + executableElement.setModifier( |
| + Modifier.STATIC, serializedExecutable.isStatic); |
| + } |
| + buildExecutableCommonParts(executableElement, serializedExecutable); |
| + DartType type; |
| + if (kind == UnlinkedExecutableKind.getter) { |
| + executableElement.setModifier(Modifier.GETTER, true); |
| + type = executableElement.returnType; |
| + } else { |
| + executableElement.setModifier(Modifier.SETTER, true); |
| + type = executableElement.parameters[0].type; |
| + } |
| + holder.addAccessor(executableElement); |
| + // TODO(paulberry): consider removing implicit variables from the |
| + // element model; the spec doesn't call for them, and they cause |
| + // trouble when getters/setters exist in different parts. |
| + PropertyInducingElementImpl implicitVariable; |
| + if (isTopLevel) { |
| + implicitVariable = buildImplicitTopLevelVariable(name, kind, holder); |
| + } else { |
| + implicitVariable = buildImplicitField(name, type, kind, holder); |
| + implicitVariable.setModifier( |
| + Modifier.STATIC, serializedExecutable.isStatic); |
| + } |
| + executableElement.variable = implicitVariable; |
| + if (kind == UnlinkedExecutableKind.getter) { |
| + implicitVariable.getter = executableElement; |
| + } else { |
| + implicitVariable.setter = executableElement; |
| + } |
| + // TODO(paulberry): do the right thing when getter and setter are in |
| + // different units. |
| + 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.parameters = |
| + serializedExecutable.parameters.map(buildParameter).toList(); |
| + if (serializedExecutable.returnType != null) { |
| + executableElement.returnType = buildType(serializedExecutable.returnType); |
| + } else { |
| + executableElement.returnType = VoidTypeImpl.instance; |
| + } |
| + executableElement.type = new FunctionTypeImpl(executableElement); |
| + if (serializedExecutable.hasImplicitReturnType) { |
| + executableElement.setModifier(Modifier.IMPLICIT_TYPE, true); |
| + } |
| + } |
| + |
| + /** |
| + * Resynthesize an [ExportElement], |
| + */ |
| + ExportElement buildExport(UnlinkedExport serializedExport) { |
| + ExportElementImpl exportElement = new ExportElementImpl(0); |
| + String exportedLibraryUri = summaryResynthesizer.sourceFactory |
| + .resolveUri(librarySource, serializedExport.uri) |
| + .uri |
| + .toString(); |
| + exportElement.exportedLibrary = new LibraryElementHandle( |
| + summaryResynthesizer, |
| + new ElementLocationImpl.con3(<String>[exportedLibraryUri])); |
| + exportElement.uri = serializedExport.uri; |
| + exportElement.combinators = |
| + serializedExport.combinators.map(buildCombinator).toList(); |
| + return exportElement; |
| + } |
| + |
| + /** |
| + * Resynthesize a [FieldElement]. |
| + */ |
| + FieldElement buildField(UnlinkedVariable serializedField) { |
| + FieldElementImpl fieldElement = |
| + new FieldElementImpl(serializedField.name, -1); |
| + fieldElement.type = buildType(serializedField.type); |
| + if (serializedField.isConst) { |
| + fieldElement.setModifier(Modifier.CONST, true); |
| + } |
| + return fieldElement; |
| + } |
| + |
| + /** |
| + * 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, -1); |
| + getter.setModifier(Modifier.GETTER, true); |
| + getter.setModifier(Modifier.STATIC, element.isStatic); |
| + getter.setModifier(Modifier.SYNTHETIC, true); |
| + getter.returnType = type; |
| + getter.type = new FunctionTypeImpl(getter); |
| + getter.variable = element; |
| + holder.addAccessor(getter); |
| + element.getter = getter; |
| + if (!(element.isConst || element.isFinal)) { |
| + PropertyAccessorElementImpl setter = |
| + new PropertyAccessorElementImpl(name, -1); |
| + setter.setModifier(Modifier.SETTER, true); |
| + setter.setModifier(Modifier.STATIC, element.isStatic); |
| + setter.setModifier(Modifier.SYNTHETIC, true); |
| + setter.parameters = <ParameterElement>[ |
| + new ParameterElementImpl('_$name', -1) |
| + ..setModifier(Modifier.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]. |
| + */ |
| + PropertyInducingElementImpl buildImplicitField(String name, DartType type, |
| + UnlinkedExecutableKind kind, ElementHolder holder) { |
| + if (holder.getField(name) == null) { |
| + FieldElementImpl field = new FieldElementImpl(name, -1); |
| + field.setModifier(Modifier.SYNTHETIC, true); |
| + if (kind == UnlinkedExecutableKind.getter) { |
| + field.setModifier(Modifier.FINAL, true); |
| + } |
| + field.type = type; |
| + holder.addField(field); |
| + return field; |
| + } else { |
| + // TODO(paulberry): if adding a setter where there was previously |
| + // only a getter, remove "final" modifier. |
| + // TODO(paulberry): what if the getter and setter have a type mismatch? |
| + throw new UnimplementedError(); |
| + } |
| + } |
| + |
| + /** |
| + * 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) { |
| + if (holder.getTopLevelVariable(name) == null) { |
| + TopLevelVariableElementImpl variable = |
| + new TopLevelVariableElementImpl(name, -1); |
| + variable.setModifier(Modifier.SYNTHETIC, true); |
| + if (kind == UnlinkedExecutableKind.getter) { |
| + variable.setModifier(Modifier.FINAL, true); |
| + } |
| + holder.addTopLevelVariable(variable); |
| + return variable; |
| + } else { |
| + // TODO(paulberry): if adding a setter where there was previously |
| + // only a getter, remove "final" modifier. |
| + // TODO(paulberry): what if the getter and setter have a type mismatch? |
| + throw new UnimplementedError(); |
| + } |
| + } |
| + |
| + /** |
| + * Resynthesize an [ImportElement]. |
| + */ |
| + ImportElement buildImport(UnlinkedImport serializedImport, int dependency) { |
| + bool isSynthetic = serializedImport.isImplicit; |
| + // TODO(paulberry): it seems problematic for the offset to be 0 for |
| + // non-synthetic imports, since it is used to disambiguate location. |
| + ImportElementImpl importElement = |
| + new ImportElementImpl(isSynthetic ? -1 : serializedImport.offset); |
| + String absoluteUri = summaryResynthesizer.sourceFactory |
| + .resolveUri( |
| + librarySource, prelinkedLibrary.dependencies[dependency].uri) |
| + .uri |
| + .toString(); |
| + importElement.importedLibrary = new LibraryElementHandle( |
| + summaryResynthesizer, |
| + new ElementLocationImpl.con3(<String>[absoluteUri])); |
| + if (isSynthetic) { |
| + importElement.setModifier(Modifier.SYNTHETIC, true); |
| + } else { |
| + importElement.uri = serializedImport.uri; |
| + } |
| + if (serializedImport.prefixReference != 0) { |
| + UnlinkedReference serializedPrefix = prelinkedLibrary.units[0] |
| + .unlinked |
| + .references[serializedImport.prefixReference]; |
| + importElement.prefix = new PrefixElementImpl(serializedPrefix.name, -1); |
| + } |
| + importElement.combinators = |
| + serializedImport.combinators.map(buildCombinator).toList(); |
| + return importElement; |
| + } |
| + |
| + /** |
| + * Main entry point. Resynthesize the [LibraryElement] and return it. |
| + */ |
| + LibraryElement buildLibrary() { |
| + // TODO(paulberry): is it ok to pass -1 for offset and nameLength? |
| + LibraryElementImpl libraryElement = new LibraryElementImpl( |
| + summaryResynthesizer.context, |
| + prelinkedLibrary.units[0].unlinked.libraryName, |
| + -1, |
| + -1); |
| + CompilationUnitElementImpl definingCompilationUnit = |
| + new CompilationUnitElementImpl(librarySource.shortName); |
| + libraryElement.definingCompilationUnit = definingCompilationUnit; |
| + definingCompilationUnit.source = librarySource; |
| + definingCompilationUnit.librarySource = librarySource; |
| + List<CompilationUnitElement> parts = <CompilationUnitElement>[]; |
| + UnlinkedUnit unlinkedDefiningUnit = prelinkedLibrary.units[0].unlinked; |
| + assert( |
| + unlinkedDefiningUnit.parts.length + 1 == prelinkedLibrary.units.length); |
| + for (int i = 1; i < prelinkedLibrary.units.length; i++) { |
| + CompilationUnitElementImpl part = buildPart( |
| + unlinkedDefiningUnit.parts[i - 1].uri, |
| + prelinkedLibrary.units[i].unlinked); |
| + parts.add(part); |
| + } |
| + libraryElement.parts = parts; |
| + List<ImportElement> imports = <ImportElement>[]; |
| + for (int i = 0; i < unlinkedDefiningUnit.imports.length; i++) { |
| + imports.add(buildImport(unlinkedDefiningUnit.imports[i], |
| + prelinkedLibrary.importDependencies[i])); |
| + } |
| + libraryElement.imports = imports; |
| + libraryElement.exports = |
| + unlinkedDefiningUnit.exports.map(buildExport).toList(); |
| + populateUnit(definingCompilationUnit, 0); |
| + for (int i = 0; i < parts.length; i++) { |
| + populateUnit(parts[i], i + 1); |
| + } |
| + return libraryElement; |
| + } |
| + |
| + /** |
| + * Resynthesize a [ParameterElement]. |
| + */ |
| + ParameterElement buildParameter(UnlinkedParam serializedParameter) { |
| + ParameterElementImpl parameterElement = |
| + new ParameterElementImpl(serializedParameter.name, -1); |
| + if (serializedParameter.isFunctionTyped) { |
| + FunctionElementImpl parameterTypeElement = |
| + new FunctionElementImpl('', -1); |
| + parameterTypeElement.setModifier(Modifier.SYNTHETIC, true); |
| + parameterElement.parameters = |
| + serializedParameter.parameters.map(buildParameter).toList(); |
| + parameterTypeElement.enclosingElement = parameterElement; |
| + parameterTypeElement.shareParameters(parameterElement.parameters); |
| + if (serializedParameter.type != null) { |
| + parameterTypeElement.returnType = buildType(serializedParameter.type); |
| + } else { |
| + parameterTypeElement.returnType = VoidTypeImpl.instance; |
| + } |
| + parameterElement.type = new FunctionTypeImpl(parameterTypeElement); |
| + } else { |
| + parameterElement.type = buildType(serializedParameter.type); |
| + if (serializedParameter.hasImplicitType) { |
| + parameterElement.setModifier(Modifier.IMPLICIT_TYPE, true); |
| + } |
| + } |
| + 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; |
| + } |
| + return parameterElement; |
| + } |
| + |
| + /** |
| + * Create, but do not populate, the [CompilationUnitElement] for a part other |
| + * than the defining compilation unit. |
| + */ |
| + CompilationUnitElementImpl buildPart( |
| + String uri, UnlinkedUnit serializedPart) { |
| + Source unitSource = |
| + summaryResynthesizer.sourceFactory.resolveUri(librarySource, uri); |
| + CompilationUnitElementImpl partUnit = |
| + new CompilationUnitElementImpl(unitSource.shortName); |
| + partUnit.source = unitSource; |
| + partUnit.librarySource = librarySource; |
| + partUnit.uri = uri; |
| + return partUnit; |
| + } |
| + |
| + /** |
| + * Build a [DartType] object based on an [UnlinkedTypeRef]. 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(UnlinkedTypeRef type) { |
| + if (type.paramReference != 0) { |
| + // TODO(paulberry): make this work for generic methods. |
| + return currentTypeParameters[ |
| + currentTypeParameters.length - type.paramReference].type; |
| + } else { |
| + // TODO(paulberry): handle references to things other than classes (note: |
| + // this should only occur in the case of erroneous code). |
| + // TODO(paulberry): test reference to something inside a part. |
| + // TODO(paulberry): test reference to something inside a part of the |
| + // current lib. |
| + UnlinkedReference reference = |
| + prelinkedUnit.unlinked.references[type.reference]; |
| + PrelinkedReference referenceResolution = |
| + prelinkedUnit.references[type.reference]; |
| + String referencedLibraryUri; |
| + String partUri; |
| + if (referenceResolution.dependency != 0) { |
| + PrelinkedDependency dependency = |
| + prelinkedLibrary.dependencies[referenceResolution.dependency]; |
| + Source referencedLibrarySource = summaryResynthesizer.sourceFactory |
| + .resolveUri(librarySource, dependency.uri); |
| + referencedLibraryUri = referencedLibrarySource.uri.toString(); |
| + PrelinkedLibrary referencedLibrary = |
| + summaryResynthesizer.getSummary(referencedLibraryUri); |
| + // TODO(paulberry): consider changing Location format so that this is |
| + // not necessary (2nd string in location should just be the unit |
| + // number). |
| + if (referenceResolution.unit != 0) { |
| + String uri = referencedLibrary.units[0].unlinked.parts[0].uri; |
| + Source partSource = summaryResynthesizer.sourceFactory |
| + .resolveUri(referencedLibrarySource, uri); |
| + partUri = partSource.uri.toString(); |
| + } else { |
| + partUri = referencedLibraryUri; |
| + } |
| + } else if (referenceResolution.kind == |
| + PrelinkedReferenceKind.unresolved) { |
| + return summaryResynthesizer.typeProvider.undefinedType; |
| + } else if (reference.name.isEmpty) { |
| + return summaryResynthesizer.typeProvider.dynamicType; |
| + } else { |
| + referencedLibraryUri = librarySource.uri.toString(); |
| + if (referenceResolution.unit != 0) { |
| + String uri = prelinkedLibrary.units[0].unlinked.parts[ |
| + referenceResolution.unit - 1].uri; |
| + Source partSource = |
| + summaryResynthesizer.sourceFactory.resolveUri(librarySource, uri); |
| + partUri = partSource.uri.toString(); |
| + } else { |
| + partUri = referencedLibraryUri; |
| + } |
| + } |
| + ResynthesizedType resynthesizedType; |
| + ElementLocationImpl location = new ElementLocationImpl.con3( |
| + <String>[referencedLibraryUri, partUri, reference.name]); |
| + switch (referenceResolution.kind) { |
| + case PrelinkedReferenceKind.classOrEnum: |
| + resynthesizedType = new ResynthesizedInterfaceTypeImpl( |
| + new ClassElementHandle(summaryResynthesizer, location), |
| + reference.name, |
| + summaryResynthesizer); |
| + break; |
| + case PrelinkedReferenceKind.typedef: |
| + resynthesizedType = new ResynthesizedFunctionTypeImpl( |
| + new FunctionTypeAliasElementHandle( |
| + summaryResynthesizer, location), |
| + reference.name, |
| + summaryResynthesizer); |
| + break; |
| + default: |
| + // TODO(paulberry): figure out how to handle this case (which should |
| + // only occur in the event of erroneous code). |
| + throw new UnimplementedError(); |
| + } |
| + if (type.typeArguments.isNotEmpty) { |
| + resynthesizedType._typeArguments = |
| + type.typeArguments.map(buildType).toList(); |
| + } |
| + return resynthesizedType; |
| + } |
| + } |
| + |
| + /** |
| + * Resynthesize a [FunctionTypeAliasElement] and place it in the |
| + * [unitHolder]. |
| + */ |
| + void buildTypedef(UnlinkedTypedef serializedTypedef) { |
| + try { |
| + currentTypeParameters = |
| + serializedTypedef.typeParameters.map(buildTypeParameter).toList(); |
| + for (int i = 0; i < serializedTypedef.typeParameters.length; i++) { |
| + finishTypeParameter( |
| + serializedTypedef.typeParameters[i], currentTypeParameters[i]); |
| + } |
| + FunctionTypeAliasElementImpl functionTypeAliasElement = |
| + new FunctionTypeAliasElementImpl(serializedTypedef.name, -1); |
| + functionTypeAliasElement.parameters = |
| + serializedTypedef.parameters.map(buildParameter).toList(); |
| + if (serializedTypedef.returnType != null) { |
| + functionTypeAliasElement.returnType = |
| + buildType(serializedTypedef.returnType); |
| + } else { |
| + functionTypeAliasElement.returnType = VoidTypeImpl.instance; |
| + } |
| + functionTypeAliasElement.type = |
| + new FunctionTypeImpl.forTypedef(functionTypeAliasElement); |
| + functionTypeAliasElement.typeParameters = currentTypeParameters; |
| + unitHolder.addTypeAlias(functionTypeAliasElement); |
| + } finally { |
| + currentTypeParameters = null; |
| + } |
| + } |
| + |
| + /** |
| + * 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, -1); |
| + typeParameterElement.type = new TypeParameterTypeImpl(typeParameterElement); |
| + return typeParameterElement; |
| + } |
| + |
| + /** |
| + * Resynthesize a [TopLevelVariableElement] or [FieldElement]. |
| + */ |
| + void buildVariable(UnlinkedVariable serializedVariable, |
| + [ElementHolder holder]) { |
| + if (holder == null) { |
| + TopLevelVariableElementImpl element = |
| + new TopLevelVariableElementImpl(serializedVariable.name, -1); |
| + buildVariableCommonParts(element, serializedVariable); |
| + unitHolder.addTopLevelVariable(element); |
| + buildImplicitAccessors(element, unitHolder); |
| + } else { |
| + FieldElementImpl element = |
| + new FieldElementImpl(serializedVariable.name, -1); |
| + buildVariableCommonParts(element, serializedVariable); |
| + holder.addField(element); |
| + buildImplicitAccessors(element, holder); |
| + } |
| + } |
| + |
| + /** |
| + * Handle the parts that are common to top level variables and fields. |
| + */ |
| + void buildVariableCommonParts(PropertyInducingElementImpl element, |
| + UnlinkedVariable serializedVariable) { |
| + element.type = buildType(serializedVariable.type); |
| + element.setModifier(Modifier.CONST, serializedVariable.isConst); |
| + element.setModifier(Modifier.STATIC, serializedVariable.isStatic); |
| + } |
| + |
| + /** |
| + * Finish creating a [TypeParameterElement] by deserializing its bound. |
| + */ |
| + void finishTypeParameter(UnlinkedTypeParam serializedTypeParameter, |
| + TypeParameterElementImpl typeParameterElement) { |
| + if (serializedTypeParameter.bound != null) { |
| + typeParameterElement.bound = buildType(serializedTypeParameter.bound); |
| + } |
| + } |
| + |
| + /** |
| + * Populate a [CompilationUnitElement] by deserializing all the elements |
| + * contained in it. |
| + */ |
| + void populateUnit(CompilationUnitElementImpl unit, int unitNum) { |
| + prelinkedUnit = prelinkedLibrary.units[unitNum]; |
| + unitHolder = new ElementHolder(); |
| + UnlinkedUnit unlinkedUnit = prelinkedUnit.unlinked; |
| + 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; |
| + } |
| + resummarizedElements[absoluteUri] = elementMap; |
| + unitHolder = null; |
| + prelinkedUnit = null; |
| + } |
| +} |