OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 library fasta.kernel_target; | 5 library fasta.kernel_target; |
6 | 6 |
7 import 'dart:async' show Future; | 7 import 'dart:async' show Future; |
8 | 8 |
| 9 import 'dart:io' show File; |
| 10 |
9 import 'package:front_end/file_system.dart'; | 11 import 'package:front_end/file_system.dart'; |
10 | 12 |
11 import 'package:kernel/ast.dart' | 13 import 'package:kernel/ast.dart' |
12 show | 14 show |
13 Arguments, | 15 Arguments, |
14 CanonicalName, | 16 CanonicalName, |
15 Class, | 17 Class, |
16 Constructor, | 18 Constructor, |
17 DartType, | 19 DartType, |
18 DynamicType, | 20 DynamicType, |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 KernelNamedTypeBuilder, | 71 KernelNamedTypeBuilder, |
70 KernelProcedureBuilder, | 72 KernelProcedureBuilder, |
71 LibraryBuilder, | 73 LibraryBuilder, |
72 MemberBuilder, | 74 MemberBuilder, |
73 NamedTypeBuilder, | 75 NamedTypeBuilder, |
74 TypeBuilder, | 76 TypeBuilder, |
75 TypeDeclarationBuilder, | 77 TypeDeclarationBuilder, |
76 TypeVariableBuilder; | 78 TypeVariableBuilder; |
77 | 79 |
78 import 'verifier.dart' show verifyProgram; | 80 import 'verifier.dart' show verifyProgram; |
| 81 import 'kernel_outline_shaker.dart' |
| 82 show trimProgram, RetainedDataBuilder, RootsMarker; |
79 | 83 |
80 class KernelTarget extends TargetImplementation { | 84 class KernelTarget extends TargetImplementation { |
81 /// The [FileSystem] which should be used to access files. | 85 /// The [FileSystem] which should be used to access files. |
82 final FileSystem fileSystem; | 86 final FileSystem fileSystem; |
83 | 87 |
84 final DillTarget dillTarget; | 88 final DillTarget dillTarget; |
85 | 89 |
86 /// Shared with [CompilerContext]. | 90 /// Shared with [CompilerContext]. |
87 final Map<String, Source> uriToSource; | 91 final Map<String, Source> uriToSource; |
88 | 92 |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 cls.implementedTypes.clear(); | 207 cls.implementedTypes.clear(); |
204 cls.supertype = null; | 208 cls.supertype = null; |
205 cls.mixedInType = null; | 209 cls.mixedInType = null; |
206 builder.supertype = new KernelNamedTypeBuilder("Object", null, | 210 builder.supertype = new KernelNamedTypeBuilder("Object", null, |
207 builder.charOffset, builder.fileUri ?? Uri.parse(cls.fileUri)) | 211 builder.charOffset, builder.fileUri ?? Uri.parse(cls.fileUri)) |
208 ..builder = objectClassBuilder; | 212 ..builder = objectClassBuilder; |
209 builder.interfaces = null; | 213 builder.interfaces = null; |
210 builder.mixedInType = null; | 214 builder.mixedInType = null; |
211 } | 215 } |
212 | 216 |
213 void handleInputError(InputError error, {bool isFullProgram}) { | 217 void handleInputError(InputError error, |
| 218 {bool isFullProgram, bool trimDependencies: false}) { |
214 if (error != null) { | 219 if (error != null) { |
215 String message = error.format(); | 220 String message = error.format(); |
216 print(message); | 221 print(message); |
217 errors.add(message); | 222 errors.add(message); |
218 } | 223 } |
219 program = erroneousProgram(isFullProgram); | 224 program = erroneousProgram(isFullProgram); |
220 } | 225 } |
221 | 226 |
222 @override | 227 @override |
223 Future<Program> buildOutlines({CanonicalName nameRoot}) async { | 228 Future<Program> buildOutlines({CanonicalName nameRoot}) async { |
(...skipping 27 matching lines...) Expand all Loading... |
251 } | 256 } |
252 return program; | 257 return program; |
253 } | 258 } |
254 | 259 |
255 /// Build the kernel representation of the program loaded by this target. The | 260 /// Build the kernel representation of the program loaded by this target. The |
256 /// program will contain full bodies for the code loaded from sources, and | 261 /// program will contain full bodies for the code loaded from sources, and |
257 /// only references to the code loaded by the [DillTarget], which may or may | 262 /// only references to the code loaded by the [DillTarget], which may or may |
258 /// not include method bodies (depending on what was loaded into that target, | 263 /// not include method bodies (depending on what was loaded into that target, |
259 /// an outline or a full kernel program). | 264 /// an outline or a full kernel program). |
260 /// | 265 /// |
| 266 /// When [trimDependencies] is true, this also runs a tree-shaker that deletes |
| 267 /// anything from the [DillTarget] that is not needed for the source program, |
| 268 /// this includes function bodies and types that are not reachable. This |
| 269 /// option is currently in flux and the internal implementation might change. |
| 270 /// See [trimDependenciesInProgram] for more details. |
| 271 /// |
261 /// If [verify], run the default kernel verification on the resulting program. | 272 /// If [verify], run the default kernel verification on the resulting program. |
262 @override | 273 @override |
263 Future<Program> buildProgram({bool verify: false}) async { | 274 Future<Program> buildProgram( |
| 275 {bool verify: false, bool trimDependencies: false}) async { |
264 if (loader.first == null) return null; | 276 if (loader.first == null) return null; |
265 if (errors.isNotEmpty) { | 277 if (errors.isNotEmpty) { |
266 handleInputError(null, isFullProgram: true); | 278 handleInputError(null, |
| 279 isFullProgram: true, trimDependencies: trimDependencies); |
267 return program; | 280 return program; |
268 } | 281 } |
269 | 282 |
270 try { | 283 try { |
271 await loader.buildBodies(); | 284 await loader.buildBodies(); |
272 loader.finishStaticInvocations(); | 285 loader.finishStaticInvocations(); |
273 finishAllConstructors(); | 286 finishAllConstructors(); |
274 loader.finishNativeMethods(); | 287 loader.finishNativeMethods(); |
275 runBuildTransformations(); | 288 runBuildTransformations(); |
276 | 289 |
277 if (verify) this.verify(); | 290 if (verify) this.verify(); |
278 if (errors.isNotEmpty) { | 291 if (errors.isNotEmpty) { |
279 handleInputError(null, isFullProgram: true); | 292 handleInputError(null, |
| 293 isFullProgram: true, trimDependencies: trimDependencies); |
280 } | 294 } |
281 handleRecoverableErrors(loader.unhandledErrors); | 295 handleRecoverableErrors(loader.unhandledErrors); |
282 } on InputError catch (e) { | 296 } on InputError catch (e) { |
283 handleInputError(e, isFullProgram: true); | 297 handleInputError(e, |
| 298 isFullProgram: true, trimDependencies: trimDependencies); |
284 } catch (e, s) { | 299 } catch (e, s) { |
285 return reportCrash(e, s, loader?.currentUriForCrashReporting); | 300 return reportCrash(e, s, loader?.currentUriForCrashReporting); |
286 } | 301 } |
| 302 if (trimDependencies) trimDependenciesInProgram(); |
287 return program; | 303 return program; |
288 } | 304 } |
289 | 305 |
| 306 Future writeDepsFile(Uri output, Uri depsFile, |
| 307 {Iterable<Uri> extraDependencies}) async { |
| 308 String toRelativeFilePath(Uri uri) { |
| 309 // Ninja expects to find file names relative to the current working |
| 310 // directory. We've tried making them relative to the deps file, but that |
| 311 // doesn't work for downstream projects. Making them absolute also |
| 312 // doesn't work. |
| 313 // |
| 314 // We can test if it works by running ninja twice, for example: |
| 315 // |
| 316 // ninja -C xcodebuild/ReleaseX64 runtime_kernel -d explain |
| 317 // ninja -C xcodebuild/ReleaseX64 runtime_kernel -d explain |
| 318 // |
| 319 // The second time, ninja should say: |
| 320 // |
| 321 // ninja: Entering directory `xcodebuild/ReleaseX64' |
| 322 // ninja: no work to do. |
| 323 // |
| 324 // It's broken if it says something like this: |
| 325 // |
| 326 // ninja explain: expected depfile 'patched_sdk.d' to mention |
| 327 // 'patched_sdk/platform.dill', got |
| 328 // '/.../xcodebuild/ReleaseX64/patched_sdk/platform.dill' |
| 329 return Uri.parse(relativizeUri(uri, base: Uri.base)).toFilePath(); |
| 330 } |
| 331 |
| 332 if (loader.first == null) return null; |
| 333 StringBuffer sb = new StringBuffer(); |
| 334 sb.write(toRelativeFilePath(output)); |
| 335 sb.write(":"); |
| 336 Set<String> allDependencies = new Set<String>(); |
| 337 allDependencies.addAll(loader.getDependencies().map(toRelativeFilePath)); |
| 338 if (extraDependencies != null) { |
| 339 allDependencies.addAll(extraDependencies.map(toRelativeFilePath)); |
| 340 } |
| 341 for (String path in allDependencies) { |
| 342 sb.write(" "); |
| 343 sb.write(path); |
| 344 } |
| 345 sb.writeln(); |
| 346 await new File.fromUri(depsFile).writeAsString("$sb"); |
| 347 ticker.logMs("Wrote deps file"); |
| 348 } |
| 349 |
290 /// Adds a synthetic field named `#errors` to the main library that contains | 350 /// Adds a synthetic field named `#errors` to the main library that contains |
291 /// [recoverableErrors] formatted. | 351 /// [recoverableErrors] formatted. |
292 /// | 352 /// |
293 /// If [recoverableErrors] is empty, this method does nothing. | 353 /// If [recoverableErrors] is empty, this method does nothing. |
294 /// | 354 /// |
295 /// If there's no main library, this method uses [erroneousProgram] to | 355 /// If there's no main library, this method uses [erroneousProgram] to |
296 /// replace [program]. | 356 /// replace [program]. |
297 void handleRecoverableErrors(List<InputError> recoverableErrors) { | 357 void handleRecoverableErrors(List<InputError> recoverableErrors) { |
298 if (recoverableErrors.isEmpty) return; | 358 if (recoverableErrors.isEmpty) return; |
299 KernelLibraryBuilder mainLibrary = loader.first; | 359 KernelLibraryBuilder mainLibrary = loader.first; |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
623 backendTarget.performGlobalTransformations(loader.coreTypes, program, | 683 backendTarget.performGlobalTransformations(loader.coreTypes, program, |
624 logger: (String msg) => ticker.logMs(msg)); | 684 logger: (String msg) => ticker.logMs(msg)); |
625 } | 685 } |
626 | 686 |
627 void verify() { | 687 void verify() { |
628 var verifyErrors = verifyProgram(program); | 688 var verifyErrors = verifyProgram(program); |
629 errors.addAll(verifyErrors.map((error) => '$error')); | 689 errors.addAll(verifyErrors.map((error) => '$error')); |
630 ticker.logMs("Verified program"); | 690 ticker.logMs("Verified program"); |
631 } | 691 } |
632 | 692 |
| 693 /// Tree-shakes most code from the [dillTarget] by visiting all other |
| 694 /// libraries in [program] and marking the APIs from the [dillTarget] |
| 695 /// libraries that are in use. |
| 696 /// |
| 697 /// Note: while it's likely we'll do some trimming of programs for modular |
| 698 /// compilation, it is unclear at this time when and how that trimming should |
| 699 /// happen. We are likely going to remove the extra visitor my either marking |
| 700 /// things while code is built, or by handling tree-shaking after the fact |
| 701 /// (e.g. during serialization). |
| 702 trimDependenciesInProgram() { |
| 703 var toShake = |
| 704 dillTarget.loader.libraries.map((lib) => lib.importUri).toSet(); |
| 705 var isIncluded = (Uri uri) => !toShake.contains(uri); |
| 706 var data = new RetainedDataBuilder(); |
| 707 // TODO(sigmund): replace this step with data that is directly computed from |
| 708 // the builders: we should know the tree-shaking roots without having to do |
| 709 // a second visit over the tree. |
| 710 new RootsMarker(loader.coreTypes, data).run(program, isIncluded); |
| 711 trimProgram(program, data, isIncluded); |
| 712 } |
| 713 |
633 /// Return `true` if the given [library] was built by this [KernelTarget] | 714 /// Return `true` if the given [library] was built by this [KernelTarget] |
634 /// from sources, and not loaded from a [DillTarget]. | 715 /// from sources, and not loaded from a [DillTarget]. |
635 bool isSourceLibrary(Library library) { | 716 bool isSourceLibrary(Library library) { |
636 return loader.libraries.contains(library); | 717 return loader.libraries.contains(library); |
637 } | 718 } |
638 } | 719 } |
639 | 720 |
640 /// Looks for a constructor call that matches `super()` from a constructor in | 721 /// Looks for a constructor call that matches `super()` from a constructor in |
641 /// [cls]. Such a constructor may have optional arguments, but no required | 722 /// [cls]. Such a constructor may have optional arguments, but no required |
642 /// arguments. | 723 /// arguments. |
643 Constructor defaultSuperConstructor(Class cls) { | 724 Constructor defaultSuperConstructor(Class cls) { |
644 Class superclass = cls.superclass; | 725 Class superclass = cls.superclass; |
645 while (superclass != null && superclass.isMixinApplication) { | 726 while (superclass != null && superclass.isMixinApplication) { |
646 superclass = superclass.superclass; | 727 superclass = superclass.superclass; |
647 } | 728 } |
648 for (Constructor constructor in superclass.constructors) { | 729 for (Constructor constructor in superclass.constructors) { |
649 if (constructor.name.name.isEmpty) { | 730 if (constructor.name.name.isEmpty) { |
650 return constructor.function.requiredParameterCount == 0 | 731 return constructor.function.requiredParameterCount == 0 |
651 ? constructor | 732 ? constructor |
652 : null; | 733 : null; |
653 } | 734 } |
654 } | 735 } |
655 return null; | 736 return null; |
656 } | 737 } |
OLD | NEW |