Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Unified Diff: pkg/analyzer/lib/src/summary/summarize_elements.dart

Issue 1420053011: Introduce code to generate summaries from an element model. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Address code review comments. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer/lib/src/summary/summarize_elements.dart
diff --git a/pkg/analyzer/lib/src/summary/summarize_elements.dart b/pkg/analyzer/lib/src/summary/summarize_elements.dart
new file mode 100644
index 0000000000000000000000000000000000000000..9f8dd161350926312407b0454301d0217955a704
--- /dev/null
+++ b/pkg/analyzer/lib/src/summary/summarize_elements.dart
@@ -0,0 +1,609 @@
+// 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 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/summary/builder.dart';
+import 'package:analyzer/src/summary/format.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.
+ *
+ * Return an object which may safely be passed to [BuilderContext.getBuffer] or
+ * included in an aggregate summary.
+ */
+Object serializeLibrary(
+ BuilderContext ctx, LibraryElement lib, TypeProvider typeProvider) {
+ return new _LibrarySerializer(ctx, lib, typeProvider).serializeLibrary();
+}
+
+/**
+ * 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;
+
+ /**
+ * List of objects which should be written to [UnlinkedLibrary.classes].
+ */
+ final List<Object> classes = <Object>[];
+
+ /**
+ * List of objects which should be written to [UnlinkedLibrary.enums].
+ */
+ final List<Object> enums = <Object>[];
+
+ /**
+ * List of objects which should be written to [UnlinkedLibrary.executables].
+ */
+ final List<Object> executables = <Object>[];
+
+ /**
+ * List of objects which should be written to [UnlinkedLibrary.typedefs].
+ */
+ final List<Object> typedefs = <Object>[];
+
+ /**
+ * List of objects which should be written to [UnlinkedLibrary.units].
+ */
+ final List<Object> units = <Object>[];
+
+ /**
+ * List of objects which should be written to [UnlinkedLibrary.variables].
+ */
+ final List<Object> variables = <Object>[];
+
+ /**
+ * 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 [PrelinkedLibrary.dependencies].
+ */
+ final List<Object> dependencies = <Object>[];
+
+ /**
+ * The unlinked portion of the "imports table". This is the list of objects
+ * which should be written to [UnlinkedLibrary.imports].
+ */
+ final List<Object> unlinkedImports = <Object>[];
+
+ /**
+ * The prelinked portion of the "imports table". This is the list of ints
+ * which should be written to [PrelinkedLibrary.imports].
+ */
+ final List<Object> prelinkedImports = <int>[];
+
+ /**
+ * Map from prefix [String] to the index of the entry in the "prefix" table
+ * that refers to it. The empty prefix is not included in this map.
+ */
+ final Map<String, int> prefixMap = <String, int>{};
+
+ /**
+ * The "prefix table". This is the list of objects which should be written
+ * to [UnlinkedLibrary.prefixes].
+ */
+ final List<Object> prefixes = <Object>[];
+
+ /**
+ * 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 [UnlinkedLibrary.references].
+ */
+ final List<Object> unlinkedReferences = <Object>[];
+
+ /**
+ * The prelinked portion of the "references table". This is the list of
+ * objects which should be written to [PrelinkedLibrary.references].
+ */
+ final List<Object> prelinkedReferences = <Object>[];
+
+ //final Map<String, int> prefixIndices = <String, int>{};
+
+ /**
+ * Index into the "references table" representing `dynamic`, if such an index
+ * exists. `null` if no such entry has been made in the references table
+ * yet.
+ */
+ int dynamicReferenceIndex = null;
+
+ /**
+ * 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;
+
+ /**
+ * Set of libraries which have been seen so far while visiting the transitive
+ * closure of exports.
+ */
+ final Set<LibraryElement> librariesAddedToTransitiveExportClosure =
+ new Set<LibraryElement>();
+
+ /**
+ * [BuilderContext] used to serialize the output summary.
+ */
+ final BuilderContext ctx;
+
+ _LibrarySerializer(this.ctx, this.libraryElement, this.typeProvider) {
+ dependencies.add(encodePrelinkedDependency(ctx));
+ dependencyMap[libraryElement] = 0;
+ prefixes.add(encodeUnlinkedPrefix(ctx));
+ }
+
+ /**
+ * Retrieve the library element for `dart:core`.
+ */
+ LibraryElement get coreLibrary => typeProvider.objectType.element.library;
+
+ /**
+ * Add all classes, enums, typedefs, executables, and top level variables
+ * from the given compilation unit [element] to the library summary.
+ * [unitNum] indicates the ordinal position of this compilation unit in the
+ * library.
+ */
+ void addCompilationUnitElements(CompilationUnitElement element, int unitNum) {
+ UnlinkedUnitBuilder b = new UnlinkedUnitBuilder(ctx);
+ if (element.uri != null) {
+ b.uri = element.uri;
+ }
+ units.add(b.finish());
+ for (ClassElement cls in element.types) {
+ classes.add(serializeClass(cls, unitNum));
+ }
+ for (ClassElement e in element.enums) {
+ enums.add(serializeEnum(e, unitNum));
+ }
+ for (FunctionTypeAliasElement type in element.functionTypeAliases) {
+ typedefs.add(serializeTypedef(type, unitNum));
+ }
+ for (FunctionElement executable in element.functions) {
+ executables.add(serializeExecutable(executable, unitNum));
+ }
+ for (PropertyAccessorElement accessor in element.accessors) {
+ if (!accessor.isSynthetic) {
+ executables.add(serializeExecutable(accessor, unitNum));
+ } else if (accessor.isGetter) {
+ PropertyInducingElement variable = accessor.variable;
+ if (variable != null) {
+ assert(!variable.isSynthetic);
+ variables.add(serializeVariable(variable, unitNum));
+ }
+ }
+ }
+ }
+
+ /**
+ * Add [exportedLibrary] (and the transitive closure of all libraries it
+ * exports) to the dependency table ([PrelinkedLibrary.dependencies]).
+ */
+ void addTransitiveExportClosure(LibraryElement exportedLibrary) {
+ if (librariesAddedToTransitiveExportClosure.add(exportedLibrary)) {
+ serializeDependency(exportedLibrary);
+ for (LibraryElement transitiveExport
+ in exportedLibrary.exportedLibraries) {
+ addTransitiveExportClosure(transitiveExport);
+ }
+ }
+ }
+
+ /**
+ * Compute the appropriate De Bruijn index to represent the given type
+ * parameter [type].
+ */
+ int findTypeParameterIndex(TypeParameterType type) {
+ int index = 0;
+ Element enclosingElement = type.element.enclosingElement;
+ while (enclosingElement != null) {
+ List<TypeParameterElement> typeParameters;
+ if (enclosingElement is ClassElement) {
+ typeParameters = enclosingElement.typeParameters;
+ } else if (enclosingElement is FunctionTypeAliasElement) {
+ // TODO(paulberry): test this.
+ typeParameters = enclosingElement.typeParameters;
+ }
+ for (int i = 0; i < typeParameters.length; i++) {
+ TypeParameterElement param = typeParameters[i];
+ if (param == type.element) {
+ return index + typeParameters.length - i;
+ }
+ }
+ index += typeParameters.length;
+ enclosingElement = enclosingElement.enclosingElement;
+ }
+ throw new StateError('Unbound type parameter $type');
+ }
+
+ /**
+ * Serialize the given [classElement], which exists in the unit numbered
+ * [unitNum], creating an [UnlinkedClass].
+ */
+ Object serializeClass(ClassElement classElement, int unitNum) {
+ UnlinkedClassBuilder b = new UnlinkedClassBuilder(ctx);
+ b.name = classElement.name;
+ b.unit = unitNum;
+ b.typeParameters =
+ classElement.typeParameters.map(serializeTypeParam).toList();
+ if (classElement.supertype != null && !classElement.supertype.isObject) {
+ b.supertype = serializeTypeRef(classElement.supertype);
+ }
+ b.mixins = classElement.mixins.map(serializeTypeRef).toList();
+ b.interfaces = classElement.interfaces.map(serializeTypeRef).toList();
+ List<Object> fields = <Object>[];
+ List<Object> executables = <Object>[];
+ for (ConstructorElement executable in classElement.constructors) {
+ if (!executable.isSynthetic) {
+ executables.add(serializeExecutable(executable, 0));
+ }
+ }
+ for (MethodElement executable in classElement.methods) {
+ executables.add(serializeExecutable(executable, 0));
+ }
+ for (PropertyAccessorElement accessor in classElement.accessors) {
+ if (!accessor.isSynthetic) {
+ executables.add(serializeExecutable(accessor, 0));
+ } else if (accessor.isGetter) {
+ PropertyInducingElement field = accessor.variable;
+ if (field != null && !field.isSynthetic) {
+ fields.add(serializeVariable(field, 0));
+ }
+ }
+ }
+ b.fields = fields;
+ b.executables = executables;
+ b.isAbstract = classElement.isAbstract;
+ b.isMixinApplication = classElement.isMixinApplication;
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [combinator] into an [UnlinkedCombinator].
+ */
+ Object serializeCombinator(NamespaceCombinator combinator) {
+ UnlinkedCombinatorBuilder b = new UnlinkedCombinatorBuilder(ctx);
+ if (combinator is ShowElementCombinator) {
+ b.shows = combinator.shownNames.map(serializeCombinatorName).toList();
+ } else if (combinator is HideElementCombinator) {
+ b.hides = combinator.hiddenNames.map(serializeCombinatorName).toList();
+ }
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [name] into an [UnlinkedCombinatorName].
+ */
+ Object serializeCombinatorName(String name) {
+ return encodeUnlinkedCombinatorName(ctx, name: name);
+ }
+
+ /**
+ * Return the index of the entry in the dependency table
+ * ([PrelinkedLibrary.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;
+ dependencies.add(encodePrelinkedDependency(ctx,
+ uri: dependentLibrary.source.uri.toString()));
+ return index;
+ });
+ }
+
+ /**
+ * Return the index of the entry in the references table
+ * ([UnlinkedLibrary.references] and [PrelinkedLibrary.references])
+ * representing the pseudo-type `dynamic`. A new entry is added to the table
+ * if necessary to satisfy the request.
+ */
+ int serializeDynamicReference() {
+ if (dynamicReferenceIndex == null) {
+ assert(unlinkedReferences.length == prelinkedReferences.length);
+ dynamicReferenceIndex = unlinkedReferences.length;
+ unlinkedReferences.add(encodeUnlinkedReference(ctx));
+ prelinkedReferences.add(encodePrelinkedReference(ctx,
+ kind: PrelinkedReferenceKind.classOrEnum));
+ }
+ return dynamicReferenceIndex;
+ }
+
+ /**
+ * Serialize the given [enumElement], which exists in the unit numbered
+ * [unitNum], creating an [UnlinkedEnum].
+ */
+ Object serializeEnum(ClassElement enumElement, int unitNum) {
+ UnlinkedEnumBuilder b = new UnlinkedEnumBuilder(ctx);
+ b.name = enumElement.name;
+ List<Object> values = <Object>[];
+ for (FieldElement field in enumElement.fields) {
+ if (field.isConst && field.type.element == enumElement) {
+ values.add(encodeUnlinkedEnumValue(ctx, name: field.name));
+ }
+ }
+ b.values = values;
+ b.unit = unitNum;
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [executableElement], which exists in the unit numbered
+ * [unitNum], creating an [UnlinkedExecutable]. For elements declared inside
+ * a class, [unitNum] should be zero.
+ */
+ Object serializeExecutable(ExecutableElement executableElement, int unitNum) {
+ if (executableElement.enclosingElement is ClassElement) {
+ assert(unitNum == 0);
+ }
+ UnlinkedExecutableBuilder b = new UnlinkedExecutableBuilder(ctx);
+ b.name = executableElement.name;
+ b.unit = unitNum;
+ if (!executableElement.type.returnType.isVoid) {
+ b.returnType = serializeTypeRef(executableElement.type.returnType);
+ }
+ // TODO(paulberry): serialize type parameters.
+ 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 ConstructorElement) {
+ b.kind = UnlinkedExecutableKind.constructor;
+ b.isConst = executableElement.isConst;
+ b.isFactory = executableElement.isFactory;
+ } else {
+ b.kind = UnlinkedExecutableKind.functionOrMethod;
+ }
+ b.isAbstract = executableElement.isAbstract;
+ b.isStatic = executableElement.isStatic &&
+ executableElement.enclosingElement is ClassElement;
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [exportElement] into an [UnlinkedExport].
+ */
+ Object serializeExport(ExportElement exportElement) {
+ UnlinkedExportBuilder b = new UnlinkedExportBuilder(ctx);
+ b.uri = exportElement.uri;
+ b.combinators = exportElement.combinators.map(serializeCombinator).toList();
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [importElement], adding information about it to
+ * the [unlinkedImports] and [prelinkedImports] lists.
+ */
+ void serializeImport(ImportElement importElement) {
+ assert(unlinkedImports.length == prelinkedImports.length);
+ UnlinkedImportBuilder b = new UnlinkedImportBuilder(ctx);
+ b.isDeferred = importElement.isDeferred;
+ b.offset = importElement.nameOffset;
+ b.combinators = importElement.combinators.map(serializeCombinator).toList();
+ if (importElement.prefix != null) {
+ b.prefix = prefixMap.putIfAbsent(importElement.prefix.name, () {
+ int index = prefixes.length;
+ prefixes
+ .add(encodeUnlinkedPrefix(ctx, name: importElement.prefix.name));
+ return index;
+ });
+ }
+ if (importElement.isSynthetic) {
+ b.isImplicit = true;
+ } else {
+ b.uri = importElement.uri;
+ }
+ addTransitiveExportClosure(importElement.importedLibrary);
+ unlinkedImports.add(b.finish());
+ prelinkedImports.add(serializeDependency(importElement.importedLibrary));
+ }
+
+ /**
+ * Serialize the whole library element into a [PrelinkedLibrary]. Should be
+ * called exactly once for each instance of [_LibrarySerializer].
+ */
+ Object serializeLibrary() {
+ UnlinkedLibraryBuilder ub = new UnlinkedLibraryBuilder(ctx);
+ PrelinkedLibraryBuilder pb = new PrelinkedLibraryBuilder(ctx);
+ if (libraryElement.name.isNotEmpty) {
+ ub.name = libraryElement.name;
+ }
+ for (ImportElement importElement in libraryElement.imports) {
+ serializeImport(importElement);
+ }
+ ub.exports = libraryElement.exports.map(serializeExport).toList();
+ addCompilationUnitElements(libraryElement.definingCompilationUnit, 0);
+ for (int i = 0; i < libraryElement.parts.length; i++) {
+ addCompilationUnitElements(libraryElement.parts[i], i + 1);
+ }
+ ub.classes = classes;
+ ub.enums = enums;
+ ub.executables = executables;
+ ub.imports = unlinkedImports;
+ ub.prefixes = prefixes;
+ ub.references = unlinkedReferences;
+ ub.typedefs = typedefs;
+ ub.units = units;
+ ub.variables = variables;
+ pb.unlinked = ub.finish();
+ pb.dependencies = dependencies;
+ pb.importDependencies = prelinkedImports;
+ pb.references = prelinkedReferences;
+ return pb.finish();
+ }
+
+ /**
+ * Serialize the given [parameter] into an [UnlinkedParam].
+ */
+ Object serializeParam(ParameterElement parameter) {
+ UnlinkedParamBuilder b = new UnlinkedParamBuilder(ctx);
+ b.name = parameter.name;
+ switch (parameter.parameterKind) {
+ case ParameterKind.REQUIRED:
+ b.kind = UnlinkedParamKind.required;
+ break;
+ case ParameterKind.POSITIONAL:
+ b.kind = UnlinkedParamKind.positional;
+ break;
+ case ParameterKind.NAMED:
+ b.kind = UnlinkedParamKind.named;
+ break;
+ }
+ b.isInitializingFormal = parameter.isInitializingFormal;
+ DartType type = parameter.type;
+ if (type is FunctionType) {
+ b.isFunctionTyped = true;
+ if (!type.returnType.isVoid) {
+ b.type = serializeTypeRef(type.returnType);
+ }
+ b.parameters = type.parameters.map(serializeParam).toList();
+ } else {
+ b.type = serializeTypeRef(type);
+ }
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [typedefElement], which exists in the unit numbered
+ * [unitNum], creating an [UnlinkedTypedef].
+ */
+ Object serializeTypedef(
+ FunctionTypeAliasElement typedefElement, int unitNum) {
+ UnlinkedTypedefBuilder b = new UnlinkedTypedefBuilder(ctx);
+ b.name = typedefElement.name;
+ b.unit = unitNum;
+ b.typeParameters =
+ typedefElement.typeParameters.map(serializeTypeParam).toList();
+ if (!typedefElement.returnType.isVoid) {
+ b.returnType = serializeTypeRef(typedefElement.returnType);
+ }
+ b.parameters = typedefElement.parameters.map(serializeParam).toList();
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [typeParameter] into an [UnlinkedTypeParam].
+ */
+ Object serializeTypeParam(TypeParameterElement typeParameter) {
+ UnlinkedTypeParamBuilder b = new UnlinkedTypeParamBuilder(ctx);
+ b.name = typeParameter.name;
+ if (typeParameter.bound != null) {
+ b.bound = serializeTypeRef(typeParameter.bound);
+ }
+ return b.finish();
+ }
+
+ /**
+ * Serialize the given [type] into an [UnlinkedTypeRef].
+ */
+ Object serializeTypeRef(DartType type) {
+ UnlinkedTypeRefBuilder b = new UnlinkedTypeRefBuilder(ctx);
+ if (type is TypeParameterType) {
+ Element enclosingElement = type.element.enclosingElement;
+ b.paramReference = findTypeParameterIndex(type);
+ } else {
+ Element element = type.element;
+ CompilationUnitElement dependentCompilationUnit =
+ element.getAncestor((Element e) => e is CompilationUnitElement);
+ LibraryElement dependentLibrary = element.library;
+ if (dependentLibrary == null) {
+ assert(type.isDynamic);
+ if (type is UndefinedTypeImpl) {
+ b.reference = serializeUnresolvedReference();
+ } else {
+ b.reference = serializeDynamicReference();
+ }
+ } else {
+ b.reference = referenceMap.putIfAbsent(element, () {
+ assert(unlinkedReferences.length == prelinkedReferences.length);
+ int index = unlinkedReferences.length;
+ // TODO(paulberry): set UnlinkedReference.prefix.
+ unlinkedReferences
+ .add(encodeUnlinkedReference(ctx, name: element.name));
+ prelinkedReferences.add(encodePrelinkedReference(ctx,
+ dependency: serializeDependency(dependentLibrary),
+ kind: element is FunctionTypeAliasElement
+ ? PrelinkedReferenceKind.typedef
+ : PrelinkedReferenceKind.classOrEnum));
+ return index;
+ });
+ }
+ List<DartType> typeArguments;
+ if (type is InterfaceType) {
+ typeArguments = type.typeArguments;
+ } else if (type is FunctionType) {
+ typeArguments = type.typeArguments;
+ }
+ if (typeArguments != null &&
+ typeArguments.any((DartType argument) => !argument.isDynamic)) {
+ b.typeArguments = typeArguments.map(serializeTypeRef).toList();
+ }
+ }
+ return b.finish();
+ }
+
+ /**
+ * Return the index of the entry in the references table
+ * ([UnlinkedLibrary.references] and [PrelinkedLibrary.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.
+ if (unresolvedReferenceIndex == null) {
+ assert(unlinkedReferences.length == prelinkedReferences.length);
+ unresolvedReferenceIndex = unlinkedReferences.length;
+ unlinkedReferences.add(encodeUnlinkedReference(ctx));
+ prelinkedReferences.add(encodePrelinkedReference(ctx,
+ kind: PrelinkedReferenceKind.unresolved));
+ }
+ return unresolvedReferenceIndex;
+ }
+
+ /**
+ * Serialize the given [variable], which exists in the unit numbered
+ * [unitNum], creating an [UnlinkedVariable]. For variables declared inside
+ * a class (i.e. fields), [unitNum] should be zero.
+ */
+ Object serializeVariable(PropertyInducingElement variable, int unitNum) {
+ if (variable.enclosingElement is ClassElement) {
+ assert(unitNum == 0);
+ }
+ UnlinkedVariableBuilder b = new UnlinkedVariableBuilder(ctx);
+ b.name = variable.name;
+ b.unit = unitNum;
+ b.type = serializeTypeRef(variable.type);
+ b.isStatic = variable.isStatic && variable.enclosingElement is ClassElement;
+ b.isFinal = variable.isFinal;
+ b.isConst = variable.isConst;
+ return b.finish();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698