blob: c86790c60665c6afc0752d4ad46d4aac092e3bac [file] [log] [blame]
// Copyright (c) 2019, 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 'dart:collection';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/restricted_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/summary/summarize_elements.dart';
import 'package:analyzer/src/summary2/informative_data.dart';
import 'package:analyzer/src/summary2/link.dart' as summary2;
import 'package:analyzer/src/summary2/linked_bundle_context.dart' as summary2;
import 'package:analyzer/src/summary2/linked_element_factory.dart' as summary2;
import 'package:analyzer/src/summary2/reference.dart' as summary2;
import 'package:meta/meta.dart';
class DevCompilerResynthesizerBuilder {
final FileSystemState _fsState;
final SourceFactory _sourceFactory;
final DeclaredVariables _declaredVariables;
final AnalysisOptionsImpl _analysisOptions;
final SummaryDataStore _summaryData;
final List<Uri> _explicitSources;
_SourceCrawler _fileCrawler;
final List<_UnitInformativeData> _informativeData = [];
final PackageBundleAssembler _assembler;
List<int> summaryBytes;
RestrictedAnalysisContext context;
summary2.LinkedElementFactory elementFactory;
DevCompilerResynthesizerBuilder({
@required FileSystemState fsState,
@required SourceFactory sourceFactory,
@required DeclaredVariables declaredVariables,
@required AnalysisOptionsImpl analysisOptions,
@required SummaryDataStore summaryData,
@required List<Uri> explicitSources,
}) : _fsState = fsState,
_sourceFactory = sourceFactory,
_declaredVariables = declaredVariables,
_analysisOptions = analysisOptions,
_summaryData = summaryData,
_explicitSources = explicitSources,
_assembler = PackageBundleAssembler();
/// URIs of libraries that should be linked.
List<String> get libraryUris => _fileCrawler.libraryUris;
/// Link explicit sources, serialize [PackageBundle] into [summaryBytes].
///
/// Create a new [context], [resynthesizer] and [elementFactory].
void build() {
_fileCrawler = _SourceCrawler(
_fsState,
_sourceFactory,
_summaryData,
_explicitSources,
);
_fileCrawler.crawl();
_computeLinkedLibraries2();
summaryBytes = _assembler.assemble().toBuffer();
var bundle = PackageBundle.fromBuffer(summaryBytes);
// Create an analysis context to contain the state for this build unit.
var synchronousSession = SynchronousSession(
_analysisOptions,
_declaredVariables,
);
context = RestrictedAnalysisContext(synchronousSession, _sourceFactory);
_createElementFactory(bundle);
}
/// Link libraries, and fill [_assembler].
void _computeLinkedLibraries2() {
var inputLibraries = <summary2.LinkInputLibrary>[];
var sourceToUnit = _fileCrawler.sourceToUnit;
var librarySourcesToLink = <Source>[]
..addAll(_fileCrawler.librarySources)
..addAll(_fileCrawler._invalidLibrarySources);
for (var librarySource in librarySourcesToLink) {
var libraryUriStr = '${librarySource.uri}';
var unit = sourceToUnit[librarySource];
var inputUnits = <summary2.LinkInputUnit>[];
inputUnits.add(
summary2.LinkInputUnit(null, librarySource, false, unit),
);
_informativeData.add(
_UnitInformativeData(
libraryUriStr,
libraryUriStr,
createInformativeData(unit),
),
);
for (var directive in unit.directives) {
if (directive is PartDirective) {
var partRelativeUriStr = directive.uri.stringValue;
var partSource = _sourceFactory.resolveUri(
librarySource,
partRelativeUriStr,
);
// Add empty synthetic units for unresolved `part` URIs.
if (partSource == null) {
inputUnits.add(
summary2.LinkInputUnit(
partRelativeUriStr,
null,
true,
_fsState.unresolvedFile.parse(),
),
);
continue;
}
var partUnit = sourceToUnit[partSource];
inputUnits.add(
summary2.LinkInputUnit(
partRelativeUriStr,
partSource,
partSource == null,
partUnit,
),
);
var unitUriStr = '${partSource.uri}';
_informativeData.add(
_UnitInformativeData(
libraryUriStr,
unitUriStr,
createInformativeData(partUnit),
),
);
}
}
inputLibraries.add(
summary2.LinkInputLibrary(librarySource, inputUnits),
);
}
var analysisContext = RestrictedAnalysisContext(
SynchronousSession(_analysisOptions, _declaredVariables),
_sourceFactory,
);
var elementFactory = summary2.LinkedElementFactory(
analysisContext,
null,
summary2.Reference.root(),
);
for (var bundle in _summaryData.bundles) {
elementFactory.addBundle(
summary2.LinkedBundleContext(elementFactory, bundle.bundle2),
);
}
var linkResult = summary2.link(elementFactory, inputLibraries);
_assembler.setBundle2(linkResult.bundle);
}
void _createElementFactory(PackageBundle newBundle) {
elementFactory = summary2.LinkedElementFactory(
context,
null,
summary2.Reference.root(),
);
for (var bundle in _summaryData.bundles) {
elementFactory.addBundle(
summary2.LinkedBundleContext(elementFactory, bundle.bundle2),
);
}
elementFactory.addBundle(
summary2.LinkedBundleContext(elementFactory, newBundle.bundle2),
);
for (var unitData in _informativeData) {
elementFactory.setInformativeData(
unitData.libraryUriStr,
unitData.unitUriStr,
unitData.data,
);
}
var dartCore = elementFactory.libraryOfUri('dart:core');
var dartAsync = elementFactory.libraryOfUri('dart:async');
var typeProvider = TypeProviderImpl(dartCore, dartAsync);
context.typeProvider = typeProvider;
dartCore.createLoadLibraryFunction(typeProvider);
dartAsync.createLoadLibraryFunction(typeProvider);
}
}
class _SourceCrawler {
final FileSystemState _fsState;
final SourceFactory _sourceFactory;
final SummaryDataStore _summaryData;
final List<Uri> _explicitSources;
/// The pending list of sources to visit.
var _pendingSource = Queue<Uri>();
/// The sources that have been added to [_pendingSource], used to ensure
/// we only visit a given source once.
var _knownSources = Set<Uri>();
/// The set of URIs that expected to be libraries.
///
/// Some of the might turn out to have `part of` directive, and so reported
/// later. However we still must be able to provide some element for them
/// when requested via `import` or `export` directives.
final Set<Uri> _expectedLibraryUris = Set<Uri>();
/// The list of sources with URIs that [_expectedLibraryUris], but turned
/// out to be parts. We still add them into summaries, but don't resolve
/// them as units.
final List<Source> _invalidLibrarySources = [];
final Map<Source, CompilationUnit> sourceToUnit = {};
final List<String> libraryUris = [];
final List<Source> librarySources = [];
_SourceCrawler(
this._fsState,
this._sourceFactory,
this._summaryData,
this._explicitSources,
);
/// Starting with [_explicitSources], visit all transitive imports, exports,
/// parts, and create an unlinked unit for each (unless it is provided by an
/// input summary from [_summaryData]).
void crawl() {
_pendingSource.addAll(_explicitSources);
_knownSources.addAll(_explicitSources);
// Collect the unlinked units for all transitive sources.
//
// TODO(jmesserly): consider using parallelism via asynchronous IO here,
// once we fix debugger extension (web/web_command.dart) to allow async.
//
// It would let computation tasks (parsing/serializing unlinked units)
// proceed in parallel with reading the sources from disk.
while (_pendingSource.isNotEmpty) {
_visit(_pendingSource.removeFirst());
}
}
/// Visit the file with the given [uri], and fill its data.
void _visit(Uri uri) {
var uriStr = uri.toString();
// Maybe an input package contains the source.
if (_summaryData.hasUnlinkedUnit(uriStr)) {
return;
}
var source = _sourceFactory.forUri2(uri);
if (source == null) {
return;
}
var file = _fsState.getFileForPath(source.fullName);
var unit = file.parse();
sourceToUnit[source] = unit;
void enqueueSource(String relativeUri, bool shouldBeLibrary) {
var sourceUri = resolveRelativeUri(uri, Uri.parse(relativeUri));
if (_knownSources.add(sourceUri)) {
_pendingSource.add(sourceUri);
if (shouldBeLibrary) {
_expectedLibraryUris.add(sourceUri);
}
}
}
// Add reachable imports/exports/parts, if any.
var isPart = false;
for (var directive in unit.directives) {
if (directive is UriBasedDirective) {
if (directive is NamespaceDirective) {
enqueueSource(directive.uri.stringValue, true);
for (var config in directive.configurations) {
enqueueSource(config.uri.stringValue, true);
}
} else {
enqueueSource(directive.uri.stringValue, false);
}
} else if (directive is PartOfDirective) {
isPart = true;
}
}
// Remember library URIs, for linking and compiling.
if (!isPart) {
libraryUris.add(uriStr);
librarySources.add(source);
} else if (_expectedLibraryUris.contains(uri)) {
_invalidLibrarySources.add(source);
}
}
}
class _UnitInformativeData {
final String libraryUriStr;
final String unitUriStr;
final List<UnlinkedInformativeData> data;
_UnitInformativeData(this.libraryUriStr, this.unitUriStr, this.data);
}