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

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

Issue 1576743002: Create a prelinker for summaries. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 months 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/prelink.dart
diff --git a/pkg/analyzer/lib/src/summary/prelink.dart b/pkg/analyzer/lib/src/summary/prelink.dart
new file mode 100644
index 0000000000000000000000000000000000000000..5c13cfdab1b65c6296d9c08ce45d25717719d977
--- /dev/null
+++ b/pkg/analyzer/lib/src/summary/prelink.dart
@@ -0,0 +1,506 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
scheglov 2016/01/10 05:15:12 2016 :-)
Paul Berry 2016/01/10 22:15:17 Done.
+// 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.
+
+import 'package:analyzer/src/summary/base.dart';
+import 'package:analyzer/src/summary/format.dart';
+
+/**
+ * Create a [PrelinkedLibraryBuilder] corresponding to the given
+ * [definingUnit], which should be the defining compilation unit for a library.
+ * Compilation units referenced by the defining compilation unit via `part`
+ * declarations will be retrieved using [getPart]. Public namespaces for
+ * libraries referenced by the defining compilation unit via `import`
+ * declarations (and files reachable from them via `part` and `export`
+ * declarations) will be retrieved using [getImport].
+ */
+PrelinkedLibraryBuilder prelink(BuilderContext ctx, UnlinkedUnit definingUnit,
+ GetPartCallback getPart, GetImportCallback getImport) {
+ return new _Prelinker(ctx, definingUnit, getPart, getImport).prelink();
+}
+
+/**
+ * Type of the callback used by the prelinker to obtain public namespace
+ * information about libraries imported by the library to be prelinked (and
+ * the transitive closure of parts and exports reachable from those libraries).
+ * [relativeUri] should be interpreted relative to the defining compilation
+ * unit.
scheglov 2016/01/10 05:15:12 The defining compilation unit of... which library?
Paul Berry 2016/01/10 22:15:17 The defining compilation unit of the library being
+ *
+ * If no file exists at the given uri, null should be returned.
+ */
+typedef UnlinkedPublicNamespace GetImportCallback(String relativeUri);
+
+/**
+ * Type of the callback used by the prelinker to obtain unlinked summaries of
+ * part files of the library to be prelinked. [relaviteUri] should be
+ * interpreted relative to the defining compilation unit.
Brian Wilkerson 2016/01/10 16:45:26 Ditto
Paul Berry 2016/01/10 22:15:17 Done.
+ *
+ * If no file exists at the given uri, null should be returned.
Brian Wilkerson 2016/01/10 16:45:26 nit: `null`
Paul Berry 2016/01/10 22:15:17 Done.
+ */
+typedef UnlinkedUnit GetPartCallback(String relativeUri);
+
+/**
+ * A [NameFilter] represents the set of filtering rules implied by zero or more
+ * combinators in an `export` or `import` statement.
+ */
+class NameFilter {
+ /**
+ * A [NameFilter] representing no filtering at all (i.e. no combinators).
+ */
+ static final NameFilter identity =
+ new NameFilter._(hiddenNames: new Set<String>());
+
+ /**
+ * If this [NameFilter] accepts a finite number of names and hides all
+ * others, the (possibly empty) set of names it accepts. Otherwise `null`.
+ */
+ final Set<String> shownNames;
+
+ /**
+ * If [shownNames] is `null`, the (possibly empty) set of names not accepted
+ * by this filter (all other names are accepted). If [shownNames] is not
+ * `null`, then [hiddenNames] will be `null`.
+ */
+ final Set<String> hiddenNames;
+
+ /**
+ * Create a [NameFilter] based on the given [combinator].
+ */
+ factory NameFilter.forCombinator(UnlinkedCombinator combinator) {
+ if (combinator.shows.isNotEmpty) {
+ return new NameFilter._(shownNames: combinator.shows.toSet());
+ } else {
+ return new NameFilter._(hiddenNames: combinator.hides.toSet());
+ }
+ }
+
+ /**
+ * Create a [NameFilter] based on the given (possibly empty) sequence of
+ * [combinators].
+ */
+ factory NameFilter.forCombinators(List<UnlinkedCombinator> combinators) {
+ NameFilter result = identity;
+ for (UnlinkedCombinator combinator in combinators) {
+ result = result.merge(new NameFilter.forCombinator(combinator));
+ }
+ return result;
+ }
+
+ const NameFilter._({this.shownNames, this.hiddenNames});
+
+ /**
+ * Determine if the given [name] is accepted by this [NameFilter].
+ */
+ bool accepts(String name) {
+ if (shownNames != null) {
+ return shownNames.contains(name);
+ } else {
+ return !hiddenNames.contains(name);
+ }
+ }
+
+ /**
+ * Produce a new [NameFilter] by combining this [NameFilter] with another
+ * one. The new [NameFilter] will only accept names that would be accepted
+ * by both input filters.
+ */
+ NameFilter merge(NameFilter other) {
+ if (shownNames != null) {
+ if (other.shownNames != null) {
+ return new NameFilter._(
+ shownNames: shownNames.intersection(other.shownNames));
+ } else {
+ return new NameFilter._(
+ shownNames: shownNames.difference(other.hiddenNames));
+ }
+ } else {
+ if (other.shownNames != null) {
+ return new NameFilter._(
+ shownNames: other.shownNames.difference(hiddenNames));
+ } else {
+ return new NameFilter._(
+ hiddenNames: hiddenNames.union(other.hiddenNames));
+ }
+ }
+ }
+}
+
+/**
+ * A [_Meaning] stores all the information necessary to find the declaration
+ * referred to by a name in a namespace.
+ */
+class _Meaning {
+ /**
+ * Which unit in the dependent library contains the declared entity.
+ */
+ final int unit;
+
+ /**
+ * The kind of entity being referred to.
+ */
+ final PrelinkedReferenceKind kind;
+
+ /**
+ * Which of the dependencies of the library being prelinked contains the
+ * declared entity.
+ */
+ final int dependency;
+
+ /**
+ * If the entity being referred to is generic, the number of type parameters
+ * it accepts. Otherwise zero.
+ */
+ final int numTypeParameters;
+
+ _Meaning(this.unit, this.kind, this.dependency, this.numTypeParameters);
+
+ /**
+ * Encode this [_Meaning] as a [PrelinkedReference].
+ */
+ PrelinkedReferenceBuilder encode(BuilderContext ctx) {
+ return encodePrelinkedReference(ctx,
+ unit: unit,
+ kind: kind,
+ dependency: dependency,
+ numTypeParameters: numTypeParameters);
+ }
+}
+
+/**
+ * A [_Meaning] representing a prefix introduced by an import directive.
+ */
+class _PrefixMeaning extends _Meaning {
+ final Map<String, _Meaning> namespace = <String, _Meaning>{};
+
+ _PrefixMeaning() : super(0, PrelinkedReferenceKind.prefix, 0, 0);
+}
+
+/**
+ * Helper class containing temporary data structures needed to prelink a single
+ * library.
+ *
+ * Note: throughout this class, a `null` value for a relative URI represents
+ * the defining compilation unit of the library being prelinked.
+ */
+class _Prelinker {
+ final BuilderContext ctx;
+ final UnlinkedUnit definingUnit;
+ final GetPartCallback getPart;
+ final GetImportCallback getImport;
+
+ /**
+ * Cache of values returned by [getImport].
+ */
+ final Map<String, UnlinkedPublicNamespace> importCache =
+ <String, UnlinkedPublicNamespace>{};
+
+ /**
+ * Cache of values returned by [getPart].
+ */
+ final Map<String, UnlinkedUnit> partCache = <String, UnlinkedUnit>{};
+
+ /**
+ * Names defined inside the library being prelinked.
+ */
+ final Map<String, _Meaning> privateNamespace;
+
+ /**
+ * List of dependencies of the library being prelinked. This will be output
+ * to [PrelinkedLibrary.dependencies].
+ */
+ final List<PrelinkedDependencyBuilder> dependencies;
+
+ /**
+ * Map from the relative URI of a dependent library to the index of the
+ * corresponding entry in [dependencies].
+ */
+ final Map<String, int> uriToDependency = <String, int>{null: 0};
+
+ /**
+ * List of public namespaces corresponding to each entry in [dependencies].
+ */
+ final List<Map<String, _Meaning>> dependencyToPublicNamespace =
+ <Map<String, _Meaning>>[null];
+
+ _Prelinker(
+ BuilderContext ctx, this.definingUnit, this.getPart, this.getImport)
+ : ctx = ctx,
+ dependencies = <PrelinkedDependencyBuilder>[
+ encodePrelinkedDependency(ctx)
+ ],
+ privateNamespace = <String, _Meaning>{
+ '': new _Meaning(0, PrelinkedReferenceKind.classOrEnum, 0, 0)
+ } {
+ partCache[null] = definingUnit;
+ importCache[null] = definingUnit.publicNamespace;
+ }
+
+ /**
+ * Compute the public namespace for the library whose URI is reachable from
+ * [definingUnit] via [relativeUri], by aggregating together public namespace
+ * information from all of its parts.
+ */
+ Map<String, _Meaning> aggregatePublicNamespace(String relativeUri) {
+ if (uriToDependency.containsKey(relativeUri)) {
+ return dependencyToPublicNamespace[uriToDependency[relativeUri]];
+ }
+ assert(dependencies.length == dependencyToPublicNamespace.length);
+ int dependency = dependencies.length;
+ uriToDependency[relativeUri] = dependency;
+ dependencies.add(encodePrelinkedDependency(ctx, uri: relativeUri));
+
+ Map<String, _Meaning> aggregated = <String, _Meaning>{};
+
+ List<String> unitUris = getUnitUris(relativeUri);
+ for (int unitNum = 0; unitNum < unitUris.length; unitNum++) {
+ String unitUri = unitUris[unitNum];
+ UnlinkedPublicNamespace importedNamespace = getImportCached(unitUri);
+ if (importedNamespace == null) {
+ continue;
+ }
+ for (UnlinkedPublicName name in importedNamespace.names) {
+ aggregated.putIfAbsent(
+ name.name,
+ () => new _Meaning(
+ unitNum, name.kind, dependency, name.numTypeParameters));
+ }
+ }
+
+ dependencyToPublicNamespace.add(aggregated);
+ return aggregated;
+ }
+
+ /**
+ * Compute the export namespace for the library whose URI is reachable from
+ * [definingUnit] via [relativeUri], by aggregating together public namespace
+ * information from the library and the transitive closure of its exports.
+ */
+ Map<String, _Meaning> computeExportNamespace(String relativeUri) {
+ Map<String, _Meaning> exportNamespace =
+ aggregatePublicNamespace(relativeUri);
+ void chaseExports(
+ NameFilter filter, String relativeUri, Set<String> seenUris) {
+ if (seenUris.add(relativeUri)) {
+ UnlinkedPublicNamespace exportedNamespace =
+ getImportCached(relativeUri);
+ if (exportedNamespace != null) {
+ for (UnlinkedExportPublic export in exportedNamespace.exports) {
+ String exportUri = resolveUri(relativeUri, export.uri);
+ aggregatePublicNamespace(exportUri)
+ .forEach((String name, _Meaning meaning) {
+ if (filter.accepts(name) && !exportNamespace.containsKey(name)) {
+ exportNamespace[name] = meaning;
+ }
+ });
+ chaseExports(
+ filter.merge(new NameFilter.forCombinators(export.combinators)),
+ exportUri,
+ seenUris);
+ }
+ }
+ seenUris.remove(relativeUri);
+ }
+ }
+ chaseExports(NameFilter.identity, relativeUri, new Set<String>());
+ return exportNamespace;
+ }
+
+ /**
+ * Extract all the names defined in [unit] (which is the [unitNum]th unit in
+ * the library being prelinked) and store them in [privateNamespace].
+ * Excludes names introduced by `import` statements.
+ */
+ void extractPrivateNames(UnlinkedUnit unit, int unitNum) {
+ for (UnlinkedClass cls in unit.classes) {
+ privateNamespace.putIfAbsent(
+ cls.name,
+ () => new _Meaning(unitNum, PrelinkedReferenceKind.classOrEnum, 0,
+ cls.typeParameters.length));
+ }
+ for (UnlinkedEnum enm in unit.enums) {
+ privateNamespace.putIfAbsent(
+ enm.name,
+ () =>
+ new _Meaning(unitNum, PrelinkedReferenceKind.classOrEnum, 0, 0));
+ }
+ for (UnlinkedExecutable executable in unit.executables) {
+ privateNamespace.putIfAbsent(
+ executable.name,
+ () => new _Meaning(unitNum, PrelinkedReferenceKind.other, 0,
+ executable.typeParameters.length));
+ }
+ for (UnlinkedTypedef typedef in unit.typedefs) {
+ privateNamespace.putIfAbsent(
+ typedef.name,
+ () => new _Meaning(unitNum, PrelinkedReferenceKind.typedef, 0,
+ typedef.typeParameters.length));
+ }
+ for (UnlinkedVariable variable in unit.variables) {
+ privateNamespace.putIfAbsent(variable.name,
+ () => new _Meaning(unitNum, PrelinkedReferenceKind.other, 0, 0));
+ }
+ }
+
+ /**
+ * Filter the export namespace for the library whose URI is reachable from
+ * [definingUnit] via [relativeUri], retaining only those names accepted by
+ * [combinators], and store the resulting names in [result]. Names that
+ * already exist in [result] are not overwritten.
+ */
+ void filterExportNamespace(String relativeUri,
+ List<UnlinkedCombinator> combinators, Map<String, _Meaning> result) {
+ Map<String, _Meaning> exportNamespace = computeExportNamespace(relativeUri);
+ NameFilter filter = new NameFilter.forCombinators(combinators);
+ exportNamespace.forEach((String name, _Meaning meaning) {
+ if (filter.accepts(name) && !result.containsKey(name)) {
+ result[name] = meaning;
+ }
+ });
+ }
+
+ /**
+ * Wrapper around [getImport] that caches the return value in [importCache].
+ */
+ UnlinkedPublicNamespace getImportCached(String relativeUri) {
+ return importCache.putIfAbsent(relativeUri, () => getImport(relativeUri));
+ }
+
+ /**
+ * Wrapper around [getPart] that caches the return value in [partCache] and
+ * updates [importCache] appropriately.
+ */
+ UnlinkedUnit getPartCached(String relativeUri) {
+ return partCache.putIfAbsent(relativeUri, () {
+ UnlinkedUnit unit = getPart(relativeUri);
+ importCache[relativeUri] = unit?.publicNamespace;
+ return unit;
+ });
+ }
+
+ /**
+ * Compute the set of relative URIs of all the compilation units in the
+ * library whose URI is reachable from [definingUnit] via [relativeUri].
+ */
+ List<String> getUnitUris(String relativeUri) {
+ List<String> result = <String>[relativeUri];
+ UnlinkedPublicNamespace publicNamespace = getImportCached(relativeUri);
+ if (publicNamespace != null) {
+ result.addAll(publicNamespace.parts
+ .map((String uri) => resolveUri(relativeUri, uri)));
+ }
+ return result;
+ }
+
+ /**
+ * Process a single `import` declaration in the library being prelinked. The
+ * return value is the index of the imported library in [dependencies].
+ */
+ int handleImport(UnlinkedImport import) {
+ String uri = import.isImplicit ? 'dart:core' : import.uri;
+ Map<String, _Meaning> targetNamespace = null;
+ if (import.prefixReference != 0) {
+ // The name introduced by an import declaration can't have a prefix of
+ // its own.
+ assert(
+ definingUnit.references[import.prefixReference].prefixReference == 0);
+ String prefix = definingUnit.references[import.prefixReference].name;
+ _Meaning prefixMeaning = privateNamespace[prefix];
+ if (prefixMeaning is _PrefixMeaning) {
+ targetNamespace = prefixMeaning.namespace;
+ }
+ } else {
+ targetNamespace = privateNamespace;
+ }
+ filterExportNamespace(uri, import.combinators, targetNamespace);
+ return uriToDependency[uri];
+ }
+
+ /**
+ * Produce a [PrelinkedUnit] for the given [unit], by resolving every one of
+ * its references.
+ */
+ PrelinkedUnitBuilder linkUnit(UnlinkedUnit unit) {
+ if (unit == null) {
+ return encodePrelinkedUnit(ctx);
+ }
+ Map<int, Map<String, _Meaning>> prefixNamespaces =
+ <int, Map<String, _Meaning>>{};
+ List<PrelinkedReferenceBuilder> references = <PrelinkedReferenceBuilder>[];
+ for (int i = 0; i < unit.references.length; i++) {
+ UnlinkedReference reference = unit.references[i];
+ Map<String, _Meaning> namespace;
+ if (reference.prefixReference != 0) {
+ // Prefix references must always point backward.
+ assert(reference.prefixReference < i);
+ namespace = prefixNamespaces[reference.prefixReference];
+ // Prefix references must always point to proper prefixes.
+ assert(namespace != null);
+ } else {
+ namespace = privateNamespace;
+ }
+ _Meaning meaning = namespace[reference.name];
+ if (meaning != null) {
+ if (meaning is _PrefixMeaning) {
+ prefixNamespaces[i] = meaning.namespace;
+ }
+ references.add(meaning.encode(ctx));
+ } else {
+ references.add(encodePrelinkedReference(ctx,
+ kind: PrelinkedReferenceKind.unresolved));
+ }
+ }
+ return encodePrelinkedUnit(ctx, references: references);
+ }
+
+ /**
+ * Form the [PrelinkedLibrary] for the [definingUnit] that was passed to the
+ * constructor.
+ */
+ PrelinkedLibraryBuilder prelink() {
+ // Gather up the unlinked summaries for all the compilation units in the
+ // library.
+ List<UnlinkedUnit> units = getUnitUris(null).map(getPartCached).toList();
+
+ // Create the private namespace for the library by gathering all the names
+ // defined in its compilation units.
+ for (int unitNum = 0; unitNum < units.length; unitNum++) {
+ UnlinkedUnit unit = units[unitNum];
+ if (unit != null) {
+ extractPrivateNames(unit, unitNum);
+ }
+ }
+
+ // Fill in prefixes defined in import declarations.
+ for (var import in units[0].imports) {
scheglov 2016/01/10 05:15:12 Type?
Paul Berry 2016/01/10 22:15:17 Done.
+ if (import.prefixReference != 0) {
+ privateNamespace.putIfAbsent(
+ units[0].references[import.prefixReference].name,
+ () => new _PrefixMeaning());
+ }
+ }
+
+ // Fill in imported names.
+ List<int> importDependencies =
+ definingUnit.imports.map(handleImport).toList();
+
+ // Link each compilation unit.
+ List<PrelinkedUnitBuilder> linkedUnits = units.map(linkUnit).toList();
+
+ return encodePrelinkedLibrary(ctx,
+ units: linkedUnits,
+ dependencies: dependencies,
+ importDependencies: importDependencies);
+ }
+
+ /**
+ * Resolve [relativeUri] relative to [sourceUri]. Works correctly if
+ * [sourceUri] is also relative.
+ */
+ String resolveUri(String sourceUri, String relativeUri) {
+ if (sourceUri == null) {
+ return relativeUri;
+ } else {
+ return Uri.parse(sourceUri).resolve(relativeUri).toString();
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698