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