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

Side by Side Diff: pkg/front_end/lib/src/incremental/kernel_driver.dart

Issue 2976003002: Implement IncrementalKernelGeneratorImpl using KernelDriver. (Closed)
Patch Set: Add comments. Created 3 years, 5 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 unified diff | Download patch
« no previous file with comments | « no previous file | pkg/front_end/lib/src/incremental_kernel_generator_impl.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:async'; 5 import 'dart:async';
6 6
7 import 'package:front_end/file_system.dart'; 7 import 'package:front_end/file_system.dart';
8 import 'package:front_end/incremental_kernel_generator.dart';
9 import 'package:front_end/src/base/api_signature.dart'; 8 import 'package:front_end/src/base/api_signature.dart';
10 import 'package:front_end/src/base/performace_logger.dart'; 9 import 'package:front_end/src/base/performace_logger.dart';
11 import 'package:front_end/src/base/processed_options.dart';
12 import 'package:front_end/src/fasta/dill/dill_library_builder.dart'; 10 import 'package:front_end/src/fasta/dill/dill_library_builder.dart';
13 import 'package:front_end/src/fasta/dill/dill_target.dart'; 11 import 'package:front_end/src/fasta/dill/dill_target.dart';
14 import 'package:front_end/src/fasta/kernel/kernel_target.dart'; 12 import 'package:front_end/src/fasta/kernel/kernel_target.dart';
15 import 'package:front_end/src/fasta/kernel/utils.dart'; 13 import 'package:front_end/src/fasta/kernel/utils.dart';
16 import 'package:front_end/src/fasta/ticker.dart'; 14 import 'package:front_end/src/fasta/ticker.dart';
17 import 'package:front_end/src/fasta/translate_uri.dart'; 15 import 'package:front_end/src/fasta/translate_uri.dart';
18 import 'package:front_end/src/incremental/byte_store.dart'; 16 import 'package:front_end/src/incremental/byte_store.dart';
19 import 'package:front_end/src/incremental/file_state.dart'; 17 import 'package:front_end/src/incremental/file_state.dart';
20 import 'package:kernel/binary/ast_from_binary.dart'; 18 import 'package:kernel/binary/ast_from_binary.dart';
21 import 'package:kernel/kernel.dart' hide Source; 19 import 'package:kernel/kernel.dart' hide Source;
22 import 'package:kernel/target/targets.dart' show TargetFlags; 20 import 'package:kernel/target/targets.dart' show TargetFlags;
23 import 'package:kernel/target/vm_fasta.dart' show VmFastaTarget; 21 import 'package:kernel/target/vm_fasta.dart' show VmFastaTarget;
24 import 'package:meta/meta.dart'; 22 import 'package:meta/meta.dart';
25 23
26 /// Implementation of [IncrementalKernelGenerator]. 24 /// This function is invoked for each newly discovered file, and the returned
25 /// [Future] is awaited before reading the file content.
26 typedef Future<Null> KernelDriverFileAddedFn(Uri uri);
27
28 /// This class computes [KernelResult]s for Dart files.
27 /// 29 ///
28 /// TODO(scheglov) Update the documentation. 30 /// Let the "current file state" represent a map from file URI to the file
31 /// contents most recently read from that file. When the driver needs to
32 /// access a file that is not in the current file state yet, it will call
33 /// the optional "file added" function, read the file and put it into the
34 /// current file state.
29 /// 35 ///
30 /// Theory of operation: an instance of [IncrementalResolvedAstGenerator] is 36 /// The client invokes [getKernel] to schedule computing the [KernelResult]
31 /// used to obtain resolved ASTs, and these are fed into kernel code generation 37 /// for a Dart file. The driver will eventually use the current file state
32 /// logic. 38 /// of the specified file and all files that it transitively depends on to
33 class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator { 39 /// compute corresponding kernel files (or read them from the [ByteStore]).
40 ///
41 /// A call to [invalidate] removes the specified file from the current file
42 /// state, so that it will be reread before any following [getKernel] will
43 /// return a result.
44 class KernelDriver {
34 /// The version of data format, should be incremented on every format change. 45 /// The version of data format, should be incremented on every format change.
35 static const int DATA_VERSION = 1; 46 static const int DATA_VERSION = 1;
36 47
37 /// The compiler options, such as the [FileSystem], the SDK dill location, 48 /// The logger to report compilation progress.
38 /// etc. 49 final PerformanceLog _logger;
39 final ProcessedOptions _options; 50
51 /// The [FileSystem] which should be used by the front end to access files.
52 final FileSystem _fileSystem;
53
54 /// The byte storage to get and put serialized data.
55 final ByteStore _byteStore;
40 56
41 /// The object that knows how to resolve "package:" and "dart:" URIs. 57 /// The object that knows how to resolve "package:" and "dart:" URIs.
42 final TranslateUri _uriTranslator; 58 final TranslateUri _uriTranslator;
43 59
44 /// The logger to report compilation progress. 60 /// Is `true` if strong mode analysis should be used.
45 final PerformanceLog _logger; 61 final bool _strongMode;
46 62
47 /// The byte storage to get and put serialized data. 63 /// The function that is invoked when a new file is about to be added to
48 final ByteStore _byteStore; 64 /// the current file state. The [Future] that it returns is awaited before
49 65 /// reading the file contents.
50 /// The URI of the program entry point. 66 final KernelDriverFileAddedFn _fileAddedFn;
51 final Uri _entryPoint;
52
53 /// The function to notify when files become used or unused, or `null`.
54 final WatchUsedFilesFn _watchFn;
55 67
56 /// The salt to mix into all hashes used as keys for serialized data. 68 /// The salt to mix into all hashes used as keys for serialized data.
57 List<int> _salt; 69 List<int> _salt;
58 70
59 /// The current file system state. 71 /// The current file system state.
60 FileSystemState _fsState; 72 FileSystemState _fsState;
61 73
62 /// Latest compilation signatures produced by [computeDelta] for libraries.
63 final Map<Uri, String> _latestSignature = {};
64
65 /// The set of absolute file URIs that were reported through [invalidate] 74 /// The set of absolute file URIs that were reported through [invalidate]
66 /// and not checked for actual changes yet. 75 /// and not checked for actual changes yet.
67 final Set<Uri> _invalidatedFiles = new Set<Uri>(); 76 final Set<Uri> _invalidatedFiles = new Set<Uri>();
68 77
69 /// The object that provides additional information for tests. 78 /// The object that provides additional information for tests.
70 final _TestView _testView = new _TestView(); 79 final _TestView _testView = new _TestView();
71 80
72 IncrementalKernelGeneratorImpl( 81 KernelDriver(this._logger, this._fileSystem, this._byteStore,
73 this._options, this._uriTranslator, this._entryPoint, 82 this._uriTranslator, this._strongMode,
74 {WatchUsedFilesFn watch}) 83 {KernelDriverFileAddedFn fileAddedFn})
75 : _logger = _options.logger, 84 : _fileAddedFn = fileAddedFn {
76 _byteStore = _options.byteStore,
77 _watchFn = watch {
78 _computeSalt(); 85 _computeSalt();
79 86
80 Future<Null> onFileAdded(Uri uri) { 87 Future<Null> onFileAdded(Uri uri) {
81 if (_watchFn != null) { 88 if (_fileAddedFn != null) {
82 return _watchFn(uri, true); 89 return _fileAddedFn(uri);
83 } 90 }
84 return new Future.value(); 91 return new Future.value();
85 } 92 }
86 93
87 _fsState = new FileSystemState(_options.byteStore, _options.fileSystem, 94 _fsState = new FileSystemState(
88 _uriTranslator, _salt, onFileAdded); 95 _byteStore, _fileSystem, _uriTranslator, _salt, onFileAdded);
89 } 96 }
90 97
98 /// Return the [FileSystemState] that contains the current file state.
99 FileSystemState get fsState => _fsState;
100
91 /// Return the object that provides additional information for tests. 101 /// Return the object that provides additional information for tests.
92 @visibleForTesting 102 @visibleForTesting
93 _TestView get test => _testView; 103 _TestView get test => _testView;
94 104
95 @override 105 /// Return the [KernelResult] for the Dart file with the given [uri].
96 Future<DeltaProgram> computeDelta() async { 106 ///
107 /// The [uri] must be absolute and normalized.
108 ///
109 /// The driver will update the current file state for any file previously
110 /// reported using [invalidate].
111 ///
112 /// If the driver has the cached result for the file with the current file
113 /// state, it is returned.
114 ///
115 /// Otherwise the driver will compute new kernel files and return them.
116 Future<KernelResult> getKernel(Uri uri) async {
97 return await _logger.runAsync('Compute delta', () async { 117 return await _logger.runAsync('Compute delta', () async {
98 await _refreshInvalidatedFiles(); 118 await _refreshInvalidatedFiles();
99 119
100 // Ensure that the graph starting at the entry point is ready. 120 // Ensure that the graph starting at the entry point is ready.
101 FileState entryLibrary = 121 FileState entryLibrary =
102 await _logger.runAsync('Build graph of files', () async { 122 await _logger.runAsync('Build graph of files', () async {
103 return await _fsState.getFile(_entryPoint); 123 return await _fsState.getFile(uri);
104 }); 124 });
105 125
106 List<LibraryCycle> cycles = _logger.run('Compute library cycles', () { 126 List<LibraryCycle> cycles = _logger.run('Compute library cycles', () {
107 List<LibraryCycle> cycles = entryLibrary.topologicalOrder; 127 List<LibraryCycle> cycles = entryLibrary.topologicalOrder;
108 _logger.writeln('Computed ${cycles.length} cycles.'); 128 _logger.writeln('Computed ${cycles.length} cycles.');
109 return cycles; 129 return cycles;
110 }); 130 });
111 131
112 CanonicalName nameRoot = new CanonicalName.root(); 132 CanonicalName nameRoot = new CanonicalName.root();
113 DillTarget dillTarget = new DillTarget( 133 DillTarget dillTarget = new DillTarget(
114 new Ticker(isVerbose: false), 134 new Ticker(isVerbose: false),
115 _uriTranslator, 135 _uriTranslator,
116 new VmFastaTarget(new TargetFlags(strongMode: _options.strongMode))); 136 new VmFastaTarget(new TargetFlags(strongMode: _strongMode)));
117 137
118 List<_LibraryCycleResult> results = []; 138 List<LibraryCycleResult> results = [];
119 _testView.compiledCycles.clear(); 139 _testView.compiledCycles.clear();
120 await _logger.runAsync('Compute results for cycles', () async { 140 await _logger.runAsync('Compute results for cycles', () async {
121 for (LibraryCycle cycle in cycles) { 141 for (LibraryCycle cycle in cycles) {
122 _LibraryCycleResult result = 142 LibraryCycleResult result =
123 await _compileCycle(nameRoot, dillTarget, cycle); 143 await _compileCycle(nameRoot, dillTarget, cycle);
124 results.add(result); 144 results.add(result);
125 } 145 }
126 }); 146 });
127 147
128 Program program = new Program(nameRoot: nameRoot); 148 return new KernelResult(nameRoot, results);
129
130 // The set of affected library cycles (have different signatures).
131 final affectedLibraryCycles = new Set<LibraryCycle>();
132 for (_LibraryCycleResult result in results) {
133 for (Library library in result.kernelLibraries) {
134 Uri uri = library.importUri;
135 if (_latestSignature[uri] != result.signature) {
136 _latestSignature[uri] = result.signature;
137 affectedLibraryCycles.add(result.cycle);
138 }
139 }
140 }
141
142 // The set of affected library cycles (have different signatures),
143 // or libraries that import or export affected libraries (so VM might
144 // have inlined some code from affected libraries into them).
145 final vmRequiredLibraryCycles = new Set<LibraryCycle>();
146
147 void gatherVmRequiredLibraryCycles(LibraryCycle cycle) {
148 if (vmRequiredLibraryCycles.add(cycle)) {
149 cycle.directUsers.forEach(gatherVmRequiredLibraryCycles);
150 }
151 }
152
153 affectedLibraryCycles.forEach(gatherVmRequiredLibraryCycles);
154
155 // Add required libraries.
156 for (_LibraryCycleResult result in results) {
157 if (vmRequiredLibraryCycles.contains(result.cycle)) {
158 for (Library library in result.kernelLibraries) {
159 program.libraries.add(library);
160 library.parent = program;
161 }
162 }
163 }
164
165 // Set the main method.
166 if (program.libraries.isNotEmpty) {
167 for (Library library in results.last.kernelLibraries) {
168 if (library.importUri == _entryPoint) {
169 program.mainMethod = library.procedures.firstWhere(
170 (procedure) => procedure.name.name == 'main',
171 orElse: () => null);
172 break;
173 }
174 }
175 }
176
177 return new DeltaProgram(program);
178 }); 149 });
179 } 150 }
180 151
181 @override 152 /// The file with the given [uri] might have changed - updated, added, or
153 /// removed. Or not, we don't know. Or it might have, but then changed back.
154 ///
155 /// The [uri] must be absolute and normalized file URI.
156 ///
157 /// Schedules the file contents for the [uri] to be read into the current
158 /// file state prior the next invocation of [getKernel] returns the result.
159 ///
160 /// Invocation of this method will not prevent a [Future] returned from
161 /// [getKernel] from completing with a result, but the result is not
162 /// guaranteed to be consistent with the new current file state after this
163 /// [invalidate] invocation.
182 void invalidate(Uri uri) { 164 void invalidate(Uri uri) {
183 _invalidatedFiles.add(uri); 165 _invalidatedFiles.add(uri);
184 } 166 }
185 167
186 @override 168 /// Flush the current file state completely.
169 ///
170 /// TODO(scheglov) Do we really need this functionality?
187 void invalidateAll() { 171 void invalidateAll() {
188 _invalidatedFiles.addAll(_fsState.fileUris); 172 _invalidatedFiles.addAll(_fsState.fileUris);
189 } 173 }
190 174
191 /// Ensure that [dillTarget] includes the [cycle] libraries. It already 175 /// Ensure that [dillTarget] includes the [cycle] libraries. It already
192 /// contains all the libraries that sorted before the given [cycle] in 176 /// contains all the libraries that sorted before the given [cycle] in
193 /// topological order. Return the result with the cycle libraries. 177 /// topological order. Return the result with the cycle libraries.
194 Future<_LibraryCycleResult> _compileCycle( 178 Future<LibraryCycleResult> _compileCycle(
195 CanonicalName nameRoot, DillTarget dillTarget, LibraryCycle cycle) async { 179 CanonicalName nameRoot, DillTarget dillTarget, LibraryCycle cycle) async {
196 return _logger.runAsync('Compile cycle $cycle', () async { 180 return _logger.runAsync('Compile cycle $cycle', () async {
197 String signature = _getCycleSignature(cycle); 181 String signature = _getCycleSignature(cycle);
198 182
199 _logger.writeln('Signature: $signature.'); 183 _logger.writeln('Signature: $signature.');
200 var kernelKey = '$signature.kernel'; 184 var kernelKey = '$signature.kernel';
201 185
202 // We need kernel libraries for these URIs. 186 // We need kernel libraries for these URIs.
203 var libraryUris = new Set<Uri>(); 187 var libraryUris = new Set<Uri>();
204 var libraryUriToFile = <Uri, FileState>{}; 188 var libraryUriToFile = <Uri, FileState>{};
(...skipping 17 matching lines...) Expand all
222 // Check if there is already a bundle with these libraries. 206 // Check if there is already a bundle with these libraries.
223 List<int> bytes = _byteStore.get(kernelKey); 207 List<int> bytes = _byteStore.get(kernelKey);
224 if (bytes != null) { 208 if (bytes != null) {
225 return _logger.runAsync('Read serialized libraries', () async { 209 return _logger.runAsync('Read serialized libraries', () async {
226 var program = new Program(nameRoot: nameRoot); 210 var program = new Program(nameRoot: nameRoot);
227 var reader = new BinaryBuilder(bytes); 211 var reader = new BinaryBuilder(bytes);
228 reader.readProgram(program); 212 reader.readProgram(program);
229 213
230 await appendNewDillLibraries(program); 214 await appendNewDillLibraries(program);
231 215
232 return new _LibraryCycleResult(cycle, signature, program.libraries); 216 return new LibraryCycleResult(cycle, signature, program.libraries);
233 }); 217 });
234 } 218 }
235 219
236 // Create KernelTarget and configure it for compiling the cycle URIs. 220 // Create KernelTarget and configure it for compiling the cycle URIs.
237 KernelTarget kernelTarget = 221 KernelTarget kernelTarget =
238 new KernelTarget(_fsState.fileSystemView, dillTarget, _uriTranslator); 222 new KernelTarget(_fsState.fileSystemView, dillTarget, _uriTranslator);
239 for (FileState library in cycle.libraries) { 223 for (FileState library in cycle.libraries) {
240 kernelTarget.read(library.uri); 224 kernelTarget.read(library.uri);
241 } 225 }
242 226
(...skipping 13 matching lines...) Expand all
256 .toList(); 240 .toList();
257 241
258 _logger.run('Serialize ${kernelLibraries.length} libraries', () { 242 _logger.run('Serialize ${kernelLibraries.length} libraries', () {
259 program.uriToSource.clear(); 243 program.uriToSource.clear();
260 List<int> bytes = 244 List<int> bytes =
261 serializeProgram(program, filter: kernelLibraries.contains); 245 serializeProgram(program, filter: kernelLibraries.contains);
262 _byteStore.put(kernelKey, bytes); 246 _byteStore.put(kernelKey, bytes);
263 _logger.writeln('Stored ${bytes.length} bytes.'); 247 _logger.writeln('Stored ${bytes.length} bytes.');
264 }); 248 });
265 249
266 return new _LibraryCycleResult(cycle, signature, kernelLibraries); 250 return new LibraryCycleResult(cycle, signature, kernelLibraries);
267 }); 251 });
268 } 252 }
269 253
270 /// Compute exports scopes for a new strongly connected cycle of [libraries]. 254 /// Compute exports scopes for a new strongly connected cycle of [libraries].
271 /// The [dillTarget] can be used to access libraries from previous cycles. 255 /// The [dillTarget] can be used to access libraries from previous cycles.
272 /// TODO(scheglov) Remove/replace this when Kernel has export scopes. 256 /// TODO(scheglov) Remove/replace this when Kernel has export scopes.
273 void _computeExportScopes(DillTarget dillTarget, 257 void _computeExportScopes(DillTarget dillTarget,
274 Map<Uri, FileState> uriToFile, List<DillLibraryBuilder> libraries) { 258 Map<Uri, FileState> uriToFile, List<DillLibraryBuilder> libraries) {
275 bool wasChanged = false; 259 bool wasChanged = false;
276 do { 260 do {
(...skipping 15 matching lines...) Expand all
292 } 276 }
293 } 277 }
294 } 278 }
295 } while (wasChanged); 279 } while (wasChanged);
296 } 280 }
297 281
298 /// Compute salt and put into [_salt]. 282 /// Compute salt and put into [_salt].
299 void _computeSalt() { 283 void _computeSalt() {
300 var saltBuilder = new ApiSignature(); 284 var saltBuilder = new ApiSignature();
301 saltBuilder.addInt(DATA_VERSION); 285 saltBuilder.addInt(DATA_VERSION);
302 saltBuilder.addBool(_options.strongMode); 286 saltBuilder.addBool(_strongMode);
303 saltBuilder.addString(_entryPoint.toString());
304 _salt = saltBuilder.toByteList(); 287 _salt = saltBuilder.toByteList();
305 } 288 }
306 289
307 String _getCycleSignature(LibraryCycle cycle) { 290 String _getCycleSignature(LibraryCycle cycle) {
308 bool hasMixinApplication = 291 bool hasMixinApplication =
309 cycle.libraries.any((library) => library.hasMixinApplicationLibrary); 292 cycle.libraries.any((library) => library.hasMixinApplicationLibrary);
310 var signatureBuilder = new ApiSignature(); 293 var signatureBuilder = new ApiSignature();
311 signatureBuilder.addBytes(_salt); 294 signatureBuilder.addBytes(_salt);
312 Set<FileState> transitiveFiles = cycle.libraries 295 Set<FileState> transitiveFiles = cycle.libraries
313 .map((library) => library.transitiveFiles) 296 .map((library) => library.transitiveFiles)
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 _invalidatedFiles.clear(); 330 _invalidatedFiles.clear();
348 331
349 // Refresh the files. 332 // Refresh the files.
350 for (var fileUri in invalidatedFiles) { 333 for (var fileUri in invalidatedFiles) {
351 var file = _fsState.getFileByFileUri(fileUri); 334 var file = _fsState.getFileByFileUri(fileUri);
352 if (file != null) { 335 if (file != null) {
353 _logger.writeln('Refresh $fileUri'); 336 _logger.writeln('Refresh $fileUri');
354 await file.refresh(); 337 await file.refresh();
355 } 338 }
356 } 339 }
357
358 // The file graph might have changed, perform GC.
359 var removedFiles = _fsState.gc(_entryPoint);
360 if (removedFiles.isNotEmpty && _watchFn != null) {
361 for (var removedFile in removedFiles) {
362 await _watchFn(removedFile.fileUri, false);
363 }
364 }
365 }); 340 });
366 } 341 }
367 } 342 }
368 343
344 /// The result of compiling of a single file.
345 class KernelResult {
346 final CanonicalName nameRoot;
347 final List<LibraryCycleResult> results;
348
349 KernelResult(this.nameRoot, this.results);
350 }
351
369 /// Compilation result for a library cycle. 352 /// Compilation result for a library cycle.
370 class _LibraryCycleResult { 353 class LibraryCycleResult {
371 final LibraryCycle cycle; 354 final LibraryCycle cycle;
372 355
373 /// The signature of the result. 356 /// The signature of the result.
374 /// 357 ///
375 /// It is based on the full content of the libraries in the [cycle], and 358 /// It is based on the full content of the libraries in the [cycle], and
376 /// either API signatures of the transitive dependencies (usually), or 359 /// either API signatures of the transitive dependencies (usually), or
377 /// the full content of them (in the [cycle] has a library with a mixin 360 /// the full content of them (in the [cycle] has a library with a mixin
378 /// application). 361 /// application).
379 final String signature; 362 final String signature;
380 363
381 /// Kernel libraries for libraries in the [cycle]. Bodies of dependencies 364 /// Kernel libraries for libraries in the [cycle]. Bodies of dependencies
382 /// are not included, but but references to those dependencies are included. 365 /// are not included, but but references to those dependencies are included.
383 final List<Library> kernelLibraries; 366 final List<Library> kernelLibraries;
384 367
385 _LibraryCycleResult(this.cycle, this.signature, this.kernelLibraries); 368 LibraryCycleResult(this.cycle, this.signature, this.kernelLibraries);
386 } 369 }
387 370
388 @visibleForTesting 371 @visibleForTesting
389 class _TestView { 372 class _TestView {
390 /// The list of [LibraryCycle]s compiled for the last delta. 373 /// The list of [LibraryCycle]s compiled for the last delta.
391 /// It does not include libraries which were read from the cache. 374 /// It does not include libraries which were read from the cache.
392 final List<LibraryCycle> compiledCycles = []; 375 final List<LibraryCycle> compiledCycles = [];
393 } 376 }
OLDNEW
« no previous file with comments | « no previous file | pkg/front_end/lib/src/incremental_kernel_generator_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698