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 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
207 cls.implementedTypes.clear(); | 203 cls.implementedTypes.clear(); |
208 cls.supertype = null; | 204 cls.supertype = null; |
209 cls.mixedInType = null; | 205 cls.mixedInType = null; |
210 builder.supertype = new KernelNamedTypeBuilder("Object", null, | 206 builder.supertype = new KernelNamedTypeBuilder("Object", null, |
211 builder.charOffset, builder.fileUri ?? Uri.parse(cls.fileUri)) | 207 builder.charOffset, builder.fileUri ?? Uri.parse(cls.fileUri)) |
212 ..builder = objectClassBuilder; | 208 ..builder = objectClassBuilder; |
213 builder.interfaces = null; | 209 builder.interfaces = null; |
214 builder.mixedInType = null; | 210 builder.mixedInType = null; |
215 } | 211 } |
216 | 212 |
217 void handleInputError(InputError error, | 213 void handleInputError(InputError error, {bool isFullProgram}) { |
218 {bool isFullProgram, bool trimDependencies: false}) { | |
219 if (error != null) { | 214 if (error != null) { |
220 String message = error.format(); | 215 String message = error.format(); |
221 print(message); | 216 print(message); |
222 errors.add(message); | 217 errors.add(message); |
223 } | 218 } |
224 program = erroneousProgram(isFullProgram); | 219 program = erroneousProgram(isFullProgram); |
225 } | 220 } |
226 | 221 |
227 @override | 222 @override |
228 Future<Program> buildOutlines({CanonicalName nameRoot}) async { | 223 Future<Program> buildOutlines({CanonicalName nameRoot}) async { |
(...skipping 27 matching lines...) Expand all Loading... |
256 } | 251 } |
257 return program; | 252 return program; |
258 } | 253 } |
259 | 254 |
260 /// Build the kernel representation of the program loaded by this target. The | 255 /// Build the kernel representation of the program loaded by this target. The |
261 /// program will contain full bodies for the code loaded from sources, and | 256 /// program will contain full bodies for the code loaded from sources, and |
262 /// only references to the code loaded by the [DillTarget], which may or may | 257 /// only references to the code loaded by the [DillTarget], which may or may |
263 /// not include method bodies (depending on what was loaded into that target, | 258 /// not include method bodies (depending on what was loaded into that target, |
264 /// an outline or a full kernel program). | 259 /// an outline or a full kernel program). |
265 /// | 260 /// |
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 /// | |
272 /// If [verify], run the default kernel verification on the resulting program. | 261 /// If [verify], run the default kernel verification on the resulting program. |
273 @override | 262 @override |
274 Future<Program> buildProgram( | 263 Future<Program> buildProgram({bool verify: false}) async { |
275 {bool verify: false, bool trimDependencies: false}) async { | |
276 if (loader.first == null) return null; | 264 if (loader.first == null) return null; |
277 if (errors.isNotEmpty) { | 265 if (errors.isNotEmpty) { |
278 handleInputError(null, | 266 handleInputError(null, isFullProgram: true); |
279 isFullProgram: true, trimDependencies: trimDependencies); | |
280 return program; | 267 return program; |
281 } | 268 } |
282 | 269 |
283 try { | 270 try { |
284 await loader.buildBodies(); | 271 await loader.buildBodies(); |
285 loader.finishStaticInvocations(); | 272 loader.finishStaticInvocations(); |
286 finishAllConstructors(); | 273 finishAllConstructors(); |
287 loader.finishNativeMethods(); | 274 loader.finishNativeMethods(); |
288 runBuildTransformations(); | 275 runBuildTransformations(); |
289 | 276 |
290 if (verify) this.verify(); | 277 if (verify) this.verify(); |
291 if (errors.isNotEmpty) { | 278 if (errors.isNotEmpty) { |
292 handleInputError(null, | 279 handleInputError(null, isFullProgram: true); |
293 isFullProgram: true, trimDependencies: trimDependencies); | |
294 } | 280 } |
295 handleRecoverableErrors(loader.unhandledErrors); | 281 handleRecoverableErrors(loader.unhandledErrors); |
296 } on InputError catch (e) { | 282 } on InputError catch (e) { |
297 handleInputError(e, | 283 handleInputError(e, isFullProgram: true); |
298 isFullProgram: true, trimDependencies: trimDependencies); | |
299 } catch (e, s) { | 284 } catch (e, s) { |
300 return reportCrash(e, s, loader?.currentUriForCrashReporting); | 285 return reportCrash(e, s, loader?.currentUriForCrashReporting); |
301 } | 286 } |
302 if (trimDependencies) trimDependenciesInProgram(); | |
303 return program; | 287 return program; |
304 } | 288 } |
305 | 289 |
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 | |
350 /// Adds a synthetic field named `#errors` to the main library that contains | 290 /// Adds a synthetic field named `#errors` to the main library that contains |
351 /// [recoverableErrors] formatted. | 291 /// [recoverableErrors] formatted. |
352 /// | 292 /// |
353 /// If [recoverableErrors] is empty, this method does nothing. | 293 /// If [recoverableErrors] is empty, this method does nothing. |
354 /// | 294 /// |
355 /// If there's no main library, this method uses [erroneousProgram] to | 295 /// If there's no main library, this method uses [erroneousProgram] to |
356 /// replace [program]. | 296 /// replace [program]. |
357 void handleRecoverableErrors(List<InputError> recoverableErrors) { | 297 void handleRecoverableErrors(List<InputError> recoverableErrors) { |
358 if (recoverableErrors.isEmpty) return; | 298 if (recoverableErrors.isEmpty) return; |
359 KernelLibraryBuilder mainLibrary = loader.first; | 299 KernelLibraryBuilder mainLibrary = loader.first; |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
683 backendTarget.performGlobalTransformations(loader.coreTypes, program, | 623 backendTarget.performGlobalTransformations(loader.coreTypes, program, |
684 logger: (String msg) => ticker.logMs(msg)); | 624 logger: (String msg) => ticker.logMs(msg)); |
685 } | 625 } |
686 | 626 |
687 void verify() { | 627 void verify() { |
688 var verifyErrors = verifyProgram(program); | 628 var verifyErrors = verifyProgram(program); |
689 errors.addAll(verifyErrors.map((error) => '$error')); | 629 errors.addAll(verifyErrors.map((error) => '$error')); |
690 ticker.logMs("Verified program"); | 630 ticker.logMs("Verified program"); |
691 } | 631 } |
692 | 632 |
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 | |
714 /// Return `true` if the given [library] was built by this [KernelTarget] | 633 /// Return `true` if the given [library] was built by this [KernelTarget] |
715 /// from sources, and not loaded from a [DillTarget]. | 634 /// from sources, and not loaded from a [DillTarget]. |
716 bool isSourceLibrary(Library library) { | 635 bool isSourceLibrary(Library library) { |
717 return loader.libraries.contains(library); | 636 return loader.libraries.contains(library); |
718 } | 637 } |
719 } | 638 } |
720 | 639 |
721 /// Looks for a constructor call that matches `super()` from a constructor in | 640 /// Looks for a constructor call that matches `super()` from a constructor in |
722 /// [cls]. Such a constructor may have optional arguments, but no required | 641 /// [cls]. Such a constructor may have optional arguments, but no required |
723 /// arguments. | 642 /// arguments. |
724 Constructor defaultSuperConstructor(Class cls) { | 643 Constructor defaultSuperConstructor(Class cls) { |
725 Class superclass = cls.superclass; | 644 Class superclass = cls.superclass; |
726 while (superclass != null && superclass.isMixinApplication) { | 645 while (superclass != null && superclass.isMixinApplication) { |
727 superclass = superclass.superclass; | 646 superclass = superclass.superclass; |
728 } | 647 } |
729 for (Constructor constructor in superclass.constructors) { | 648 for (Constructor constructor in superclass.constructors) { |
730 if (constructor.name.name.isEmpty) { | 649 if (constructor.name.name.isEmpty) { |
731 return constructor.function.requiredParameterCount == 0 | 650 return constructor.function.requiredParameterCount == 0 |
732 ? constructor | 651 ? constructor |
733 : null; | 652 : null; |
734 } | 653 } |
735 } | 654 } |
736 return null; | 655 return null; |
737 } | 656 } |
OLD | NEW |