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 |