| // 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 serialization.elements; |
| |
| import 'dart:convert'; |
| |
| 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/ast.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| import 'package:analyzer/src/summary/format.dart'; |
| import 'package:analyzer/src/summary/idl.dart'; |
| import 'package:analyzer/src/summary/name_filter.dart'; |
| import 'package:analyzer/src/summary/summarize_const_expr.dart'; |
| import 'package:crypto/crypto.dart'; |
| |
| /** |
| * Serialize all the elements in [lib] to a summary using [ctx] as the context |
| * for building the summary, and using [typeProvider] to find built-in types. |
| */ |
| LibrarySerializationResult serializeLibrary( |
| LibraryElement lib, TypeProvider typeProvider, bool strongMode) { |
| _LibrarySerializer serializer = |
| new _LibrarySerializer(lib, typeProvider, strongMode); |
| LinkedLibraryBuilder linked = serializer.serializeLibrary(); |
| return new LibrarySerializationResult(linked, serializer.unlinkedUnits, |
| serializer.unitUris, serializer.unitSources); |
| } |
| |
| ReferenceKind _getReferenceKind(Element element) { |
| if (element == null || |
| element is ClassElement || |
| element is DynamicElementImpl) { |
| return ReferenceKind.classOrEnum; |
| } else if (element is ConstructorElement) { |
| return ReferenceKind.constructor; |
| } else if (element is FunctionElement) { |
| if (element.enclosingElement is CompilationUnitElement) { |
| return ReferenceKind.topLevelFunction; |
| } |
| return ReferenceKind.function; |
| } else if (element is FunctionTypeAliasElement) { |
| return ReferenceKind.typedef; |
| } else if (element is PropertyAccessorElement) { |
| if (element.enclosingElement is ClassElement) { |
| return ReferenceKind.propertyAccessor; |
| } |
| return ReferenceKind.topLevelPropertyAccessor; |
| } else if (element is MethodElement) { |
| return ReferenceKind.method; |
| } else if (element is TopLevelVariableElement) { |
| // Summaries don't need to distinguish between references to a variable and |
| // references to its getter. |
| return ReferenceKind.topLevelPropertyAccessor; |
| } else if (element is LocalVariableElement) { |
| return ReferenceKind.variable; |
| } else if (element is FieldElement) { |
| // Summaries don't need to distinguish between references to a field and |
| // references to its getter. |
| return ReferenceKind.propertyAccessor; |
| } else { |
| throw new Exception('Unexpected element kind: ${element.runtimeType}'); |
| } |
| } |
| |
| /** |
| * Type of closures used by [_LibrarySerializer] to defer generation of |
| * [EntityRefBuilder] objects until the end of serialization of a |
| * compilation unit. |
| */ |
| typedef EntityRefBuilder _SerializeTypeRef(); |
| |
| /** |
| * Data structure holding the result of serializing a [LibraryElement]. |
| */ |
| class LibrarySerializationResult { |
| /** |
| * Linked information the given library. |
| */ |
| final LinkedLibraryBuilder linked; |
| |
| /** |
| * Unlinked information for the compilation units constituting the library. |
| * The zeroth entry in the list is the defining compilation unit; the |
| * remaining entries are the parts, in the order listed in the defining |
| * compilation unit's part declarations. |
| */ |
| final List<UnlinkedUnitBuilder> unlinkedUnits; |
| |
| /** |
| * Absolute URI of each compilation unit appearing in the library. |
| */ |
| final List<String> unitUris; |
| |
| /** |
| * Source object corresponding to each compilation unit appearing in the |
| * library. |
| */ |
| final List<Source> unitSources; |
| |
| LibrarySerializationResult( |
| this.linked, this.unlinkedUnits, this.unitUris, this.unitSources); |
| } |
| |
| /** |
| * Object that gathers information uses it to assemble a new |
| * [PackageBundleBuilder]. |
| */ |
| class PackageBundleAssembler { |
| /** |
| * Value that will be stored in [PackageBundle.majorVersion] for any summaries |
| * created by this code. When making a breaking change to the summary format, |
| * this value should be incremented by 1 and [currentMinorVersion] should be |
| * reset to zero. |
| */ |
| static const int currentMajorVersion = 1; |
| |
| /** |
| * Value that will be stored in [PackageBundle.minorVersion] for any summaries |
| * created by this code. When making a non-breaking change to the summary |
| * format that clients might need to be aware of (such as adding a kind of |
| * data that was previously not summarized), this value should be incremented |
| * by 1. |
| */ |
| static const int currentMinorVersion = 0; |
| |
| final List<String> _linkedLibraryUris = <String>[]; |
| final List<LinkedLibraryBuilder> _linkedLibraries = <LinkedLibraryBuilder>[]; |
| final List<String> _unlinkedUnitUris = <String>[]; |
| final List<UnlinkedUnitBuilder> _unlinkedUnits = <UnlinkedUnitBuilder>[]; |
| final List<String> _unlinkedUnitHashes = <String>[]; |
| |
| /** |
| * Assemble a new [PackageBundleBuilder] using the gathered information. |
| */ |
| PackageBundleBuilder assemble() { |
| return new PackageBundleBuilder( |
| linkedLibraryUris: _linkedLibraryUris, |
| linkedLibraries: _linkedLibraries, |
| unlinkedUnitUris: _unlinkedUnitUris, |
| unlinkedUnits: _unlinkedUnits, |
| unlinkedUnitHashes: _unlinkedUnitHashes, |
| majorVersion: currentMajorVersion, |
| minorVersion: currentMinorVersion); |
| } |
| |
| /** |
| * Serialize the library with the given [element]. |
| */ |
| void serializeLibraryElement(LibraryElement element) { |
| String uri = element.source.uri.toString(); |
| LibrarySerializationResult libraryResult = serializeLibrary( |
| element, |
| element.context.typeProvider, |
| element.context.analysisOptions.strongMode); |
| _linkedLibraryUris.add(uri); |
| _linkedLibraries.add(libraryResult.linked); |
| _unlinkedUnitUris.addAll(libraryResult.unitUris); |
| _unlinkedUnits.addAll(libraryResult.unlinkedUnits); |
| for (Source source in libraryResult.unitSources) { |
| _unlinkedUnitHashes.add(_hash(source.contents.data)); |
| } |
| } |
| |
| /** |
| * Compute a hash of the given file contents. |
| */ |
| String _hash(String contents) { |
| MD5 md5 = new MD5(); |
| md5.add(UTF8.encode(contents)); |
| return CryptoUtils.bytesToHex(md5.close()); |
| } |
| } |
| |
| /** |
| * Instances of this class keep track of intermediate state during |
| * serialization of a single compilation unit. |
| */ |
| class _CompilationUnitSerializer { |
| /** |
| * The [_LibrarySerializer] which is serializing the library of which |
| * [compilationUnit] is a part. |
| */ |
| final _LibrarySerializer librarySerializer; |
| |
| /** |
| * The [CompilationUnitElement] being serialized. |
| */ |
| final CompilationUnitElement compilationUnit; |
| |
| /** |
| * The ordinal index of [compilationUnit] within the library, where 0 |
| * represents the defining compilation unit. |
| */ |
| final int unitNum; |
| |
| /** |
| * The final linked summary of the compilation unit. |
| */ |
| final LinkedUnitBuilder linkedUnit = new LinkedUnitBuilder(); |
| |
| /** |
| * The final unlinked summary of the compilation unit. |
| */ |
| final UnlinkedUnitBuilder unlinkedUnit = new UnlinkedUnitBuilder(); |
| |
| /** |
| * Absolute URI of the compilation unit. |
| */ |
| String unitUri; |
| |
| /** |
| * Map from [Element] to the index of the entry in the "references table" |
| * that refers to it. |
| */ |
| final Map<Element, int> referenceMap = <Element, int>{}; |
| |
| /** |
| * The unlinked portion of the "references table". This is the list of |
| * objects which should be written to [UnlinkedUnit.references]. |
| */ |
| List<UnlinkedReferenceBuilder> unlinkedReferences; |
| |
| /** |
| * The linked portion of the "references table". This is the list of |
| * objects which should be written to [LinkedUnit.references]. |
| */ |
| List<LinkedReferenceBuilder> linkedReferences; |
| |
| /** |
| * The number of slot ids which have been assigned to this compilation unit. |
| */ |
| int numSlots = 0; |
| |
| /** |
| * List of closures which should be invoked at the end of serialization of a |
| * compilation unit, to produce [LinkedUnit.types]. |
| */ |
| final List<_SerializeTypeRef> deferredLinkedTypes = <_SerializeTypeRef>[]; |
| |
| /** |
| * Index into the "references table" representing an unresolved reference, if |
| * such an index exists. `null` if no such entry has been made in the |
| * references table yet. |
| */ |
| int unresolvedReferenceIndex = null; |
| |
| /** |
| * Index into the "references table" representing the "bottom" type, if such |
| * an index exists. `null` if no such entry has been made in the references |
| * table yet. |
| */ |
| int bottomReferenceIndex = null; |
| |
| /** |
| * If `true`, we are currently generating linked references, so new |
| * references will be not stored in [unlinkedReferences]. |
| */ |
| bool buildingLinkedReferences = false; |
| |
| _CompilationUnitSerializer( |
| this.librarySerializer, this.compilationUnit, this.unitNum); |
| |
| /** |
| * Source object for the compilation unit. |
| */ |
| Source get unitSource => compilationUnit.source; |
| |
| /** |
| * Add all classes, enums, typedefs, executables, and top level variables |
| * from the given compilation unit [element] to the compilation unit summary. |
| * [unitNum] indicates the ordinal position of this compilation unit in the |
| * library. |
| */ |
| void addCompilationUnitElements() { |
| unlinkedReferences = <UnlinkedReferenceBuilder>[ |
| new UnlinkedReferenceBuilder() |
| ]; |
| linkedReferences = <LinkedReferenceBuilder>[ |
| new LinkedReferenceBuilder(kind: ReferenceKind.classOrEnum) |
| ]; |
| List<UnlinkedPublicNameBuilder> names = <UnlinkedPublicNameBuilder>[]; |
| for (PropertyAccessorElement accessor in compilationUnit.accessors) { |
| if (accessor.isPublic) { |
| names.add(new UnlinkedPublicNameBuilder( |
| kind: ReferenceKind.topLevelPropertyAccessor, |
| name: accessor.name, |
| numTypeParameters: accessor.typeParameters.length)); |
| } |
| } |
| for (ClassElement cls in compilationUnit.types) { |
| if (cls.isPublic) { |
| names.add(new UnlinkedPublicNameBuilder( |
| kind: ReferenceKind.classOrEnum, |
| name: cls.name, |
| numTypeParameters: cls.typeParameters.length, |
| members: serializeClassConstMembers(cls))); |
| } |
| } |
| for (ClassElement enm in compilationUnit.enums) { |
| if (enm.isPublic) { |
| names.add(new UnlinkedPublicNameBuilder( |
| kind: ReferenceKind.classOrEnum, name: enm.name)); |
| } |
| } |
| for (FunctionElement function in compilationUnit.functions) { |
| if (function.isPublic) { |
| names.add(new UnlinkedPublicNameBuilder( |
| kind: ReferenceKind.topLevelFunction, |
| name: function.name, |
| numTypeParameters: function.typeParameters.length)); |
| } |
| } |
| for (FunctionTypeAliasElement typedef |
| in compilationUnit.functionTypeAliases) { |
| if (typedef.isPublic) { |
| names.add(new UnlinkedPublicNameBuilder( |
| kind: ReferenceKind.typedef, |
| name: typedef.name, |
| numTypeParameters: typedef.typeParameters.length)); |
| } |
| } |
| if (unitNum == 0) { |
| LibraryElement libraryElement = librarySerializer.libraryElement; |
| if (libraryElement.name.isNotEmpty) { |
| LibraryElement libraryElement = librarySerializer.libraryElement; |
| unlinkedUnit.libraryName = libraryElement.name; |
| unlinkedUnit.libraryNameOffset = libraryElement.nameOffset; |
| unlinkedUnit.libraryNameLength = libraryElement.nameLength; |
| unlinkedUnit.libraryDocumentationComment = |
| serializeDocumentation(libraryElement); |
| unlinkedUnit.libraryAnnotations = serializeAnnotations(libraryElement); |
| } |
| unlinkedUnit.publicNamespace = new UnlinkedPublicNamespaceBuilder( |
| exports: libraryElement.exports.map(serializeExportPublic).toList(), |
| parts: libraryElement.parts |
| .map((CompilationUnitElement e) => e.uri) |
| .toList(), |
| names: names); |
| unlinkedUnit.exports = |
| libraryElement.exports.map(serializeExportNonPublic).toList(); |
| unlinkedUnit.imports = |
| libraryElement.imports.map(serializeImport).toList(); |
| unlinkedUnit.parts = libraryElement.parts |
| .map((CompilationUnitElement e) => new UnlinkedPartBuilder( |
| uriOffset: e.uriOffset, |
| uriEnd: e.uriEnd, |
| annotations: serializeAnnotations(e))) |
| .toList(); |
| } else { |
| // TODO(paulberry): we need to figure out a way to record library, part, |
| // import, and export declarations that appear in non-defining |
| // compilation units (even though such declarations are prohibited by the |
| // language), so that if the user makes code changes that cause a |
| // non-defining compilation unit to become a defining compilation unit, |
| // we can create a correct summary by simply re-linking. |
| unlinkedUnit.publicNamespace = |
| new UnlinkedPublicNamespaceBuilder(names: names); |
| } |
| unlinkedUnit.classes = compilationUnit.types.map(serializeClass).toList(); |
| unlinkedUnit.enums = compilationUnit.enums.map(serializeEnum).toList(); |
| unlinkedUnit.typedefs = |
| compilationUnit.functionTypeAliases.map(serializeTypedef).toList(); |
| List<UnlinkedExecutableBuilder> executables = |
| compilationUnit.functions.map(serializeExecutable).toList(); |
| for (PropertyAccessorElement accessor in compilationUnit.accessors) { |
| if (!accessor.isSynthetic) { |
| executables.add(serializeExecutable(accessor)); |
| } |
| } |
| unlinkedUnit.executables = executables; |
| List<UnlinkedVariableBuilder> variables = <UnlinkedVariableBuilder>[]; |
| for (PropertyAccessorElement accessor in compilationUnit.accessors) { |
| if (accessor.isSynthetic && accessor.isGetter) { |
| PropertyInducingElement variable = accessor.variable; |
| if (variable != null) { |
| assert(!variable.isSynthetic); |
| variables.add(serializeVariable(variable)); |
| } |
| } |
| } |
| unlinkedUnit.variables = variables; |
| unlinkedUnit.references = unlinkedReferences; |
| linkedUnit.references = linkedReferences; |
| unitUri = compilationUnit.source.uri.toString(); |
| } |
| |
| /** |
| * Create the [LinkedUnit.types] table based on deferred types that were |
| * found during [addCompilationUnitElements]. |
| */ |
| void createLinkedTypes() { |
| buildingLinkedReferences = true; |
| linkedUnit.types = deferredLinkedTypes |
| .map((_SerializeTypeRef closure) => closure()) |
| .toList(); |
| buildingLinkedReferences = false; |
| } |
| |
| /** |
| * Compute the appropriate De Bruijn index to represent the given type |
| * parameter [type]. |
| */ |
| int findTypeParameterIndex(TypeParameterType type, Element context) { |
| Element originalContext = context; |
| int index = 0; |
| while (context != null) { |
| List<TypeParameterElement> typeParameters; |
| if (context is ClassElement) { |
| typeParameters = context.typeParameters; |
| } else if (context is FunctionTypeAliasElement) { |
| typeParameters = context.typeParameters; |
| } else if (context is ExecutableElement) { |
| typeParameters = context.typeParameters; |
| } |
| if (typeParameters != null) { |
| for (int i = 0; i < typeParameters.length; i++) { |
| TypeParameterElement param = typeParameters[i]; |
| if (param == type.element) { |
| return index + typeParameters.length - i; |
| } |
| } |
| index += typeParameters.length; |
| } |
| context = context.enclosingElement; |
| } |
| throw new StateError( |
| 'Unbound type parameter $type (${originalContext?.location})'); |
| } |
| |
| /** |
| * Get the type arguments for the given [type], or `null` if the type has no |
| * type arguments. |
| * |
| * TODO(paulberry): consider adding an abstract getter to [DartType] to do |
| * this. |
| */ |
| List<DartType> getTypeArguments(DartType type) { |
| if (type is InterfaceType) { |
| return type.typeArguments; |
| } else if (type is FunctionType) { |
| return type.typeArguments; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Serialize annotations from the given [element]. If [element] has no |
| * annotations, the empty list is returned. |
| */ |
| List<UnlinkedConstBuilder> serializeAnnotations(Element element) { |
| if (element.metadata.isEmpty) { |
| return const <UnlinkedConstBuilder>[]; |
| } |
| return element.metadata.map((ElementAnnotationImpl a) { |
| _ConstExprSerializer serializer = new _ConstExprSerializer(this, null); |
| serializer.serializeAnnotation(a.annotationAst); |
| return serializer.toBuilder(); |
| }).toList(); |
| } |
| |
| /** |
| * Return the index of the entry in the references table |
| * ([LinkedLibrary.references]) used for the "bottom" type. A new entry is |
| * added to the table if necessary to satisfy the request. |
| */ |
| int serializeBottomReference() { |
| if (bottomReferenceIndex == null) { |
| // References to the "bottom" type are always implicit, since there is no |
| // way to explicitly refer to the "bottom" type. Therefore they should |
| // be stored only in the linked references table. |
| bottomReferenceIndex = linkedReferences.length; |
| linkedReferences.add(new LinkedReferenceBuilder( |
| name: '*bottom*', kind: ReferenceKind.classOrEnum)); |
| } |
| return bottomReferenceIndex; |
| } |
| |
| /** |
| * Serialize the given [classElement], creating an [UnlinkedClass]. |
| */ |
| UnlinkedClassBuilder serializeClass(ClassElement classElement) { |
| UnlinkedClassBuilder b = new UnlinkedClassBuilder(); |
| b.name = classElement.name; |
| b.nameOffset = classElement.nameOffset; |
| b.typeParameters = |
| classElement.typeParameters.map(serializeTypeParam).toList(); |
| if (classElement.supertype == null) { |
| b.hasNoSupertype = true; |
| } else if (!classElement.supertype.isObject) { |
| b.supertype = serializeTypeRef(classElement.supertype, classElement); |
| } |
| b.mixins = classElement.mixins |
| .map((InterfaceType t) => serializeTypeRef(t, classElement)) |
| .toList(); |
| b.interfaces = classElement.interfaces |
| .map((InterfaceType t) => serializeTypeRef(t, classElement)) |
| .toList(); |
| List<UnlinkedVariableBuilder> fields = <UnlinkedVariableBuilder>[]; |
| List<UnlinkedExecutableBuilder> executables = <UnlinkedExecutableBuilder>[]; |
| for (ConstructorElement executable in classElement.constructors) { |
| if (!executable.isSynthetic) { |
| executables.add(serializeExecutable(executable)); |
| } |
| } |
| for (MethodElement executable in classElement.methods) { |
| executables.add(serializeExecutable(executable)); |
| } |
| for (PropertyAccessorElement accessor in classElement.accessors) { |
| if (!accessor.isSynthetic) { |
| executables.add(serializeExecutable(accessor)); |
| } else if (accessor.isGetter) { |
| PropertyInducingElement field = accessor.variable; |
| if (field != null && !field.isSynthetic) { |
| fields.add(serializeVariable(field)); |
| } |
| } |
| } |
| b.fields = fields; |
| b.executables = executables; |
| b.isAbstract = classElement.isAbstract; |
| b.isMixinApplication = classElement.isMixinApplication; |
| b.documentationComment = serializeDocumentation(classElement); |
| b.annotations = serializeAnnotations(classElement); |
| return b; |
| } |
| |
| /** |
| * If [cls] is a class, return the list of its members available for |
| * constants - static constant fields, static methods and constructors. |
| * Otherwise return `null`. |
| */ |
| List<UnlinkedPublicNameBuilder> serializeClassConstMembers(ClassElement cls) { |
| if (cls.isMixinApplication) { |
| // Mixin application members can't be determined directly from the AST so |
| // we can't store them in UnlinkedPublicName. |
| // TODO(paulberry): find somewhere else to store them. |
| return null; |
| } |
| if (cls.kind == ElementKind.CLASS) { |
| List<UnlinkedPublicNameBuilder> bs = <UnlinkedPublicNameBuilder>[]; |
| for (FieldElement field in cls.fields) { |
| if (field.isStatic && field.isConst && field.isPublic) { |
| // TODO(paulberry): should numTypeParameters include class params? |
| bs.add(new UnlinkedPublicNameBuilder( |
| name: field.name, |
| kind: ReferenceKind.propertyAccessor, |
| numTypeParameters: 0)); |
| } |
| } |
| for (MethodElement method in cls.methods) { |
| if (method.isStatic && method.isPublic) { |
| // TODO(paulberry): should numTypeParameters include class params? |
| bs.add(new UnlinkedPublicNameBuilder( |
| name: method.name, |
| kind: ReferenceKind.method, |
| numTypeParameters: method.typeParameters.length)); |
| } |
| } |
| for (ConstructorElement constructor in cls.constructors) { |
| if (constructor.isPublic && constructor.name.isNotEmpty) { |
| // TODO(paulberry): should numTypeParameters include class params? |
| bs.add(new UnlinkedPublicNameBuilder( |
| name: constructor.name, |
| kind: ReferenceKind.constructor, |
| numTypeParameters: 0)); |
| } |
| } |
| return bs; |
| } |
| return null; |
| } |
| |
| /** |
| * Serialize the given [combinator] into an [UnlinkedCombinator]. |
| */ |
| UnlinkedCombinatorBuilder serializeCombinator( |
| NamespaceCombinator combinator) { |
| UnlinkedCombinatorBuilder b = new UnlinkedCombinatorBuilder(); |
| if (combinator is ShowElementCombinator) { |
| b.shows = combinator.shownNames; |
| b.offset = combinator.offset; |
| b.end = combinator.end; |
| } else if (combinator is HideElementCombinator) { |
| b.hides = combinator.hiddenNames; |
| } |
| return b; |
| } |
| |
| /** |
| * Serialize the given [expression], creating an [UnlinkedConstBuilder]. |
| */ |
| UnlinkedConstBuilder serializeConstExpr(Expression expression, |
| [Set<String> constructorParameterNames]) { |
| _ConstExprSerializer serializer = |
| new _ConstExprSerializer(this, constructorParameterNames); |
| serializer.serialize(expression); |
| return serializer.toBuilder(); |
| } |
| |
| /** |
| * Serialize documentation from the given [element], creating an |
| * [UnlinkedDocumentationComment]. |
| * |
| * If [element] has no documentation, `null` is returned. |
| */ |
| UnlinkedDocumentationCommentBuilder serializeDocumentation(Element element) { |
| if (element.documentationComment == null) { |
| return null; |
| } |
| return new UnlinkedDocumentationCommentBuilder( |
| text: element.documentationComment, |
| offset: element.docRange.offset, |
| length: element.docRange.length); |
| } |
| |
| /** |
| * Serialize the given [enumElement], creating an [UnlinkedEnum]. |
| */ |
| UnlinkedEnumBuilder serializeEnum(ClassElement enumElement) { |
| UnlinkedEnumBuilder b = new UnlinkedEnumBuilder(); |
| b.name = enumElement.name; |
| b.nameOffset = enumElement.nameOffset; |
| List<UnlinkedEnumValueBuilder> values = <UnlinkedEnumValueBuilder>[]; |
| for (FieldElement field in enumElement.fields) { |
| if (field.isConst && field.type.element == enumElement) { |
| values.add(new UnlinkedEnumValueBuilder( |
| name: field.name, |
| nameOffset: field.nameOffset, |
| documentationComment: serializeDocumentation(field))); |
| } |
| } |
| b.values = values; |
| b.documentationComment = serializeDocumentation(enumElement); |
| b.annotations = serializeAnnotations(enumElement); |
| return b; |
| } |
| |
| /** |
| * Serialize the given [executableElement], creating an [UnlinkedExecutable]. |
| */ |
| UnlinkedExecutableBuilder serializeExecutable( |
| ExecutableElement executableElement) { |
| UnlinkedExecutableBuilder b = new UnlinkedExecutableBuilder(); |
| b.name = executableElement.name; |
| b.nameOffset = executableElement.nameOffset; |
| if (executableElement is ConstructorElement) { |
| if (executableElement.name.isNotEmpty) { |
| b.nameEnd = executableElement.nameEnd; |
| b.periodOffset = executableElement.periodOffset; |
| } |
| } else { |
| if (!executableElement.hasImplicitReturnType) { |
| b.returnType = serializeTypeRef( |
| executableElement.type.returnType, executableElement); |
| } else if (!executableElement.isStatic) { |
| b.inferredReturnTypeSlot = |
| storeInferredType(executableElement.returnType, executableElement); |
| } |
| } |
| b.typeParameters = |
| executableElement.typeParameters.map(serializeTypeParam).toList(); |
| b.parameters = |
| executableElement.type.parameters.map(serializeParam).toList(); |
| if (executableElement is PropertyAccessorElement) { |
| if (executableElement.isGetter) { |
| b.kind = UnlinkedExecutableKind.getter; |
| } else { |
| b.kind = UnlinkedExecutableKind.setter; |
| } |
| } else if (executableElement is ConstructorElementImpl) { |
| b.kind = UnlinkedExecutableKind.constructor; |
| b.isConst = executableElement.isConst; |
| b.isFactory = executableElement.isFactory; |
| ConstructorElement redirectedConstructor = |
| executableElement.redirectedConstructor; |
| if (redirectedConstructor != null) { |
| b.isRedirectedConstructor = true; |
| if (executableElement.isFactory) { |
| InterfaceType returnType = redirectedConstructor is ConstructorMember |
| ? redirectedConstructor.definingType |
| : redirectedConstructor.enclosingElement.type; |
| EntityRefBuilder typeRef = |
| serializeTypeRef(returnType, executableElement); |
| if (redirectedConstructor.name.isNotEmpty) { |
| String name = redirectedConstructor.name; |
| int typeId = typeRef.reference; |
| LinkedReference typeLinkedRef = linkedReferences[typeId]; |
| int refId = serializeUnlinkedReference( |
| name, ReferenceKind.constructor, |
| unit: typeLinkedRef.unit, prefixReference: typeId); |
| b.redirectedConstructor = new EntityRefBuilder( |
| reference: refId, typeArguments: typeRef.typeArguments); |
| } else { |
| b.redirectedConstructor = typeRef; |
| } |
| } else { |
| b.redirectedConstructorName = redirectedConstructor.name; |
| } |
| } |
| if (executableElement.isConst && |
| executableElement.constantInitializers != null) { |
| Set<String> constructorParameterNames = |
| executableElement.parameters.map((p) => p.name).toSet(); |
| b.constantInitializers = executableElement.constantInitializers |
| .map((ConstructorInitializer initializer) => |
| serializeConstructorInitializer( |
| initializer, |
| (expr) => |
| serializeConstExpr(expr, constructorParameterNames))) |
| .toList(); |
| } |
| } else { |
| b.kind = UnlinkedExecutableKind.functionOrMethod; |
| } |
| b.isAbstract = executableElement.isAbstract; |
| b.isStatic = executableElement.isStatic && |
| executableElement.enclosingElement is ClassElement; |
| b.isExternal = executableElement.isExternal; |
| b.documentationComment = serializeDocumentation(executableElement); |
| b.annotations = serializeAnnotations(executableElement); |
| if (executableElement is FunctionElement) { |
| SourceRange visibleRange = executableElement.visibleRange; |
| if (visibleRange != null) { |
| b.visibleOffset = visibleRange.offset; |
| b.visibleLength = visibleRange.length; |
| } |
| } |
| b.localFunctions = |
| executableElement.functions.map(serializeExecutable).toList(); |
| b.localLabels = executableElement.labels.map(serializeLabel).toList(); |
| b.localVariables = |
| executableElement.localVariables.map(serializeVariable).toList(); |
| return b; |
| } |
| |
| /** |
| * Serialize the given [exportElement] into an [UnlinkedExportNonPublic]. |
| */ |
| UnlinkedExportNonPublicBuilder serializeExportNonPublic( |
| ExportElement exportElement) { |
| UnlinkedExportNonPublicBuilder b = new UnlinkedExportNonPublicBuilder(); |
| b.offset = exportElement.nameOffset; |
| b.uriOffset = exportElement.uriOffset; |
| b.uriEnd = exportElement.uriEnd; |
| b.annotations = serializeAnnotations(exportElement); |
| return b; |
| } |
| |
| /** |
| * Serialize the given [exportElement] into an [UnlinkedExportPublic]. |
| */ |
| UnlinkedExportPublicBuilder serializeExportPublic( |
| ExportElement exportElement) { |
| UnlinkedExportPublicBuilder b = new UnlinkedExportPublicBuilder(); |
| b.uri = exportElement.uri; |
| b.combinators = exportElement.combinators.map(serializeCombinator).toList(); |
| return b; |
| } |
| |
| /** |
| * Serialize the given [importElement] yielding an [UnlinkedImportBuilder]. |
| * Also, add linked information about it to the [linkedImports] list. |
| */ |
| UnlinkedImportBuilder serializeImport(ImportElement importElement) { |
| UnlinkedImportBuilder b = new UnlinkedImportBuilder(); |
| b.annotations = serializeAnnotations(importElement); |
| b.isDeferred = importElement.isDeferred; |
| b.combinators = importElement.combinators.map(serializeCombinator).toList(); |
| if (importElement.prefix != null) { |
| b.prefixReference = serializePrefix(importElement.prefix); |
| b.prefixOffset = importElement.prefix.nameOffset; |
| } |
| if (importElement.isSynthetic) { |
| b.isImplicit = true; |
| } else { |
| b.offset = importElement.nameOffset; |
| b.uri = importElement.uri; |
| b.uriOffset = importElement.uriOffset; |
| b.uriEnd = importElement.uriEnd; |
| } |
| return b; |
| } |
| |
| /** |
| * Serialize the given [label], creating an [UnlinkedLabelBuilder]. |
| */ |
| UnlinkedLabelBuilder serializeLabel(LabelElementImpl label) { |
| UnlinkedLabelBuilder b = new UnlinkedLabelBuilder(); |
| b.name = label.name; |
| b.nameOffset = label.nameOffset; |
| b.isOnSwitchMember = label.isOnSwitchMember; |
| b.isOnSwitchStatement = label.isOnSwitchStatement; |
| return b; |
| } |
| |
| /** |
| * Serialize the given [parameter] into an [UnlinkedParam]. |
| */ |
| UnlinkedParamBuilder serializeParam(ParameterElement parameter, |
| [Element context]) { |
| context ??= parameter; |
| UnlinkedParamBuilder b = new UnlinkedParamBuilder(); |
| b.name = parameter.name; |
| b.nameOffset = parameter.nameOffset >= 0 ? parameter.nameOffset : 0; |
| switch (parameter.parameterKind) { |
| case ParameterKind.REQUIRED: |
| b.kind = UnlinkedParamKind.required; |
| break; |
| case ParameterKind.POSITIONAL: |
| b.kind = UnlinkedParamKind.positional; |
| break; |
| case ParameterKind.NAMED: |
| b.kind = UnlinkedParamKind.named; |
| break; |
| } |
| b.annotations = serializeAnnotations(parameter); |
| b.isInitializingFormal = parameter.isInitializingFormal; |
| DartType type = parameter.type; |
| if (parameter.hasImplicitType) { |
| Element contextParent = context.enclosingElement; |
| if (!parameter.isInitializingFormal && |
| contextParent is ExecutableElement && |
| !contextParent.isStatic && |
| contextParent is! ConstructorElement) { |
| b.inferredTypeSlot = storeInferredType(type, context); |
| } |
| } else { |
| if (type is FunctionType && type.element.isSynthetic) { |
| b.isFunctionTyped = true; |
| b.type = serializeTypeRef(type.returnType, parameter); |
| b.parameters = type.parameters |
| .map((parameter) => serializeParam(parameter, context)) |
| .toList(); |
| } else { |
| b.type = serializeTypeRef(type, context); |
| } |
| } |
| if (parameter is ConstVariableElement) { |
| ConstVariableElement constParameter = parameter as ConstVariableElement; |
| Expression initializer = constParameter.constantInitializer; |
| if (initializer != null) { |
| b.defaultValue = serializeConstExpr(initializer); |
| b.defaultValueCode = parameter.defaultValueCode; |
| } |
| } |
| // TODO(scheglov) VariableMember.initializer is not implemented |
| if (parameter is! VariableMember && parameter.initializer != null) { |
| b.initializer = serializeExecutable(parameter.initializer); |
| } |
| { |
| SourceRange visibleRange = parameter.visibleRange; |
| if (visibleRange != null) { |
| b.visibleOffset = visibleRange.offset; |
| b.visibleLength = visibleRange.length; |
| } |
| } |
| return b; |
| } |
| |
| /** |
| * Serialize the given [prefix] into an index into the references table. |
| */ |
| int serializePrefix(PrefixElement element) { |
| return referenceMap.putIfAbsent(element, |
| () => serializeUnlinkedReference(element.name, ReferenceKind.prefix)); |
| } |
| |
| /** |
| * Compute the reference index which should be stored in a [EntityRef]. |
| */ |
| int serializeReferenceForType(DartType type) { |
| Element element = type.element; |
| LibraryElement dependentLibrary = element?.library; |
| if (dependentLibrary == null) { |
| if (type.isBottom) { |
| // References to the "bottom" type are always implicit, since there is |
| // no way to explicitly refer to the "bottom" type. Therefore they |
| // should always be linked. |
| assert(buildingLinkedReferences); |
| return serializeBottomReference(); |
| } |
| assert(type.isDynamic || type.isVoid); |
| if (type is UndefinedTypeImpl) { |
| return serializeUnresolvedReference(); |
| } |
| // Note: for a type which is truly `dynamic` or `void`, fall through to |
| // use [_getElementReferenceId]. |
| } |
| return _getElementReferenceId(element); |
| } |
| |
| /** |
| * Serialize the given [typedefElement], creating an [UnlinkedTypedef]. |
| */ |
| UnlinkedTypedefBuilder serializeTypedef( |
| FunctionTypeAliasElement typedefElement) { |
| UnlinkedTypedefBuilder b = new UnlinkedTypedefBuilder(); |
| b.name = typedefElement.name; |
| b.nameOffset = typedefElement.nameOffset; |
| b.typeParameters = |
| typedefElement.typeParameters.map(serializeTypeParam).toList(); |
| b.returnType = serializeTypeRef(typedefElement.returnType, typedefElement); |
| b.parameters = typedefElement.parameters.map(serializeParam).toList(); |
| b.documentationComment = serializeDocumentation(typedefElement); |
| b.annotations = serializeAnnotations(typedefElement); |
| return b; |
| } |
| |
| /** |
| * Serialize the given [typeParameter] into an [UnlinkedTypeParam]. |
| */ |
| UnlinkedTypeParamBuilder serializeTypeParam( |
| TypeParameterElement typeParameter) { |
| UnlinkedTypeParamBuilder b = new UnlinkedTypeParamBuilder(); |
| b.name = typeParameter.name; |
| b.nameOffset = typeParameter.nameOffset; |
| if (typeParameter.bound != null) { |
| b.bound = serializeTypeRef(typeParameter.bound, typeParameter); |
| } |
| b.annotations = serializeAnnotations(typeParameter); |
| return b; |
| } |
| |
| /** |
| * Serialize the given [type] into a [EntityRef]. If [slot] is provided, |
| * it should be included in the [EntityRef]. |
| * |
| * [context] is the element within which the [EntityRef] will be |
| * interpreted; this is used to serialize type parameters. |
| */ |
| EntityRefBuilder serializeTypeRef(DartType type, Element context, |
| {int slot}) { |
| if (slot != null) { |
| assert(buildingLinkedReferences); |
| } |
| EntityRefBuilder b = new EntityRefBuilder(slot: slot); |
| Element typeElement = type.element; |
| if (type is TypeParameterType) { |
| b.paramReference = findTypeParameterIndex(type, context); |
| } else if (type is FunctionType && |
| typeElement is FunctionElement && |
| typeElement.enclosingElement == null) { |
| b.syntheticReturnType = |
| serializeTypeRef(typeElement.returnType, typeElement); |
| b.syntheticParams = typeElement.parameters |
| .map((ParameterElement param) => serializeParam(param, context)) |
| .toList(); |
| } else { |
| if (type is FunctionType && |
| typeElement.enclosingElement is ParameterElement) { |
| // Code cannot refer to function types implicitly defined by parameters |
| // directly, so if we get here, we must be serializing a linked |
| // reference from type inference. |
| assert(buildingLinkedReferences); |
| ParameterElement parameterElement = typeElement.enclosingElement; |
| while (true) { |
| Element parent = parameterElement.enclosingElement; |
| if (parent is ExecutableElement) { |
| Element grandParent = parent.enclosingElement; |
| b.implicitFunctionTypeIndices |
| .insert(0, parent.parameters.indexOf(parameterElement)); |
| if (grandParent is ParameterElement) { |
| // Function-typed parameter inside a function-typed parameter. |
| parameterElement = grandParent; |
| continue; |
| } else { |
| // Function-typed parameter inside a top level function or method. |
| b.reference = _getElementReferenceId(parent); |
| break; |
| } |
| } else { |
| throw new StateError( |
| 'Unexpected element enclosing parameter: ${parent.runtimeType}'); |
| } |
| } |
| } else { |
| b.reference = serializeReferenceForType(type); |
| } |
| List<DartType> typeArguments = getTypeArguments(type); |
| if (typeArguments != null) { |
| // Trailing type arguments of type 'dynamic' should be omitted. |
| int numArgsToSerialize = typeArguments.length; |
| while (numArgsToSerialize > 0 && |
| typeArguments[numArgsToSerialize - 1].isDynamic) { |
| --numArgsToSerialize; |
| } |
| if (numArgsToSerialize > 0) { |
| List<EntityRefBuilder> serializedArguments = <EntityRefBuilder>[]; |
| for (int i = 0; i < numArgsToSerialize; i++) { |
| serializedArguments |
| .add(serializeTypeRef(typeArguments[i], context)); |
| } |
| b.typeArguments = serializedArguments; |
| } |
| } |
| } |
| return b; |
| } |
| |
| /** |
| * Create a new entry in the references table ([UnlinkedLibrary.references] |
| * and [LinkedLibrary.references]) representing an entity having the given |
| * [name] and [kind]. If [unit] is given, it is the index of the compilation |
| * unit containing the entity being referred to. If [prefixReference] is |
| * given, it indicates the entry in the references table for the prefix. |
| */ |
| int serializeUnlinkedReference(String name, ReferenceKind kind, |
| {int unit: 0, int prefixReference: 0}) { |
| assert(unlinkedReferences.length == linkedReferences.length); |
| int index = unlinkedReferences.length; |
| unlinkedReferences.add(new UnlinkedReferenceBuilder( |
| name: name, prefixReference: prefixReference)); |
| linkedReferences.add(new LinkedReferenceBuilder(kind: kind, unit: unit)); |
| return index; |
| } |
| |
| /** |
| * Return the index of the entry in the references table |
| * ([UnlinkedLibrary.references] and [LinkedLibrary.references]) used for |
| * unresolved references. A new entry is added to the table if necessary to |
| * satisfy the request. |
| */ |
| int serializeUnresolvedReference() { |
| // TODO(paulberry): in order for relinking to work, we need to record the |
| // name and prefix of the unresolved symbol. This is not (yet) encoded in |
| // the element model. For the moment we use a name that can't possibly |
| // ever exist. |
| if (unresolvedReferenceIndex == null) { |
| unresolvedReferenceIndex = |
| serializeUnlinkedReference('*unresolved*', ReferenceKind.unresolved); |
| } |
| return unresolvedReferenceIndex; |
| } |
| |
| /** |
| * Serialize the given [variable], creating an [UnlinkedVariable]. |
| */ |
| UnlinkedVariableBuilder serializeVariable(VariableElement variable) { |
| UnlinkedVariableBuilder b = new UnlinkedVariableBuilder(); |
| b.name = variable.name; |
| b.nameOffset = variable.nameOffset; |
| if (!variable.hasImplicitType) { |
| b.type = serializeTypeRef(variable.type, variable); |
| } |
| b.isStatic = variable.isStatic && variable.enclosingElement is ClassElement; |
| b.isFinal = variable.isFinal; |
| b.isConst = variable.isConst; |
| b.documentationComment = serializeDocumentation(variable); |
| b.annotations = serializeAnnotations(variable); |
| if (variable is ConstVariableElement) { |
| ConstVariableElement constVariable = variable as ConstVariableElement; |
| Expression initializer = constVariable.constantInitializer; |
| if (initializer != null) { |
| b.constExpr = serializeConstExpr(initializer); |
| } |
| } |
| if (variable is PropertyInducingElement) { |
| if (b.isFinal || b.isConst) { |
| b.propagatedTypeSlot = |
| storeLinkedType(variable.propagatedType, variable); |
| } else { |
| // Variable is not propagable. |
| assert(variable.propagatedType == null); |
| } |
| } |
| if (variable.hasImplicitType && |
| (variable.initializer != null || !variable.isStatic)) { |
| b.inferredTypeSlot = storeInferredType(variable.type, variable); |
| } |
| if (variable is LocalVariableElement) { |
| SourceRange visibleRange = variable.visibleRange; |
| if (visibleRange != null) { |
| b.visibleOffset = visibleRange.offset; |
| b.visibleLength = visibleRange.length; |
| } |
| } |
| // TODO(scheglov) VariableMember.initializer is not implemented |
| if (variable is! VariableMember && variable.initializer != null) { |
| b.initializer = serializeExecutable(variable.initializer); |
| } |
| return b; |
| } |
| |
| /** |
| * Create a slot id for the given [type] (which is an inferred type). If |
| * [type] is not `dynamic`, it is stored in [linkedTypes] so that once the |
| * compilation unit has been fully visited, it will be serialized into |
| * [LinkedUnit.types]. |
| * |
| * [context] is the element within which the slot id will appear; this is |
| * used to serialize type parameters. |
| */ |
| int storeInferredType(DartType type, Element context) { |
| return storeLinkedType(type.isDynamic ? null : type, context); |
| } |
| |
| /** |
| * Create a slot id for the given [type] (which may be either a propagated |
| * type or an inferred type). If [type] is not `null`, it is stored in |
| * [linkedTypes] so that once the compilation unit has been fully visited, |
| * it will be serialized to [LinkedUnit.types]. |
| * |
| * [context] is the element within which the slot id will appear; this is |
| * used to serialize type parameters. |
| */ |
| int storeLinkedType(DartType type, Element context) { |
| int slot = ++numSlots; |
| if (type != null) { |
| deferredLinkedTypes |
| .add(() => serializeTypeRef(type, context, slot: slot)); |
| } |
| return slot; |
| } |
| |
| int _getElementReferenceId(Element element) { |
| return referenceMap.putIfAbsent(element, () { |
| LibraryElement dependentLibrary = librarySerializer.libraryElement; |
| int unit = 0; |
| Element enclosingElement; |
| if (element != null) { |
| enclosingElement = element.enclosingElement; |
| if (enclosingElement is CompilationUnitElement) { |
| dependentLibrary = enclosingElement.library; |
| unit = dependentLibrary.units.indexOf(enclosingElement); |
| assert(unit != -1); |
| } |
| } |
| ReferenceKind kind = _getReferenceKind(element); |
| String name = element == null ? 'void' : element.name; |
| int index; |
| LinkedReferenceBuilder linkedReference; |
| if (buildingLinkedReferences) { |
| linkedReference = |
| new LinkedReferenceBuilder(kind: kind, unit: unit, name: name); |
| if (enclosingElement != null && |
| enclosingElement is! CompilationUnitElement) { |
| linkedReference.containingReference = |
| _getElementReferenceId(enclosingElement); |
| if (enclosingElement is ClassElement) { |
| // Nothing to do. |
| } else if (enclosingElement is ExecutableElement) { |
| if (element is FunctionElement) { |
| assert(enclosingElement.functions.contains(element)); |
| linkedReference.localIndex = |
| enclosingElement.functions.indexOf(element); |
| } else if (element is LocalVariableElement) { |
| assert(enclosingElement.localVariables.contains(element)); |
| linkedReference.localIndex = |
| enclosingElement.localVariables.indexOf(element); |
| } else { |
| throw new StateError( |
| 'Unexpected enclosed element type: ${element.runtimeType}'); |
| } |
| } else if (enclosingElement is VariableElement) { |
| assert(identical(enclosingElement.initializer, element)); |
| } else { |
| throw new StateError( |
| 'Unexpected enclosing element type: ${enclosingElement.runtimeType}'); |
| } |
| } |
| index = linkedReferences.length; |
| linkedReferences.add(linkedReference); |
| } else { |
| assert(unlinkedReferences.length == linkedReferences.length); |
| int prefixReference = 0; |
| Element enclosing = element?.enclosingElement; |
| if (enclosing == null || enclosing is CompilationUnitElement) { |
| // Figure out a prefix that may be used to refer to the given element. |
| // TODO(paulberry): to avoid subtle relinking inconsistencies we |
| // should use the actual prefix from the AST (a given type may be |
| // reachable via multiple prefixes), but sadly, this information is |
| // not recorded in the element model. |
| PrefixElement prefix = librarySerializer.prefixMap[element]; |
| if (prefix != null) { |
| prefixReference = serializePrefix(prefix); |
| } |
| } else { |
| prefixReference = _getElementReferenceId(enclosing); |
| } |
| index = serializeUnlinkedReference(name, kind, |
| prefixReference: prefixReference, unit: unit); |
| linkedReference = linkedReferences[index]; |
| } |
| linkedReference.dependency = |
| librarySerializer.serializeDependency(dependentLibrary); |
| if (element is TypeParameterizedElement) { |
| linkedReference.numTypeParameters += element.typeParameters.length; |
| } |
| return index; |
| }); |
| } |
| |
| int _getLengthPropertyReference(int prefix) { |
| return serializeUnlinkedReference('length', ReferenceKind.length, |
| prefixReference: prefix); |
| } |
| } |
| |
| /** |
| * Instances of this class keep track of intermediate state during |
| * serialization of a single constant [Expression]. |
| */ |
| class _ConstExprSerializer extends AbstractConstExprSerializer { |
| final _CompilationUnitSerializer serializer; |
| |
| /** |
| * If a constructor initializer expression is being serialized, the names of |
| * the constructor parameters. Otherwise `null`. |
| */ |
| final Set<String> constructorParameterNames; |
| |
| _ConstExprSerializer(this.serializer, this.constructorParameterNames); |
| |
| @override |
| bool isConstructorParameterName(String name) { |
| return constructorParameterNames?.contains(name) ?? false; |
| } |
| |
| @override |
| void serializeAnnotation(Annotation annotation) { |
| if (annotation.arguments == null) { |
| assert(annotation.constructorName == null); |
| serialize(annotation.name); |
| } else { |
| Identifier name = annotation.name; |
| Element nameElement = name.staticElement; |
| EntityRefBuilder constructor; |
| if (nameElement is ConstructorElement && name is PrefixedIdentifier) { |
| assert(annotation.constructorName == null); |
| constructor = serializeConstructorName( |
| new TypeName(name.prefix, null)..type = nameElement.returnType, |
| name.identifier); |
| } else if (nameElement is TypeDefiningElement) { |
| constructor = serializeConstructorName( |
| new TypeName(annotation.name, null)..type = nameElement.type, |
| annotation.constructorName); |
| } else { |
| throw new StateError('Unexpected annotation nameElement type:' |
| ' ${nameElement.runtimeType}'); |
| } |
| serializeInstanceCreation(constructor, annotation.arguments); |
| } |
| } |
| |
| @override |
| EntityRefBuilder serializeConstructorName( |
| TypeName type, SimpleIdentifier name) { |
| EntityRefBuilder typeRef = serializeType(type); |
| if (name == null) { |
| return typeRef; |
| } else { |
| LinkedReference typeLinkedRef = |
| serializer.linkedReferences[typeRef.reference]; |
| int refId = serializer.serializeUnlinkedReference( |
| name.name, |
| name.staticElement != null |
| ? ReferenceKind.constructor |
| : ReferenceKind.unresolved, |
| prefixReference: typeRef.reference, |
| unit: typeLinkedRef.unit); |
| return new EntityRefBuilder( |
| reference: refId, typeArguments: typeRef.typeArguments); |
| } |
| } |
| |
| EntityRefBuilder serializeIdentifier(Identifier identifier, |
| {int prefixReference: 0}) { |
| Element element = identifier.staticElement; |
| // Unresolved identifier. |
| if (element == null) { |
| int reference; |
| if (identifier is PrefixedIdentifier) { |
| int prefix = serializeIdentifier(identifier.prefix).reference; |
| reference = serializer.serializeUnlinkedReference( |
| identifier.identifier.name, ReferenceKind.unresolved, |
| prefixReference: prefix); |
| } else { |
| reference = serializer.serializeUnlinkedReference( |
| identifier.name, ReferenceKind.unresolved, |
| prefixReference: prefixReference); |
| } |
| return new EntityRefBuilder(reference: reference); |
| } |
| // The only supported instance property accessor - `length`. |
| if (identifier is PrefixedIdentifier && |
| element is PropertyAccessorElement && |
| !element.isStatic) { |
| if (element.name != 'length') { |
| throw new StateError('Only "length" property is allowed in constants.'); |
| } |
| Element prefixElement = identifier.prefix.staticElement; |
| int prefixRef = serializer._getElementReferenceId(prefixElement); |
| int lengthRef = serializer._getLengthPropertyReference(prefixRef); |
| return new EntityRefBuilder(reference: lengthRef); |
| } |
| if (element is TypeParameterElement) { |
| throw new StateError('Constants may not refer to type parameters.'); |
| } |
| return new EntityRefBuilder( |
| reference: serializer._getElementReferenceId(element)); |
| } |
| |
| @override |
| EntityRefBuilder serializePropertyAccess(PropertyAccess access) { |
| Element element = access.propertyName.staticElement; |
| // Unresolved property access. |
| if (element == null) { |
| Expression target = access.target; |
| if (target is Identifier) { |
| EntityRefBuilder targetRef = serializeIdentifier(target); |
| EntityRefBuilder propertyRef = serializeIdentifier(access.propertyName, |
| prefixReference: targetRef.reference); |
| return new EntityRefBuilder(reference: propertyRef.reference); |
| } else { |
| // TODO(scheglov) should we handle other targets in malformed constants? |
| throw new StateError('Unexpected target type: ${target.runtimeType}'); |
| } |
| } |
| // The only supported instance property accessor - `length`. |
| Expression target = access.target; |
| if (target is Identifier && |
| element is PropertyAccessorElement && |
| !element.isStatic) { |
| assert(element.name == 'length'); |
| Element prefixElement = target.staticElement; |
| int prefixRef = serializer._getElementReferenceId(prefixElement); |
| int lengthRef = serializer._getLengthPropertyReference(prefixRef); |
| return new EntityRefBuilder(reference: lengthRef); |
| } |
| return new EntityRefBuilder( |
| reference: serializer._getElementReferenceId(element)); |
| } |
| |
| @override |
| EntityRefBuilder serializeType(TypeName typeName) { |
| if (typeName != null) { |
| DartType type = typeName.type; |
| if (type == null || type.isUndefined) { |
| return serializeIdentifier(typeName.name); |
| } |
| } |
| DartType type = typeName != null ? typeName.type : DynamicTypeImpl.instance; |
| return serializer.serializeTypeRef(type, null); |
| } |
| } |
| |
| /** |
| * Instances of this class keep track of intermediate state during |
| * serialization of a single library. |
| */ |
| class _LibrarySerializer { |
| /** |
| * The library to be serialized. |
| */ |
| final LibraryElement libraryElement; |
| |
| /** |
| * The type provider. This is used to locate the library for `dart:core`. |
| */ |
| final TypeProvider typeProvider; |
| |
| /** |
| * Indicates whether the element model being serialized was analyzed using |
| * strong mode. |
| */ |
| final bool strongMode; |
| |
| /** |
| * Map from [LibraryElement] to the index of the entry in the "dependency |
| * table" that refers to it. |
| */ |
| final Map<LibraryElement, int> dependencyMap = <LibraryElement, int>{}; |
| |
| /** |
| * The "dependency table". This is the list of objects which should be |
| * written to [LinkedLibrary.dependencies]. |
| */ |
| final List<LinkedDependencyBuilder> dependencies = |
| <LinkedDependencyBuilder>[]; |
| |
| /** |
| * The linked portion of the "imports table". This is the list of ints |
| * which should be written to [LinkedLibrary.imports]. |
| */ |
| final List<int> linkedImports = <int>[]; |
| |
| /** |
| * Set of libraries which have been seen so far while visiting the transitive |
| * closure of exports. |
| */ |
| final Set<LibraryElement> librariesAddedToTransitiveExportClosure = |
| new Set<LibraryElement>(); |
| |
| /** |
| * Map from imported element to the prefix which may be used to refer to that |
| * element; elements for which no prefix is needed are absent from this map. |
| */ |
| final Map<Element, PrefixElement> prefixMap = <Element, PrefixElement>{}; |
| |
| /** |
| * List of serializers for the compilation units constituting this library. |
| */ |
| final List<_CompilationUnitSerializer> compilationUnitSerializers = |
| <_CompilationUnitSerializer>[]; |
| |
| _LibrarySerializer(this.libraryElement, this.typeProvider, this.strongMode) { |
| dependencies.add(new LinkedDependencyBuilder()); |
| dependencyMap[libraryElement] = 0; |
| } |
| |
| /** |
| * Retrieve a list of the Sources for the compilation units in the library. |
| */ |
| List<Source> get unitSources => compilationUnitSerializers |
| .map((_CompilationUnitSerializer s) => s.unitSource) |
| .toList(); |
| |
| /** |
| * Retrieve a list of the URIs for the compilation units in the library. |
| */ |
| List<String> get unitUris => compilationUnitSerializers |
| .map((_CompilationUnitSerializer s) => s.unitUri) |
| .toList(); |
| |
| /** |
| * Retrieve a list of the [UnlinkedUnitBuilder]s for the compilation units in |
| * the library. |
| */ |
| List<UnlinkedUnitBuilder> get unlinkedUnits => compilationUnitSerializers |
| .map((_CompilationUnitSerializer s) => s.unlinkedUnit) |
| .toList(); |
| |
| /** |
| * Add [exportedLibrary] (and the transitive closure of all libraries it |
| * exports) to the dependency table ([LinkedLibrary.dependencies]). |
| */ |
| void addTransitiveExportClosure(LibraryElement exportedLibrary) { |
| if (librariesAddedToTransitiveExportClosure.add(exportedLibrary)) { |
| serializeDependency(exportedLibrary); |
| for (LibraryElement transitiveExport |
| in exportedLibrary.exportedLibraries) { |
| addTransitiveExportClosure(transitiveExport); |
| } |
| } |
| } |
| |
| /** |
| * Fill in [prefixMap] using information from [libraryElement.imports]. |
| */ |
| void computePrefixMap() { |
| for (ImportElement import in libraryElement.imports) { |
| if (import.prefix == null) { |
| continue; |
| } |
| import.importedLibrary.exportNamespace.definedNames |
| .forEach((String name, Element e) { |
| if (new NameFilter.forNamespaceCombinators(import.combinators) |
| .accepts(name)) { |
| prefixMap[e] = import.prefix; |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Return the index of the entry in the dependency table |
| * ([LinkedLibrary.dependencies]) for the given [dependentLibrary]. A new |
| * entry is added to the table if necessary to satisfy the request. |
| */ |
| int serializeDependency(LibraryElement dependentLibrary) { |
| return dependencyMap.putIfAbsent(dependentLibrary, () { |
| int index = dependencies.length; |
| List<String> parts = dependentLibrary.parts |
| .map((CompilationUnitElement e) => e.source.uri.toString()) |
| .toList(); |
| dependencies.add(new LinkedDependencyBuilder( |
| uri: dependentLibrary.source.uri.toString(), parts: parts)); |
| return index; |
| }); |
| } |
| |
| /** |
| * Serialize the whole library element into a [LinkedLibrary]. Should be |
| * called exactly once for each instance of [_LibrarySerializer]. |
| * |
| * The unlinked compilation units are stored in [unlinkedUnits], and their |
| * absolute URIs are stored in [unitUris]. |
| */ |
| LinkedLibraryBuilder serializeLibrary() { |
| computePrefixMap(); |
| LinkedLibraryBuilder pb = new LinkedLibraryBuilder(); |
| for (ExportElement exportElement in libraryElement.exports) { |
| addTransitiveExportClosure(exportElement.exportedLibrary); |
| } |
| for (ImportElement importElement in libraryElement.imports) { |
| addTransitiveExportClosure(importElement.importedLibrary); |
| linkedImports.add(serializeDependency(importElement.importedLibrary)); |
| } |
| compilationUnitSerializers.add(new _CompilationUnitSerializer( |
| this, libraryElement.definingCompilationUnit, 0)); |
| for (int i = 0; i < libraryElement.parts.length; i++) { |
| compilationUnitSerializers.add( |
| new _CompilationUnitSerializer(this, libraryElement.parts[i], i + 1)); |
| } |
| for (_CompilationUnitSerializer compilationUnitSerializer |
| in compilationUnitSerializers) { |
| compilationUnitSerializer.addCompilationUnitElements(); |
| } |
| pb.units = compilationUnitSerializers |
| .map((_CompilationUnitSerializer s) => s.linkedUnit) |
| .toList(); |
| pb.dependencies = dependencies; |
| pb.numPrelinkedDependencies = dependencies.length; |
| for (_CompilationUnitSerializer compilationUnitSerializer |
| in compilationUnitSerializers) { |
| compilationUnitSerializer.createLinkedTypes(); |
| } |
| pb.importDependencies = linkedImports; |
| List<String> exportedNames = |
| libraryElement.exportNamespace.definedNames.keys.toList(); |
| exportedNames.sort(); |
| List<LinkedExportNameBuilder> exportNames = <LinkedExportNameBuilder>[]; |
| for (String name in exportedNames) { |
| if (libraryElement.publicNamespace.definedNames.containsKey(name)) { |
| continue; |
| } |
| Element element = libraryElement.exportNamespace.get(name); |
| LibraryElement dependentLibrary = element.library; |
| CompilationUnitElement unitElement = |
| element.getAncestor((Element e) => e is CompilationUnitElement); |
| int unit = dependentLibrary.units.indexOf(unitElement); |
| assert(unit != -1); |
| ReferenceKind kind = _getReferenceKind(element); |
| exportNames.add(new LinkedExportNameBuilder( |
| name: name, |
| dependency: serializeDependency(dependentLibrary), |
| unit: unit, |
| kind: kind)); |
| } |
| pb.exportNames = exportNames; |
| return pb; |
| } |
| } |