| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 import 'dart:async'; | |
| 6 | |
| 7 import 'package:analyzer/dart/ast/ast.dart'; | |
| 8 import 'package:analyzer/file_system/file_system.dart'; | |
| 9 import 'package:analyzer/src/context/context.dart'; | |
| 10 import 'package:analyzer/src/dart/analysis/driver.dart' as driver; | |
| 11 import 'package:analyzer/src/dart/analysis/file_state.dart'; | |
| 12 import 'package:analyzer/src/generated/engine.dart'; | |
| 13 import 'package:analyzer/src/generated/sdk.dart'; | |
| 14 import 'package:analyzer/src/generated/source.dart'; | |
| 15 import 'package:analyzer/src/summary/idl.dart'; | |
| 16 import 'package:analyzer/src/util/absolute_path.dart'; | |
| 17 import 'package:front_end/incremental_resolved_ast_generator.dart'; | |
| 18 import 'package:front_end/src/base/file_repository.dart'; | |
| 19 import 'package:front_end/src/base/performace_logger.dart'; | |
| 20 import 'package:front_end/src/base/processed_options.dart'; | |
| 21 import 'package:front_end/src/base/resolve_relative_uri.dart'; | |
| 22 import 'package:front_end/src/base/source.dart'; | |
| 23 import 'package:front_end/src/dependency_grapher_impl.dart'; | |
| 24 import 'package:front_end/src/incremental/byte_store.dart'; | |
| 25 import 'package:path/src/context.dart'; | |
| 26 | |
| 27 dynamic unimplemented() { | |
| 28 // TODO(paulberry): get rid of this. | |
| 29 throw new UnimplementedError(); | |
| 30 } | |
| 31 | |
| 32 /// Implementation of [IncrementalKernelGenerator]. | |
| 33 /// | |
| 34 /// Theory of operation: this class is a thin wrapper around | |
| 35 /// [driver.AnalysisDriver]. When the client requests a new delta, we forward | |
| 36 /// the request to the analysis driver. When the client calls an invalidate | |
| 37 /// method, we ensure that the proper files will be re-read next time a delta is | |
| 38 /// requested. | |
| 39 /// | |
| 40 /// Note that the analysis driver expects to be able to read file contents | |
| 41 /// synchronously based on filesystem path rather than asynchronously based on | |
| 42 /// URI, so the file contents are first read into memory using the asynchronous | |
| 43 /// FileSystem API, and then these are fed into the analysis driver using a | |
| 44 /// proxy implementation of [ResourceProvider]. TODO(paulberry): make this (and | |
| 45 /// other proxies in this file) unnecessary. | |
| 46 class IncrementalResolvedAstGeneratorImpl | |
| 47 implements IncrementalResolvedAstGenerator { | |
| 48 driver.AnalysisDriverScheduler _scheduler; | |
| 49 final _fileRepository = new FileRepository(); | |
| 50 _ResourceProviderProxy _resourceProvider; | |
| 51 driver.AnalysisDriver _driver; | |
| 52 bool _isInitialized = false; | |
| 53 final ProcessedOptions _options; | |
| 54 final Uri _source; | |
| 55 bool _schedulerStarted = false; | |
| 56 final _fileState = <Uri, String>{}; | |
| 57 | |
| 58 IncrementalResolvedAstGeneratorImpl(this._source, this._options); | |
| 59 | |
| 60 @override | |
| 61 Future<DeltaLibraries> computeDelta() async { | |
| 62 if (!_isInitialized) { | |
| 63 await init(); | |
| 64 } | |
| 65 // The analysis driver doesn't currently support an asynchronous file API, | |
| 66 // so we have to find all the files first to read their contents. | |
| 67 // TODO(paulberry): this is an unnecessary source of duplicate work and | |
| 68 // should be eliminated ASAP. | |
| 69 var graph = | |
| 70 await graphForProgram([_source], _options, fileReader: _fileReader); | |
| 71 // TODO(paulberry): collect no-longer-referenced files from _fileState and | |
| 72 // _fileRepository. | |
| 73 var libraries = <Uri, Map<Uri, CompilationUnit>>{}; | |
| 74 if (!_schedulerStarted) { | |
| 75 _scheduler.start(); | |
| 76 _schedulerStarted = true; | |
| 77 } | |
| 78 for (var libraryCycle in graph.topologicallySortedCycles) { | |
| 79 for (var libraryUri in libraryCycle.libraries.keys) { | |
| 80 var libraryNode = libraryCycle.libraries[libraryUri]; | |
| 81 for (var partUri in libraryNode.parts) { | |
| 82 // TODO(paulberry): resolve the part URI. | |
| 83 _fileReader(partUri, partUri); | |
| 84 } | |
| 85 } | |
| 86 for (var libraryUri in libraryCycle.libraries.keys) { | |
| 87 var libraryNode = libraryCycle.libraries[libraryUri]; | |
| 88 var result = | |
| 89 await _driver.getResult(_fileRepository.pathForUri(libraryUri)); | |
| 90 // TODO(paulberry): handle errors. | |
| 91 var units = {libraryUri: result.unit}; | |
| 92 for (var partUri in libraryNode.parts) { | |
| 93 // Really we ought to have a driver API that lets us request a | |
| 94 // specific part of a given library. Otherwise we will run into | |
| 95 // problems if a part is included in multiple libraries. | |
| 96 // TODO(paulberry): address this. | |
| 97 var partResult = | |
| 98 await _driver.getResult(_fileRepository.pathForUri(partUri)); | |
| 99 // TODO(paulberry): handle errors. | |
| 100 units[partUri] = partResult.unit; | |
| 101 } | |
| 102 libraries[libraryUri] = units; | |
| 103 } | |
| 104 } | |
| 105 _driver.addFile(_fileRepository.pathForUri(_source)); | |
| 106 // TODO(paulberry): stop the scheduler | |
| 107 return new DeltaLibraries(libraries); | |
| 108 } | |
| 109 | |
| 110 Future<Null> init() async { | |
| 111 // TODO(paulberry): can we just use null? | |
| 112 var performanceLog = new PerformanceLog(new _NullStringSink()); | |
| 113 _scheduler = new driver.AnalysisDriverScheduler(performanceLog); | |
| 114 _resourceProvider = new _ResourceProviderProxy(_fileRepository); | |
| 115 // TODO(paulberry): MemoryByteStore leaks memory (it never discards data). | |
| 116 // Do something better here. | |
| 117 var byteStore = new MemoryByteStore(); | |
| 118 // TODO(paulberry): can we just use null? | |
| 119 var fileContentOverlay = new FileContentOverlay(); | |
| 120 var sdkContext = new AnalysisContextImpl(); | |
| 121 var sdkBundle = await _options.getSdkSummary(); | |
| 122 var dartSdk = new _DartSdkProxy(sdkBundle, sdkContext, _fileRepository); | |
| 123 sdkContext.sourceFactory = | |
| 124 new SourceFactory([new DartUriResolver(dartSdk)]); | |
| 125 | |
| 126 var sourceFactory = new _SourceFactoryProxy(dartSdk, _fileRepository); | |
| 127 var analysisOptions = new AnalysisOptionsImpl(); | |
| 128 _driver = new driver.AnalysisDriver( | |
| 129 _scheduler, | |
| 130 performanceLog, | |
| 131 _resourceProvider, | |
| 132 byteStore, | |
| 133 fileContentOverlay, | |
| 134 null, | |
| 135 sourceFactory, | |
| 136 analysisOptions, | |
| 137 sdkBundle: sdkBundle); | |
| 138 _isInitialized = true; | |
| 139 } | |
| 140 | |
| 141 @override | |
| 142 void invalidate(String path) { | |
| 143 throw new UnimplementedError(); | |
| 144 } | |
| 145 | |
| 146 @override | |
| 147 void invalidateAll() { | |
| 148 _fileState.clear(); | |
| 149 _fileRepository.clearContents(); | |
| 150 // TODO(paulberry): verify that this has an effect (requires a multi-file | |
| 151 // test). | |
| 152 if (_isInitialized) { | |
| 153 _driver.knownFiles.forEach(_driver.changeFile); | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 Future<String> _fileReader(Uri originalUri, Uri resolvedUri) async { | |
| 158 String contents = _fileState[resolvedUri] ??= | |
| 159 await _options.fileSystem.entityForUri(resolvedUri).readAsString(); | |
| 160 _fileRepository.store(originalUri, contents); | |
| 161 return contents; | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 class _DartSdkProxy implements DartSdk { | |
| 166 final PackageBundle summary; | |
| 167 | |
| 168 final AnalysisContext context; | |
| 169 | |
| 170 final FileRepository _fileRepository; | |
| 171 | |
| 172 _DartSdkProxy(this.summary, this.context, this._fileRepository); | |
| 173 | |
| 174 @override | |
| 175 PackageBundle getLinkedBundle() => summary; | |
| 176 | |
| 177 @override | |
| 178 Source mapDartUri(String uriString) { | |
| 179 var uri = Uri.parse(uriString); | |
| 180 return new _SourceProxy( | |
| 181 uri, _fileRepository.pathForUri(uri, allocate: true)); | |
| 182 } | |
| 183 | |
| 184 noSuchMethod(Invocation invocation) => unimplemented(); | |
| 185 } | |
| 186 | |
| 187 class _FileProxy implements File { | |
| 188 final _SourceProxy _source; | |
| 189 | |
| 190 final _ResourceProviderProxy _resourceProvider; | |
| 191 | |
| 192 _FileProxy(this._source, this._resourceProvider); | |
| 193 | |
| 194 @override | |
| 195 String get path => _source.fullName; | |
| 196 | |
| 197 @override | |
| 198 String get shortName => path; | |
| 199 | |
| 200 @override | |
| 201 Source createSource([Uri uri]) { | |
| 202 assert(uri == null); | |
| 203 return _source; | |
| 204 } | |
| 205 | |
| 206 noSuchMethod(Invocation invocation) => unimplemented(); | |
| 207 | |
| 208 @override | |
| 209 String readAsStringSync() { | |
| 210 return _resourceProvider._fileRepository.contentsForPath(path); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 /// A string sink that ignores everything written to it. | |
| 215 class _NullStringSink implements StringSink { | |
| 216 void write(Object obj) {} | |
| 217 void writeAll(Iterable objects, [String separator = ""]) {} | |
| 218 void writeCharCode(int charCode) {} | |
| 219 void writeln([Object obj = ""]) {} | |
| 220 } | |
| 221 | |
| 222 class _ResourceProviderProxy implements ResourceProvider { | |
| 223 final FileRepository _fileRepository; | |
| 224 | |
| 225 _ResourceProviderProxy(this._fileRepository); | |
| 226 | |
| 227 @override | |
| 228 AbsolutePathContext get absolutePathContext => throw new UnimplementedError(); | |
| 229 | |
| 230 @override | |
| 231 Context get pathContext => throw new UnimplementedError(); | |
| 232 | |
| 233 @override | |
| 234 File getFile(String path) { | |
| 235 return new _FileProxy( | |
| 236 new _SourceProxy(_fileRepository.uriForPath(path), path), this); | |
| 237 } | |
| 238 | |
| 239 @override | |
| 240 Folder getFolder(String path) => throw new UnimplementedError(); | |
| 241 | |
| 242 @override | |
| 243 Future<List<int>> getModificationTimes(List<Source> sources) => | |
| 244 throw new UnimplementedError(); | |
| 245 | |
| 246 @override | |
| 247 Resource getResource(String path) => throw new UnimplementedError(); | |
| 248 | |
| 249 @override | |
| 250 Folder getStateLocation(String pluginId) => throw new UnimplementedError(); | |
| 251 } | |
| 252 | |
| 253 class _SourceFactoryProxy implements SourceFactory { | |
| 254 @override | |
| 255 final DartSdk dartSdk; | |
| 256 | |
| 257 final FileRepository _fileRepository; | |
| 258 | |
| 259 @override | |
| 260 AnalysisContext context; | |
| 261 | |
| 262 _SourceFactoryProxy(this.dartSdk, this._fileRepository); | |
| 263 | |
| 264 @override | |
| 265 SourceFactory clone() => new _SourceFactoryProxy(dartSdk, _fileRepository); | |
| 266 | |
| 267 @override | |
| 268 Source forUri(String absoluteUri) { | |
| 269 Uri uri = Uri.parse(absoluteUri); | |
| 270 return forUri2(uri); | |
| 271 } | |
| 272 | |
| 273 @override | |
| 274 Source forUri2(Uri absoluteUri) { | |
| 275 return new _SourceProxy( | |
| 276 absoluteUri, _fileRepository.pathForUri(absoluteUri, allocate: true)); | |
| 277 } | |
| 278 | |
| 279 noSuchMethod(Invocation invocation) => unimplemented(); | |
| 280 | |
| 281 Source resolveUri(Source containingSource, String containedUri) { | |
| 282 // TODO(paulberry): re-use code from dependency_grapher_impl, and support | |
| 283 // SDK URI resolution logic. | |
| 284 String absoluteUri = | |
| 285 resolveRelativeUri(containingSource?.uri, Uri.parse(containedUri)) | |
| 286 .toString(); | |
| 287 return forUri(absoluteUri); | |
| 288 } | |
| 289 | |
| 290 @override | |
| 291 Uri restoreUri(Source source) => source.uri; | |
| 292 } | |
| 293 | |
| 294 class _SourceProxy extends BasicSource { | |
| 295 @override | |
| 296 final String fullName; | |
| 297 | |
| 298 _SourceProxy(Uri uri, this.fullName) : super(uri); | |
| 299 | |
| 300 int get modificationStamp => 0; | |
| 301 | |
| 302 noSuchMethod(Invocation invocation) => unimplemented(); | |
| 303 } | |
| OLD | NEW |