| Index: packages/analyzer/lib/src/summary/prelink.dart
|
| diff --git a/packages/analyzer/lib/src/summary/prelink.dart b/packages/analyzer/lib/src/summary/prelink.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..062676ee924d98a48bba11d8532392171bfd8492
|
| --- /dev/null
|
| +++ b/packages/analyzer/lib/src/summary/prelink.dart
|
| @@ -0,0 +1,544 @@
|
| +// Copyright (c) 2016, 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.
|
| +
|
| +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';
|
| +
|
| +/**
|
| + * Create a [LinkedLibraryBuilder] 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].
|
| + */
|
| +LinkedLibraryBuilder prelink(UnlinkedUnit definingUnit, GetPartCallback getPart,
|
| + GetImportCallback getImport, GetDeclaredVariable getDeclaredVariable) {
|
| + return new _Prelinker(definingUnit, getPart, getImport, getDeclaredVariable)
|
| + .prelink();
|
| +}
|
| +
|
| +/**
|
| + * Return the raw string value of the variable with the given [name],
|
| + * or `null` of the variable is not defined.
|
| + */
|
| +typedef String GetDeclaredVariable(String name);
|
| +
|
| +/**
|
| + * 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 of the library being prelinked.
|
| + *
|
| + * 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. [relativeUri] should be
|
| + * interpreted relative to the defining compilation unit of the library being
|
| + * prelinked.
|
| + *
|
| + * If no file exists at the given uri, `null` should be returned.
|
| + */
|
| +typedef UnlinkedUnit GetPartCallback(String relativeUri);
|
| +
|
| +/**
|
| + * A [_Meaning] representing a class.
|
| + */
|
| +class _ClassMeaning extends _Meaning {
|
| + final Map<String, _Meaning> namespace;
|
| +
|
| + _ClassMeaning(int unit, int dependency, int numTypeParameters, this.namespace)
|
| + : super(unit, ReferenceKind.classOrEnum, dependency, numTypeParameters);
|
| +}
|
| +
|
| +/**
|
| + * 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 ReferenceKind 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 [LinkedExportName], using the given [name].
|
| + */
|
| + LinkedExportName encodeExportName(String name) {
|
| + return new LinkedExportNameBuilder(
|
| + name: name, dependency: dependency, unit: unit, kind: kind);
|
| + }
|
| +
|
| +/**
|
| + * Encode this [_Meaning] as a [LinkedReference].
|
| + */
|
| + LinkedReferenceBuilder encodeReference() {
|
| + return new LinkedReferenceBuilder(
|
| + 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, ReferenceKind.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 UnlinkedUnit definingUnit;
|
| + final GetPartCallback getPart;
|
| + final GetImportCallback getImport;
|
| + final GetDeclaredVariable getDeclaredVariable;
|
| +
|
| + /**
|
| + * 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 = <String, _Meaning>{
|
| + 'dynamic': new _Meaning(0, ReferenceKind.classOrEnum, 0, 0),
|
| + 'void': new _Meaning(0, ReferenceKind.classOrEnum, 0, 0)
|
| + };
|
| +
|
| + /**
|
| + * List of dependencies of the library being prelinked. This will be output
|
| + * to [LinkedLibrary.dependencies].
|
| + */
|
| + final List<LinkedDependencyBuilder> dependencies = <LinkedDependencyBuilder>[
|
| + new LinkedDependencyBuilder()
|
| + ];
|
| +
|
| + /**
|
| + * 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(this.definingUnit, this.getPart, this.getImport,
|
| + this.getDeclaredVariable) {
|
| + 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;
|
| + List<String> unitUris = getUnitUris(relativeUri);
|
| + LinkedDependencyBuilder linkedDependency = new LinkedDependencyBuilder(
|
| + uri: relativeUri, parts: unitUris.sublist(1));
|
| + dependencies.add(linkedDependency);
|
| +
|
| + Map<String, _Meaning> aggregated = <String, _Meaning>{};
|
| +
|
| + 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, () {
|
| + if (name.kind == ReferenceKind.classOrEnum) {
|
| + Map<String, _Meaning> namespace = <String, _Meaning>{};
|
| + name.members.forEach((executable) {
|
| + namespace[executable.name] = new _Meaning(
|
| + unitNum, executable.kind, 0, executable.numTypeParameters);
|
| + });
|
| + return new _ClassMeaning(
|
| + unitNum, dependency, name.numTypeParameters, namespace);
|
| + }
|
| + return 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.
|
| + *
|
| + * If [relativeUri] is `null` (meaning the export namespace of [definingUnit]
|
| + * should be computed), then names defined in [definingUnit] are ignored.
|
| + */
|
| + Map<String, _Meaning> computeExportNamespace(String relativeUri) {
|
| + Map<String, _Meaning> exportNamespace = relativeUri == null
|
| + ? <String, _Meaning>{}
|
| + : 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 relativeExportUri =
|
| + _selectUri(export.uri, export.configurations);
|
| + String exportUri = resolveUri(relativeUri, relativeExportUri);
|
| + NameFilter newFilter = filter.merge(
|
| + new NameFilter.forUnlinkedCombinators(export.combinators));
|
| + aggregatePublicNamespace(exportUri)
|
| + .forEach((String name, _Meaning meaning) {
|
| + if (newFilter.accepts(name) &&
|
| + !exportNamespace.containsKey(name)) {
|
| + exportNamespace[name] = meaning;
|
| + }
|
| + });
|
| + chaseExports(newFilter, 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, () {
|
| + Map<String, _Meaning> namespace = <String, _Meaning>{};
|
| + cls.fields.forEach((field) {
|
| + if (field.isStatic && field.isConst) {
|
| + namespace[field.name] =
|
| + new _Meaning(unitNum, ReferenceKind.propertyAccessor, 0, 0);
|
| + }
|
| + });
|
| + cls.executables.forEach((executable) {
|
| + ReferenceKind kind = null;
|
| + if (executable.kind == UnlinkedExecutableKind.constructor) {
|
| + kind = ReferenceKind.constructor;
|
| + } else if (executable.kind ==
|
| + UnlinkedExecutableKind.functionOrMethod &&
|
| + executable.isStatic) {
|
| + kind = ReferenceKind.method;
|
| + } else if (executable.kind == UnlinkedExecutableKind.getter &&
|
| + executable.isStatic) {
|
| + kind = ReferenceKind.propertyAccessor;
|
| + }
|
| + if (kind != null && executable.name.isNotEmpty) {
|
| + namespace[executable.name] = new _Meaning(
|
| + unitNum, kind, 0, executable.typeParameters.length);
|
| + }
|
| + });
|
| + return new _ClassMeaning(
|
| + unitNum, 0, cls.typeParameters.length, namespace);
|
| + });
|
| + }
|
| + for (UnlinkedEnum enm in unit.enums) {
|
| + privateNamespace.putIfAbsent(enm.name, () {
|
| + Map<String, _Meaning> namespace = <String, _Meaning>{};
|
| + enm.values.forEach((UnlinkedEnumValue value) {
|
| + namespace[value.name] =
|
| + new _Meaning(unitNum, ReferenceKind.propertyAccessor, 0, 0);
|
| + });
|
| + namespace['values'] =
|
| + new _Meaning(unitNum, ReferenceKind.propertyAccessor, 0, 0);
|
| + return new _ClassMeaning(unitNum, 0, 0, namespace);
|
| + });
|
| + }
|
| + for (UnlinkedExecutable executable in unit.executables) {
|
| + privateNamespace.putIfAbsent(
|
| + executable.name,
|
| + () => new _Meaning(
|
| + unitNum,
|
| + executable.kind == UnlinkedExecutableKind.functionOrMethod
|
| + ? ReferenceKind.topLevelFunction
|
| + : ReferenceKind.topLevelPropertyAccessor,
|
| + 0,
|
| + executable.typeParameters.length));
|
| + }
|
| + for (UnlinkedTypedef typedef in unit.typedefs) {
|
| + privateNamespace.putIfAbsent(
|
| + typedef.name,
|
| + () => new _Meaning(unitNum, ReferenceKind.typedef, 0,
|
| + typedef.typeParameters.length));
|
| + }
|
| + for (UnlinkedVariable variable in unit.variables) {
|
| + privateNamespace.putIfAbsent(
|
| + variable.name,
|
| + () => new _Meaning(
|
| + unitNum, ReferenceKind.topLevelPropertyAccessor, 0, 0));
|
| + if (!(variable.isConst || variable.isFinal)) {
|
| + privateNamespace.putIfAbsent(
|
| + variable.name + '=',
|
| + () => new _Meaning(
|
| + unitNum, ReferenceKind.topLevelPropertyAccessor, 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.forUnlinkedCombinators(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'
|
| + : _selectUri(import.uri, import.configurations);
|
| + 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 [LinkedUnit] for the given [unit], by resolving every one of
|
| + * its references.
|
| + */
|
| + LinkedUnitBuilder linkUnit(UnlinkedUnit unit) {
|
| + if (unit == null) {
|
| + return new LinkedUnitBuilder();
|
| + }
|
| + Map<int, Map<String, _Meaning>> prefixNamespaces =
|
| + <int, Map<String, _Meaning>>{};
|
| + List<LinkedReferenceBuilder> references = <LinkedReferenceBuilder>[];
|
| + for (int i = 0; i < unit.references.length; i++) {
|
| + UnlinkedReference reference = unit.references[i];
|
| + Map<String, _Meaning> namespace;
|
| + if (reference.prefixReference == 0) {
|
| + namespace = privateNamespace;
|
| + } else {
|
| + // Prefix references must always point backward.
|
| + assert(reference.prefixReference < i);
|
| + namespace = prefixNamespaces[reference.prefixReference];
|
| + // Expressions like 'a.b.c.d' cannot be prelinked.
|
| + namespace ??= const <String, _Meaning>{};
|
| + }
|
| + _Meaning meaning = namespace[reference.name];
|
| + if (meaning != null) {
|
| + if (meaning is _PrefixMeaning) {
|
| + prefixNamespaces[i] = meaning.namespace;
|
| + } else if (meaning is _ClassMeaning) {
|
| + prefixNamespaces[i] = meaning.namespace;
|
| + }
|
| + references.add(meaning.encodeReference());
|
| + } else {
|
| + references
|
| + .add(new LinkedReferenceBuilder(kind: ReferenceKind.unresolved));
|
| + }
|
| + }
|
| + return new LinkedUnitBuilder(references: references);
|
| + }
|
| +
|
| + /**
|
| + * Form the [LinkedLibrary] for the [definingUnit] that was passed to the
|
| + * constructor.
|
| + */
|
| + LinkedLibraryBuilder 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 exported names. This must be done before filling in prefixes
|
| + // defined in import declarations, because prefixes shouldn't shadow
|
| + // exports.
|
| + List<LinkedExportNameBuilder> exportNames = <LinkedExportNameBuilder>[];
|
| + computeExportNamespace(null).forEach((String name, _Meaning meaning) {
|
| + if (!privateNamespace.containsKey(name)) {
|
| + exportNames.add(meaning.encodeExportName(name));
|
| + }
|
| + });
|
| +
|
| + // Fill in prefixes defined in import declarations.
|
| + for (UnlinkedImport import in units[0].imports) {
|
| + if (import.prefixReference != 0) {
|
| + privateNamespace.putIfAbsent(
|
| + units[0].references[import.prefixReference].name,
|
| + () => new _PrefixMeaning());
|
| + }
|
| + }
|
| +
|
| + // Fill in imported and exported names.
|
| + List<int> importDependencies =
|
| + definingUnit.imports.map(handleImport).toList();
|
| + List<int> exportDependencies =
|
| + definingUnit.publicNamespace.exports.map((UnlinkedExportPublic exp) {
|
| + String uri = _selectUri(exp.uri, exp.configurations);
|
| + return uriToDependency[uri];
|
| + }).toList();
|
| +
|
| + // Link each compilation unit.
|
| + List<LinkedUnitBuilder> linkedUnits = units.map(linkUnit).toList();
|
| +
|
| + return new LinkedLibraryBuilder(
|
| + units: linkedUnits,
|
| + dependencies: dependencies,
|
| + importDependencies: importDependencies,
|
| + exportDependencies: exportDependencies,
|
| + exportNames: exportNames,
|
| + numPrelinkedDependencies: dependencies.length);
|
| + }
|
| +
|
| + /**
|
| + * 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 resolveRelativeUri(Uri.parse(sourceUri), Uri.parse(relativeUri))
|
| + .toString();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Return the URI of the first configuration from the given [configurations]
|
| + * which condition is satisfied, or the [defaultUri].
|
| + */
|
| + String _selectUri(
|
| + String defaultUri, List<UnlinkedConfiguration> configurations) {
|
| + for (UnlinkedConfiguration configuration in configurations) {
|
| + if (getDeclaredVariable(configuration.name) == configuration.value) {
|
| + return configuration.uri;
|
| + }
|
| + }
|
| + return defaultUri;
|
| + }
|
| +}
|
|
|