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

Side by Side Diff: pkg/front_end/lib/src/incremental_kernel_generator_impl.dart

Issue 2976003002: Implement IncrementalKernelGeneratorImpl using KernelDriver. (Closed)
Patch Set: 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
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';
8 import 'package:front_end/incremental_kernel_generator.dart'; 7 import 'package:front_end/incremental_kernel_generator.dart';
9 import 'package:front_end/src/base/api_signature.dart';
10 import 'package:front_end/src/base/performace_logger.dart'; 8 import 'package:front_end/src/base/performace_logger.dart';
11 import 'package:front_end/src/base/processed_options.dart'; 9 import 'package:front_end/src/base/processed_options.dart';
12 import 'package:front_end/src/fasta/dill/dill_library_builder.dart';
13 import 'package:front_end/src/fasta/dill/dill_target.dart';
14 import 'package:front_end/src/fasta/kernel/kernel_target.dart';
15 import 'package:front_end/src/fasta/kernel/utils.dart';
16 import 'package:front_end/src/fasta/ticker.dart';
17 import 'package:front_end/src/fasta/translate_uri.dart'; 10 import 'package:front_end/src/fasta/translate_uri.dart';
18 import 'package:front_end/src/incremental/byte_store.dart';
19 import 'package:front_end/src/incremental/file_state.dart'; 11 import 'package:front_end/src/incremental/file_state.dart';
20 import 'package:kernel/binary/ast_from_binary.dart'; 12 import 'package:front_end/src/incremental/kernel_driver.dart';
21 import 'package:kernel/kernel.dart' hide Source; 13 import 'package:kernel/kernel.dart' hide Source;
22 import 'package:kernel/target/targets.dart' show TargetFlags;
23 import 'package:kernel/target/vm_fasta.dart' show VmFastaTarget;
24 import 'package:meta/meta.dart'; 14 import 'package:meta/meta.dart';
25 15
26 /// Implementation of [IncrementalKernelGenerator]. 16 /// Implementation of [IncrementalKernelGenerator].
27 /// 17 ///
28 /// TODO(scheglov) Update the documentation. 18 /// TODO(scheglov) Update the documentation.
29 /// 19 ///
30 /// Theory of operation: an instance of [IncrementalResolvedAstGenerator] is 20 /// Theory of operation: an instance of [IncrementalResolvedAstGenerator] is
31 /// used to obtain resolved ASTs, and these are fed into kernel code generation 21 /// used to obtain resolved ASTs, and these are fed into kernel code generation
32 /// logic. 22 /// logic.
33 class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator { 23 class IncrementalKernelGeneratorImpl implements IncrementalKernelGenerator {
34 /// The version of data format, should be incremented on every format change. 24 /// The version of data format, should be incremented on every format change.
35 static const int DATA_VERSION = 1; 25 static const int DATA_VERSION = 1;
36 26
37 /// The compiler options, such as the [FileSystem], the SDK dill location,
38 /// etc.
39 final ProcessedOptions _options;
40
41 /// The object that knows how to resolve "package:" and "dart:" URIs.
42 final TranslateUri _uriTranslator;
43
44 /// The logger to report compilation progress. 27 /// The logger to report compilation progress.
45 final PerformanceLog _logger; 28 final PerformanceLog _logger;
46 29
47 /// The byte storage to get and put serialized data.
48 final ByteStore _byteStore;
49
50 /// The URI of the program entry point. 30 /// The URI of the program entry point.
51 final Uri _entryPoint; 31 final Uri _entryPoint;
52 32
53 /// The function to notify when files become used or unused, or `null`. 33 /// The function to notify when files become used or unused, or `null`.
54 final WatchUsedFilesFn _watchFn; 34 final WatchUsedFilesFn _watchFn;
55 35
56 /// The salt to mix into all hashes used as keys for serialized data. 36 /// TODO(scheglov) document
57 List<int> _salt; 37 KernelDriver _driver;
58
59 /// The current file system state.
60 FileSystemState _fsState;
61 38
62 /// Latest compilation signatures produced by [computeDelta] for libraries. 39 /// Latest compilation signatures produced by [computeDelta] for libraries.
63 final Map<Uri, String> _latestSignature = {}; 40 final Map<Uri, String> _latestSignature = {};
64 41
65 /// The set of absolute file URIs that were reported through [invalidate]
66 /// and not checked for actual changes yet.
67 final Set<Uri> _invalidatedFiles = new Set<Uri>();
68
69 /// The object that provides additional information for tests. 42 /// The object that provides additional information for tests.
70 final _TestView _testView = new _TestView(); 43 _TestView _testView;
71 44
72 IncrementalKernelGeneratorImpl( 45 IncrementalKernelGeneratorImpl(
73 this._options, this._uriTranslator, this._entryPoint, 46 ProcessedOptions options, TranslateUri uriTranslator, this._entryPoint,
74 {WatchUsedFilesFn watch}) 47 {WatchUsedFilesFn watch})
75 : _logger = _options.logger, 48 : _logger = options.logger,
76 _byteStore = _options.byteStore,
77 _watchFn = watch { 49 _watchFn = watch {
78 _computeSalt(); 50 _testView = new _TestView(this);
79 51
80 Future<Null> onFileAdded(Uri uri) { 52 Future<Null> onFileAdded(Uri uri) {
81 if (_watchFn != null) { 53 if (_watchFn != null) {
82 return _watchFn(uri, true); 54 return _watchFn(uri, true);
83 } 55 }
84 return new Future.value(); 56 return new Future.value();
85 } 57 }
86 58
87 _fsState = new FileSystemState(_options.byteStore, _options.fileSystem, 59 _driver = new KernelDriver(_logger, options.fileSystem, options.byteStore,
88 _uriTranslator, _salt, onFileAdded); 60 uriTranslator, options.strongMode,
61 fileAddedFn: onFileAdded);
89 } 62 }
90 63
91 /// Return the object that provides additional information for tests. 64 /// Return the object that provides additional information for tests.
92 @visibleForTesting 65 @visibleForTesting
93 _TestView get test => _testView; 66 _TestView get test => _testView;
94 67
95 @override 68 @override
96 Future<DeltaProgram> computeDelta() async { 69 Future<DeltaProgram> computeDelta() async {
97 return await _logger.runAsync('Compute delta', () async { 70 return await _logger.runAsync('Compute delta', () async {
98 await _refreshInvalidatedFiles(); 71 KernelResult kernelResult = await _driver.getKernel(_entryPoint);
72 List<LibraryCycleResult> results = kernelResult.results;
99 73
100 // Ensure that the graph starting at the entry point is ready. 74 // The file graph might have changed, perform GC.
101 FileState entryLibrary = 75 await _gc();
102 await _logger.runAsync('Build graph of files', () async {
103 return await _fsState.getFile(_entryPoint);
104 });
105
106 List<LibraryCycle> cycles = _logger.run('Compute library cycles', () {
107 List<LibraryCycle> cycles = entryLibrary.topologicalOrder;
108 _logger.writeln('Computed ${cycles.length} cycles.');
109 return cycles;
110 });
111
112 CanonicalName nameRoot = new CanonicalName.root();
113 DillTarget dillTarget = new DillTarget(
114 new Ticker(isVerbose: false),
115 _uriTranslator,
116 new VmFastaTarget(new TargetFlags(strongMode: _options.strongMode)));
117
118 List<_LibraryCycleResult> results = [];
119 _testView.compiledCycles.clear();
120 await _logger.runAsync('Compute results for cycles', () async {
121 for (LibraryCycle cycle in cycles) {
122 _LibraryCycleResult result =
123 await _compileCycle(nameRoot, dillTarget, cycle);
124 results.add(result);
125 }
126 });
127
128 Program program = new Program(nameRoot: nameRoot);
129 76
130 // The set of affected library cycles (have different signatures). 77 // The set of affected library cycles (have different signatures).
131 final affectedLibraryCycles = new Set<LibraryCycle>(); 78 final affectedLibraryCycles = new Set<LibraryCycle>();
132 for (_LibraryCycleResult result in results) { 79 for (LibraryCycleResult result in results) {
133 for (Library library in result.kernelLibraries) { 80 for (Library library in result.kernelLibraries) {
134 Uri uri = library.importUri; 81 Uri uri = library.importUri;
135 if (_latestSignature[uri] != result.signature) { 82 if (_latestSignature[uri] != result.signature) {
136 _latestSignature[uri] = result.signature; 83 _latestSignature[uri] = result.signature;
137 affectedLibraryCycles.add(result.cycle); 84 affectedLibraryCycles.add(result.cycle);
138 } 85 }
139 } 86 }
140 } 87 }
141 88
142 // The set of affected library cycles (have different signatures), 89 // The set of affected library cycles (have different signatures),
143 // or libraries that import or export affected libraries (so VM might 90 // or libraries that import or export affected libraries (so VM might
144 // have inlined some code from affected libraries into them). 91 // have inlined some code from affected libraries into them).
145 final vmRequiredLibraryCycles = new Set<LibraryCycle>(); 92 final vmRequiredLibraryCycles = new Set<LibraryCycle>();
146 93
147 void gatherVmRequiredLibraryCycles(LibraryCycle cycle) { 94 void gatherVmRequiredLibraryCycles(LibraryCycle cycle) {
148 if (vmRequiredLibraryCycles.add(cycle)) { 95 if (vmRequiredLibraryCycles.add(cycle)) {
149 cycle.directUsers.forEach(gatherVmRequiredLibraryCycles); 96 cycle.directUsers.forEach(gatherVmRequiredLibraryCycles);
150 } 97 }
151 } 98 }
152 99
153 affectedLibraryCycles.forEach(gatherVmRequiredLibraryCycles); 100 affectedLibraryCycles.forEach(gatherVmRequiredLibraryCycles);
154 101
155 // Add required libraries. 102 // Add required libraries.
156 for (_LibraryCycleResult result in results) { 103 Program program = new Program(nameRoot: kernelResult.nameRoot);
104 for (LibraryCycleResult result in results) {
157 if (vmRequiredLibraryCycles.contains(result.cycle)) { 105 if (vmRequiredLibraryCycles.contains(result.cycle)) {
158 for (Library library in result.kernelLibraries) { 106 for (Library library in result.kernelLibraries) {
159 program.libraries.add(library); 107 program.libraries.add(library);
160 library.parent = program; 108 library.parent = program;
161 } 109 }
162 } 110 }
163 } 111 }
164 112
165 // Set the main method. 113 // Set the main method.
166 if (program.libraries.isNotEmpty) { 114 if (program.libraries.isNotEmpty) {
167 for (Library library in results.last.kernelLibraries) { 115 for (Library library in results.last.kernelLibraries) {
168 if (library.importUri == _entryPoint) { 116 if (library.importUri == _entryPoint) {
169 program.mainMethod = library.procedures.firstWhere( 117 program.mainMethod = library.procedures.firstWhere(
170 (procedure) => procedure.name.name == 'main', 118 (procedure) => procedure.name.name == 'main',
171 orElse: () => null); 119 orElse: () => null);
172 break; 120 break;
173 } 121 }
174 } 122 }
175 } 123 }
176 124
177 return new DeltaProgram(program); 125 return new DeltaProgram(program);
178 }); 126 });
179 } 127 }
180 128
181 @override 129 @override
182 void invalidate(Uri uri) { 130 void invalidate(Uri uri) {
183 _invalidatedFiles.add(uri); 131 _driver.invalidate(uri);
184 } 132 }
185 133
186 @override 134 @override
187 void invalidateAll() { 135 void invalidateAll() {
188 _invalidatedFiles.addAll(_fsState.fileUris); 136 _driver.invalidateAll();
189 } 137 }
190 138
191 /// Ensure that [dillTarget] includes the [cycle] libraries. It already 139 /// TODO(scheglov) document
192 /// contains all the libraries that sorted before the given [cycle] in 140 Future<Null> _gc() async {
193 /// topological order. Return the result with the cycle libraries. 141 var removedFiles = _driver.fsState.gc(_entryPoint);
194 Future<_LibraryCycleResult> _compileCycle( 142 if (removedFiles.isNotEmpty && _watchFn != null) {
195 CanonicalName nameRoot, DillTarget dillTarget, LibraryCycle cycle) async { 143 for (var removedFile in removedFiles) {
196 return _logger.runAsync('Compile cycle $cycle', () async { 144 await _watchFn(removedFile.fileUri, false);
197 String signature = _getCycleSignature(cycle);
198
199 _logger.writeln('Signature: $signature.');
200 var kernelKey = '$signature.kernel';
201
202 // We need kernel libraries for these URIs.
203 var libraryUris = new Set<Uri>();
204 var libraryUriToFile = <Uri, FileState>{};
205 for (FileState library in cycle.libraries) {
206 Uri uri = library.uri;
207 libraryUris.add(uri);
208 libraryUriToFile[uri] = library;
209 }
210
211 Future<Null> appendNewDillLibraries(Program program) async {
212 List<DillLibraryBuilder> libraryBuilders = dillTarget.loader
213 .appendLibraries(program, (uri) => libraryUris.contains(uri));
214
215 // Compute local scopes.
216 await dillTarget.buildOutlines();
217
218 // Compute export scopes.
219 _computeExportScopes(dillTarget, libraryUriToFile, libraryBuilders);
220 }
221
222 // Check if there is already a bundle with these libraries.
223 List<int> bytes = _byteStore.get(kernelKey);
224 if (bytes != null) {
225 return _logger.runAsync('Read serialized libraries', () async {
226 var program = new Program(nameRoot: nameRoot);
227 var reader = new BinaryBuilder(bytes);
228 reader.readProgram(program);
229
230 await appendNewDillLibraries(program);
231
232 return new _LibraryCycleResult(cycle, signature, program.libraries);
233 });
234 }
235
236 // Create KernelTarget and configure it for compiling the cycle URIs.
237 KernelTarget kernelTarget =
238 new KernelTarget(_fsState.fileSystemView, dillTarget, _uriTranslator);
239 for (FileState library in cycle.libraries) {
240 kernelTarget.read(library.uri);
241 }
242
243 // Compile the cycle libraries into a new full program.
244 Program program = await _logger
245 .runAsync('Compile ${cycle.libraries.length} libraries', () async {
246 await kernelTarget.buildOutlines(nameRoot: nameRoot);
247 return await kernelTarget.buildProgram();
248 });
249 _testView.compiledCycles.add(cycle);
250
251 // Add newly compiled libraries into DILL.
252 await appendNewDillLibraries(program);
253
254 List<Library> kernelLibraries = program.libraries
255 .where((library) => libraryUris.contains(library.importUri))
256 .toList();
257
258 _logger.run('Serialize ${kernelLibraries.length} libraries', () {
259 program.uriToSource.clear();
260 List<int> bytes =
261 serializeProgram(program, filter: kernelLibraries.contains);
262 _byteStore.put(kernelKey, bytes);
263 _logger.writeln('Stored ${bytes.length} bytes.');
264 });
265
266 return new _LibraryCycleResult(cycle, signature, kernelLibraries);
267 });
268 }
269
270 /// Compute exports scopes for a new strongly connected cycle of [libraries].
271 /// The [dillTarget] can be used to access libraries from previous cycles.
272 /// TODO(scheglov) Remove/replace this when Kernel has export scopes.
273 void _computeExportScopes(DillTarget dillTarget,
274 Map<Uri, FileState> uriToFile, List<DillLibraryBuilder> libraries) {
275 bool wasChanged = false;
276 do {
277 wasChanged = false;
278 for (DillLibraryBuilder library in libraries) {
279 FileState file = uriToFile[library.uri];
280 for (NamespaceExport export in file.exports) {
281 DillLibraryBuilder exportedLibrary =
282 dillTarget.loader.read(export.library.uri, -1, accessor: library);
283 if (exportedLibrary != null) {
284 exportedLibrary.exports.forEach((name, member) {
285 if (export.isExposed(name) &&
286 library.addToExportScope(name, member)) {
287 wasChanged = true;
288 }
289 });
290 } else {
291 // TODO(scheglov) How to handle this?
292 }
293 }
294 }
295 } while (wasChanged);
296 }
297
298 /// Compute salt and put into [_salt].
299 void _computeSalt() {
300 var saltBuilder = new ApiSignature();
301 saltBuilder.addInt(DATA_VERSION);
302 saltBuilder.addBool(_options.strongMode);
303 saltBuilder.addString(_entryPoint.toString());
304 _salt = saltBuilder.toByteList();
305 }
306
307 String _getCycleSignature(LibraryCycle cycle) {
308 bool hasMixinApplication =
309 cycle.libraries.any((library) => library.hasMixinApplicationLibrary);
310 var signatureBuilder = new ApiSignature();
311 signatureBuilder.addBytes(_salt);
312 Set<FileState> transitiveFiles = cycle.libraries
313 .map((library) => library.transitiveFiles)
314 .expand((files) => files)
315 .toSet();
316 signatureBuilder.addInt(transitiveFiles.length);
317
318 // Append API signatures of transitive files.
319 for (var file in transitiveFiles) {
320 signatureBuilder.addBytes(file.uriBytes);
321 // TODO(scheglov): Stop using content hashes here, when Kernel stops
322 // copying methods of mixed-in classes.
323 // https://github.com/dart-lang/sdk/issues/29881
324 if (hasMixinApplication) {
325 signatureBuilder.addBytes(file.contentHash);
326 } else {
327 signatureBuilder.addBytes(file.apiSignature);
328 } 145 }
329 } 146 }
330
331 // Append content hashes of the cycle files.
332 for (var library in cycle.libraries) {
333 signatureBuilder.addBytes(library.contentHash);
334 for (var part in library.partFiles) {
335 signatureBuilder.addBytes(part.contentHash);
336 }
337 }
338
339 return signatureBuilder.toHex();
340 } 147 }
341
342 /// Refresh all the invalidated files and update dependencies.
343 Future<Null> _refreshInvalidatedFiles() async {
344 await _logger.runAsync('Refresh invalidated files', () async {
345 // Create a copy to avoid concurrent modifications.
346 var invalidatedFiles = _invalidatedFiles.toList();
347 _invalidatedFiles.clear();
348
349 // Refresh the files.
350 for (var fileUri in invalidatedFiles) {
351 var file = _fsState.getFileByFileUri(fileUri);
352 if (file != null) {
353 _logger.writeln('Refresh $fileUri');
354 await file.refresh();
355 }
356 }
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 });
366 }
367 }
368
369 /// Compilation result for a library cycle.
370 class _LibraryCycleResult {
371 final LibraryCycle cycle;
372
373 /// The signature of the result.
374 ///
375 /// It is based on the full content of the libraries in the [cycle], and
376 /// either API signatures of the transitive dependencies (usually), or
377 /// the full content of them (in the [cycle] has a library with a mixin
378 /// application).
379 final String signature;
380
381 /// Kernel libraries for libraries in the [cycle]. Bodies of dependencies
382 /// are not included, but but references to those dependencies are included.
383 final List<Library> kernelLibraries;
384
385 _LibraryCycleResult(this.cycle, this.signature, this.kernelLibraries);
386 } 148 }
387 149
388 @visibleForTesting 150 @visibleForTesting
389 class _TestView { 151 class _TestView {
390 /// The list of [LibraryCycle]s compiled for the last delta. 152 final IncrementalKernelGeneratorImpl _generator;
391 /// It does not include libraries which were read from the cache. 153
392 final List<LibraryCycle> compiledCycles = []; 154 _TestView(this._generator);
155
156 /// The [KernelDriver] that is used to actually compile.
157 KernelDriver get driver => _generator._driver;
393 } 158 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698