blob: aa51fbf29b7c4e844ffce2a1ec65233731c0581c [file] [log] [blame]
// Copyright (c) 2017, 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:async';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/analysis/driver.dart' as driver;
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.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/util/absolute_path.dart';
import 'package:front_end/incremental_resolved_ast_generator.dart';
import 'package:front_end/src/base/file_repository.dart';
import 'package:front_end/src/base/processed_options.dart';
import 'package:front_end/src/base/source.dart';
import 'package:front_end/src/dependency_grapher_impl.dart';
import 'package:path/src/context.dart';
dynamic unimplemented() {
// TODO(paulberry): get rid of this.
throw new UnimplementedError();
}
/// Implementation of [IncrementalKernelGenerator].
///
/// Theory of operation: this class is a thin wrapper around
/// [driver.AnalysisDriver]. When the client requests a new delta, we forward
/// the request to the analysis driver. When the client calls an invalidate
/// method, we ensure that the proper files will be re-read next time a delta is
/// requested.
///
/// Note that the analysis driver expects to be able to read file contents
/// synchronously based on filesystem path rather than asynchronously based on
/// URI, so the file contents are first read into memory using the asynchronous
/// FileSystem API, and then these are fed into the analysis driver using a
/// proxy implementation of [ResourceProvider]. TODO(paulberry): make this (and
/// other proxies in this file) unnecessary.
class IncrementalResolvedAstGeneratorImpl
implements IncrementalResolvedAstGenerator {
driver.AnalysisDriverScheduler _scheduler;
final _fileRepository = new FileRepository();
_ResourceProviderProxy _resourceProvider;
driver.AnalysisDriver _driver;
bool _isInitialized = false;
final ProcessedOptions _options;
final Uri _source;
bool _schedulerStarted = false;
final _fileState = <Uri, String>{};
IncrementalResolvedAstGeneratorImpl(this._source, this._options);
@override
Future<DeltaLibraries> computeDelta() async {
if (!_isInitialized) {
await init();
}
// The analysis driver doesn't currently support an asynchronous file API,
// so we have to find all the files first to read their contents.
// TODO(paulberry): this is an unnecessary source of duplicate work and
// should be eliminated ASAP.
var graph =
await graphForProgram([_source], _options, fileReader: _fileReader);
// TODO(paulberry): collect no-longer-referenced files from _fileState and
// _fileRepository.
var libraries = <Uri, ResolvedLibrary>{};
if (!_schedulerStarted) {
_scheduler.start();
_schedulerStarted = true;
}
for (var libraryCycle in graph.topologicallySortedCycles) {
for (var libraryUri in libraryCycle.libraries.keys) {
var libraryNode = libraryCycle.libraries[libraryUri];
for (var partUri in libraryNode.parts) {
// TODO(paulberry): resolve the part URI.
_fileReader(partUri, partUri);
}
}
for (var libraryUri in libraryCycle.libraries.keys) {
var libraryNode = libraryCycle.libraries[libraryUri];
var result =
await _driver.getResult(_fileRepository.pathForUri(libraryUri));
// TODO(paulberry): handle errors.
var definingCompilationUnit = result.unit;
var partUnits = <Uri, CompilationUnit>{};
for (var partUri in libraryNode.parts) {
// Really we ought to have a driver API that lets us request a
// specific part of a given library. Otherwise we will run into
// problems if a part is included in multiple libraries.
// TODO(paulberry): address this.
var partResult =
await _driver.getResult(_fileRepository.pathForUri(partUri));
// TODO(paulberry): handle errors.
partUnits[partUri] = partResult.unit;
}
libraries[libraryUri] =
new ResolvedLibrary(definingCompilationUnit, partUnits);
}
}
_driver.addFile(_fileRepository.pathForUri(_source));
// TODO(paulberry): stop the scheduler
return new DeltaLibraries(libraries);
}
Future<Null> init() async {
// TODO(paulberry): can we just use null?
var performanceLog = new driver.PerformanceLog(new _NullStringSink());
_scheduler = new driver.AnalysisDriverScheduler(performanceLog);
_resourceProvider = new _ResourceProviderProxy(_fileRepository);
// TODO(paulberry): MemoryByteStore leaks memory (it never discards data).
// Do something better here.
var byteStore = new MemoryByteStore();
// TODO(paulberry): can we just use null?
var fileContentOverlay = new FileContentOverlay();
var sdkContext = new AnalysisContextImpl();
var sdkBundle = await _options.getSdkSummary();
var dartSdk = new _DartSdkProxy(sdkBundle, sdkContext, _fileRepository);
sdkContext.sourceFactory =
new SourceFactory([new DartUriResolver(dartSdk)]);
var sourceFactory = new _SourceFactoryProxy(dartSdk, _fileRepository);
var analysisOptions = new AnalysisOptionsImpl();
_driver = new driver.AnalysisDriver(
_scheduler,
performanceLog,
_resourceProvider,
byteStore,
fileContentOverlay,
'front_end',
sourceFactory,
analysisOptions,
sdkBundle: sdkBundle);
_isInitialized = true;
}
@override
void invalidate(String path) {
throw new UnimplementedError();
}
@override
void invalidateAll() {
_fileState.clear();
_fileRepository.clearContents();
// TODO(paulberry): verify that this has an effect (requires a multi-file
// test).
if (_isInitialized) {
_driver.knownFiles.forEach(_driver.changeFile);
}
}
Future<String> _fileReader(Uri originalUri, Uri resolvedUri) async {
String contents = _fileState[resolvedUri] ??=
await _options.fileSystem.entityForUri(resolvedUri).readAsString();
_fileRepository.store(originalUri, contents);
return contents;
}
}
class _DartSdkProxy implements DartSdk {
final PackageBundle summary;
final AnalysisContext context;
final FileRepository _fileRepository;
_DartSdkProxy(this.summary, this.context, this._fileRepository);
@override
PackageBundle getLinkedBundle() => summary;
@override
Source mapDartUri(String uriString) {
var uri = Uri.parse(uriString);
return new _SourceProxy(
uri, _fileRepository.pathForUri(uri, allocate: true));
}
noSuchMethod(Invocation invocation) => unimplemented();
}
class _FileProxy implements File {
final _SourceProxy _source;
final _ResourceProviderProxy _resourceProvider;
_FileProxy(this._source, this._resourceProvider);
@override
String get path => _source.fullName;
@override
String get shortName => path;
@override
Source createSource([Uri uri]) {
assert(uri == null);
return _source;
}
noSuchMethod(Invocation invocation) => unimplemented();
@override
String readAsStringSync() {
return _resourceProvider._fileRepository.contentsForPath(path);
}
}
/// A string sink that ignores everything written to it.
class _NullStringSink implements StringSink {
void write(Object obj) {}
void writeAll(Iterable objects, [String separator = ""]) {}
void writeCharCode(int charCode) {}
void writeln([Object obj = ""]) {}
}
class _ResourceProviderProxy implements ResourceProvider {
final FileRepository _fileRepository;
_ResourceProviderProxy(this._fileRepository);
@override
AbsolutePathContext get absolutePathContext => throw new UnimplementedError();
@override
Context get pathContext => throw new UnimplementedError();
@override
File getFile(String path) {
return new _FileProxy(
new _SourceProxy(_fileRepository.uriForPath(path), path), this);
}
@override
Folder getFolder(String path) => throw new UnimplementedError();
@override
Future<List<int>> getModificationTimes(List<Source> sources) =>
throw new UnimplementedError();
@override
Resource getResource(String path) => throw new UnimplementedError();
@override
Folder getStateLocation(String pluginId) => throw new UnimplementedError();
}
class _SourceFactoryProxy implements SourceFactory {
@override
final DartSdk dartSdk;
final FileRepository _fileRepository;
@override
AnalysisContext context;
_SourceFactoryProxy(this.dartSdk, this._fileRepository);
@override
SourceFactory clone() => new _SourceFactoryProxy(dartSdk, _fileRepository);
@override
Source forUri(String absoluteUri) {
Uri uri = Uri.parse(absoluteUri);
return new _SourceProxy(
uri, _fileRepository.pathForUri(uri, allocate: true));
}
noSuchMethod(Invocation invocation) => unimplemented();
Source resolveUri(Source containingSource, String containedUri) {
// TODO(paulberry): re-use code from dependency_grapher_impl, and support
// SDK URI resolution logic.
String absoluteUri =
resolveRelativeUri(containingSource?.uri, Uri.parse(containedUri))
.toString();
return forUri(absoluteUri);
}
@override
Uri restoreUri(Source source) => source.uri;
}
class _SourceProxy extends BasicSource {
@override
final String fullName;
_SourceProxy(Uri uri, this.fullName) : super(uri);
int get modificationStamp => 0;
noSuchMethod(Invocation invocation) => unimplemented();
}