Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /// A transformation to create a self-contained modular kernel without | |
| 6 /// unnecessary references to other libraries. | |
| 7 library fasta.kernel.kernel_outline_shaker; | |
| 8 | |
| 9 import 'package:kernel/ast.dart'; | |
| 10 import 'package:kernel/core_types.dart'; | |
| 11 | |
| 12 import '../errors.dart' show internalError; | |
| 13 | |
| 14 /// Removes from [program] unnecessary libraries, classes, and members. | |
|
ahe
2017/05/22 12:04:24
Removes unnecessary libraries, classes, and member
| |
| 15 /// | |
| 16 /// This applies a simple "tree-shaking" technique: the full body of libraries | |
| 17 /// whose URI match [isIncluded] are preserved, and so is the outline of the | |
| 18 /// members and classes which are referred to transitively from these included | |
| 19 /// libraries. | |
| 20 /// | |
| 21 /// The intent is that the resulting program has the entire code that is meant | |
| 22 /// to be included and the minimum required to prevent dangling references and | |
| 23 /// allow modular program transformations. | |
| 24 /// | |
| 25 /// Note that the resulting program may include libraries not in [isIncluded], | |
| 26 /// but those will be marked as external. There should be no method bodies for | |
| 27 /// any members of those libraries. | |
| 28 /// | |
| 29 /// When [retainClassMembers] is true, all members of retained classes will be | |
| 30 /// retained as well. | |
| 31 void trimProgram(Program program, bool isIncluded(Uri uri), | |
| 32 {bool retainClassMembers: true}) { | |
| 33 var result = new _Analysis(program, isIncluded, retainClassMembers).run(); | |
| 34 new _Shaker(result, isIncluded).transform(program); | |
| 35 } | |
| 36 | |
| 37 /// Result of analyzing a program before tree-shaking it. | |
| 38 abstract class _AnalysisResult { | |
| 39 /// Whether a library should be preserved and mark as external. | |
| 40 bool isLibraryUsed(Library library); | |
| 41 | |
| 42 /// Whether a class should be preserved. If a class is preserved, its | |
| 43 /// supertypes will be preserved too, but some of it members may not be | |
| 44 /// included. | |
| 45 bool isClassUsed(Class cls); | |
| 46 | |
| 47 /// Whether a member should be preserved. If so, its enclosing class/library | |
| 48 /// will be preserved too. | |
| 49 bool isMemberUsed(Member member); | |
| 50 } | |
| 51 | |
| 52 /// Visitor used by [trimProgram] to collects which libraries, classes, and | |
| 53 /// members should be preserved. | |
| 54 class _Analysis extends RecursiveVisitor implements _AnalysisResult { | |
| 55 /// The program being analyzed. | |
| 56 final Program program; | |
| 57 | |
| 58 /// Helper to fetch classes and members of the core libraries. | |
| 59 final CoreTypes coreTypes; | |
| 60 | |
| 61 /// Filter to determine libraries that should be analyzed. | |
| 62 final Filter isIncluded; | |
| 63 | |
| 64 /// When retaining a class, whether to retain all of its members. | |
| 65 // TODO(sigmund): delete this option once we figure out the long term plan. Do | |
| 66 // we need to do this? Today it seems necessary because some transformations | |
| 67 // require it, but maybe we don't need them all (e.g. only constructors and | |
| 68 // members whose inferenceTarget is referred do in the sources?) Do we need a | |
| 69 // different retention policy for including the type hierarchy with and | |
| 70 // without members? | |
| 71 final bool retainClassMembers; | |
| 72 | |
| 73 /// Libraries that contained code that is transitively reachable from the | |
| 74 /// included libraries. | |
| 75 final Set<Library> _libraries = new Set<Library>(); | |
| 76 | |
| 77 /// Classes that are transitively reachable from the included libraries. | |
| 78 final Set<Class> _classes = new Set<Class>(); | |
| 79 | |
| 80 /// Members that are transitively reachable from the included libraries. | |
| 81 final Set<Member> _members = new Set<Member>(); | |
| 82 | |
| 83 @override | |
| 84 bool isLibraryUsed(Library library) => _libraries.contains(library); | |
| 85 | |
| 86 @override | |
| 87 bool isClassUsed(Class cls) => _classes.contains(cls); | |
| 88 | |
| 89 @override | |
| 90 bool isMemberUsed(Member m) => _members.contains(m); | |
| 91 | |
| 92 _Analysis(this.program, this.isIncluded, this.retainClassMembers) | |
| 93 : coreTypes = new CoreTypes(program); | |
| 94 | |
| 95 _AnalysisResult run() { | |
| 96 _markRequired(); | |
| 97 _markMember(program.mainMethod); | |
| 98 for (var library in program.libraries) { | |
| 99 if (isIncluded(library.importUri)) { | |
| 100 library.accept(this); | |
| 101 } | |
| 102 } | |
| 103 return this; | |
| 104 } | |
| 105 | |
| 106 /// Marks classes and members that are assumed to exist by fasta or by | |
| 107 /// transformers. | |
| 108 void _markRequired() { | |
| 109 coreTypes.objectClass.members.forEach(_markMember); | |
| 110 | |
| 111 // These are assumed to be available by fasta: | |
| 112 _markClass(coreTypes.objectClass); | |
| 113 _markClass(coreTypes.nullClass); | |
| 114 _markClass(coreTypes.boolClass); | |
| 115 _markClass(coreTypes.intClass); | |
| 116 _markClass(coreTypes.numClass); | |
| 117 _markClass(coreTypes.doubleClass); | |
| 118 _markClass(coreTypes.stringClass); | |
| 119 _markClass(coreTypes.listClass); | |
| 120 _markClass(coreTypes.mapClass); | |
| 121 _markClass(coreTypes.iterableClass); | |
| 122 _markClass(coreTypes.iteratorClass); | |
| 123 _markClass(coreTypes.futureClass); | |
| 124 _markClass(coreTypes.streamClass); | |
| 125 _markClass(coreTypes.symbolClass); | |
| 126 _markClass(coreTypes.internalSymbolClass); | |
| 127 _markClass(coreTypes.typeClass); | |
| 128 _markClass(coreTypes.functionClass); | |
| 129 _markClass(coreTypes.invocationClass); | |
| 130 _markMember(coreTypes.getMember("dart:_internal", "ExternalName", "")); | |
| 131 | |
| 132 // These are needed by the continuation (async/await) transformer: | |
| 133 _markClass(coreTypes.getClass('dart:core', 'Iterator')); | |
| 134 _markClass(coreTypes.getClass('dart:async', 'Future')); | |
| 135 _markClass(coreTypes.getClass('dart:async', 'FutureOr')); | |
| 136 _markClass(coreTypes.getClass('dart:async', 'Completer')); | |
| 137 _markMember(coreTypes.getMember('dart:async', 'Completer', 'sync')); | |
| 138 _markMember(coreTypes.getMember('dart:core', '_SyncIterable', '')); | |
| 139 _markMember(coreTypes.getMember('dart:async', '_StreamIterator', '')); | |
| 140 _markMember(coreTypes.getMember('dart:async', 'Future', 'microtask')); | |
| 141 _markMember( | |
| 142 coreTypes.getMember('dart:async', '_AsyncStarStreamController', '')); | |
| 143 _markMember(coreTypes.getTopLevelMember('dart:core', 'print')); | |
| 144 _markMember( | |
| 145 coreTypes.getTopLevelMember('dart:async', '_asyncThenWrapperHelper')); | |
| 146 _markMember( | |
| 147 coreTypes.getTopLevelMember('dart:async', '_asyncErrorWrapperHelper')); | |
| 148 _markMember(coreTypes.getTopLevelMember('dart:async', '_awaitHelper')); | |
| 149 | |
| 150 // These are needed by the mixin transformer | |
| 151 _markMember(coreTypes.getMember('dart:core', '_InvocationMirror', '')); | |
| 152 _markMember(coreTypes.getMember('dart:core', 'List', 'from')); | |
| 153 } | |
| 154 | |
| 155 /// Mark a library as used. | |
| 156 _markLibrary(Library lib) { | |
| 157 if (!_libraries.add(lib)) return; | |
| 158 } | |
| 159 | |
| 160 /// Mark a class and it's supertypes as used. | |
| 161 void _markClass(Class cls) { | |
| 162 if (cls == null || !_classes.add(cls)) return; | |
| 163 _markLibrary(cls.parent); | |
| 164 visitList(cls.annotations, this); | |
| 165 cls.supertype?.accept(this); | |
| 166 cls.mixedInType?.accept(this); | |
| 167 visitList(cls.implementedTypes, this); | |
| 168 visitList(cls.typeParameters, this); | |
| 169 | |
| 170 if (retainClassMembers) cls.members.forEach(_markMember); | |
| 171 } | |
| 172 | |
| 173 _markMember(Member m) { | |
| 174 if (m == null || !_members.add(m)) return; | |
| 175 _markMemberInterface(m); | |
| 176 var parent = m.parent; | |
| 177 if (parent is Library) { | |
| 178 _markLibrary(parent); | |
| 179 } else if (parent is Class) { | |
| 180 _markClass(parent); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 void _markMemberInterface(Member node) { | |
| 185 if (node is Field) { | |
| 186 node.type.accept(this); | |
| 187 } else if (node is Procedure) { | |
| 188 _markFunctionInterface(node.function); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 _markFunctionInterface(FunctionNode node) { | |
| 193 for (var parameter in node.typeParameters) { | |
| 194 parameter.bound.accept(this); | |
| 195 } | |
| 196 for (var parameter in node.positionalParameters) { | |
| 197 parameter.type.accept(this); | |
| 198 } | |
| 199 for (var parameter in node.namedParameters) { | |
| 200 parameter.type.accept(this); | |
| 201 } | |
| 202 node.returnType.accept(this); | |
| 203 } | |
| 204 | |
| 205 @override | |
| 206 visitFunctionNode(FunctionNode node) { | |
| 207 switch (node.asyncMarker) { | |
| 208 case AsyncMarker.Sync: | |
| 209 break; | |
| 210 case AsyncMarker.SyncStar: | |
| 211 _markClass(coreTypes.iterableClass); | |
|
scheglov
2017/05/18 15:54:08
These classes are already included in _markRequire
Siggi Cherem (dart-lang)
2017/05/19 04:37:32
good point. I removed them from here. Also - now t
| |
| 212 break; | |
| 213 case AsyncMarker.Async: | |
| 214 _markClass(coreTypes.futureClass); | |
| 215 break; | |
| 216 case AsyncMarker.AsyncStar: | |
| 217 _markClass(coreTypes.streamClass); | |
| 218 break; | |
| 219 case AsyncMarker.SyncYielding: | |
| 220 break; | |
| 221 } | |
| 222 node.visitChildren(this); | |
| 223 } | |
| 224 | |
| 225 @override | |
| 226 visitSuperInitializer(SuperInitializer node) { | |
| 227 _markMember(node.target); | |
| 228 node.visitChildren(this); | |
| 229 } | |
| 230 | |
| 231 @override | |
| 232 visitRedirectingInitializer(RedirectingInitializer node) { | |
| 233 _markMember(node.target); | |
| 234 node.visitChildren(this); | |
| 235 } | |
| 236 | |
| 237 @override | |
| 238 visitConstructorInvocation(ConstructorInvocation node) { | |
| 239 _markMember(node.target); | |
| 240 node.visitChildren(this); | |
| 241 } | |
| 242 | |
| 243 @override | |
| 244 visitStaticInvocation(StaticInvocation node) { | |
| 245 _markMember(node.target); | |
| 246 node.visitChildren(this); | |
| 247 } | |
| 248 | |
| 249 @override | |
| 250 visitDirectMethodInvocation(DirectMethodInvocation node) { | |
| 251 if (node.receiver is! ThisExpression) { | |
| 252 return internalError('Direct calls are only supported on "this"'); | |
| 253 } | |
| 254 _markMember(node.target); | |
| 255 node.visitChildren(this); | |
| 256 } | |
| 257 | |
| 258 @override | |
| 259 visitMethodInvocation(MethodInvocation node) { | |
| 260 _markMember(node.interfaceTarget); | |
| 261 node.visitChildren(this); | |
| 262 } | |
| 263 | |
| 264 @override | |
| 265 visitStaticGet(StaticGet node) { | |
| 266 _markMember(node.target); | |
| 267 node.visitChildren(this); | |
| 268 } | |
| 269 | |
| 270 @override | |
| 271 visitStaticSet(StaticSet node) { | |
| 272 _markMember(node.target); | |
| 273 node.visitChildren(this); | |
| 274 } | |
| 275 | |
| 276 @override | |
| 277 visitDirectPropertyGet(DirectPropertyGet node) { | |
| 278 _markMember(node.target); | |
| 279 node.visitChildren(this); | |
| 280 } | |
| 281 | |
| 282 @override | |
| 283 visitDirectPropertySet(DirectPropertySet node) { | |
| 284 _markMember(node.target); | |
| 285 node.visitChildren(this); | |
| 286 } | |
| 287 | |
| 288 @override | |
| 289 visitSuperPropertyGet(SuperPropertyGet node) { | |
| 290 _markMember(node.interfaceTarget); | |
| 291 node.visitChildren(this); | |
| 292 } | |
| 293 | |
| 294 @override | |
| 295 visitSuperPropertySet(SuperPropertySet node) { | |
| 296 _markMember(node.interfaceTarget); | |
| 297 node.visitChildren(this); | |
| 298 } | |
| 299 | |
| 300 @override | |
| 301 visitPropertyGet(PropertyGet node) { | |
| 302 _markMember(node.interfaceTarget); | |
| 303 node.visitChildren(this); | |
| 304 } | |
| 305 | |
| 306 @override | |
| 307 visitPropertySet(PropertySet node) { | |
| 308 _markMember(node.interfaceTarget); | |
| 309 node.visitChildren(this); | |
| 310 } | |
| 311 | |
| 312 @override | |
| 313 visitListLiteral(ListLiteral node) { | |
| 314 _markClass(coreTypes.listClass); | |
| 315 node.visitChildren(this); | |
| 316 } | |
| 317 | |
| 318 @override | |
| 319 visitMapLiteral(MapLiteral node) { | |
| 320 _markClass(coreTypes.mapClass); | |
| 321 node.visitChildren(this); | |
| 322 } | |
| 323 | |
| 324 @override | |
| 325 visitStringConcatenation(StringConcatenation node) { | |
| 326 node.visitChildren(this); | |
| 327 } | |
| 328 | |
| 329 @override | |
| 330 visitInterfaceType(InterfaceType node) { | |
| 331 _markClass(node.classNode); | |
| 332 node.visitChildren(this); | |
| 333 } | |
| 334 | |
| 335 @override | |
| 336 visitSupertype(Supertype node) { | |
| 337 _markClass(node.classNode); | |
| 338 node.visitChildren(this); | |
| 339 } | |
| 340 | |
| 341 @override | |
| 342 visitDoubleLiteral(DoubleLiteral node) { | |
| 343 _markClass(coreTypes.doubleClass); | |
| 344 } | |
| 345 | |
| 346 @override | |
| 347 visitSymbolLiteral(SymbolLiteral node) { | |
| 348 _markClass(coreTypes.symbolClass); | |
| 349 } | |
| 350 | |
| 351 @override | |
| 352 visitTypeLiteral(TypeLiteral node) { | |
| 353 _markClass(coreTypes.typeClass); | |
| 354 node.visitChildren(this); | |
| 355 } | |
| 356 | |
| 357 @override | |
| 358 visitTypedefReference(Typedef node) { | |
| 359 return internalError('not implemented'); | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 /// Transformer that trims everything in the excluded libraries that is not | |
| 364 /// marked as preserved by the [_Analysis] above. For every member in these | |
| 365 /// excluded libraries, this transformer also removes function bodies and | |
| 366 /// initializers. | |
| 367 class _Shaker extends Transformer { | |
| 368 final _AnalysisResult result; | |
| 369 final Filter isIncluded; | |
| 370 | |
| 371 _Shaker(this.result, this.isIncluded); | |
| 372 | |
| 373 void transform(Program program) { | |
| 374 var toRemove = new Set<Library>(); | |
| 375 for (var library in program.libraries) { | |
| 376 if (!isIncluded(library.importUri)) { | |
| 377 if (!result.isLibraryUsed(library)) { | |
| 378 toRemove.add(library); | |
| 379 } else { | |
| 380 library.isExternal = true; | |
| 381 library.transformChildren(this); | |
| 382 } | |
| 383 } | |
| 384 } | |
| 385 program.libraries.removeWhere(toRemove.contains); | |
| 386 } | |
| 387 | |
| 388 Class visitClass(Class node) { | |
| 389 if (!result.isClassUsed(node)) { | |
| 390 node.canonicalName?.unbind(); | |
| 391 return null; // Remove the class. | |
| 392 } else { | |
| 393 node.transformChildren(this); | |
| 394 return node; | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 Member defaultMember(Member node) { | |
| 399 if (!result.isMemberUsed(node)) { | |
| 400 node.canonicalName?.unbind(); | |
| 401 return null; | |
| 402 } else { | |
| 403 if (node is Procedure) { | |
| 404 _clearFunction(node.function); | |
| 405 } else if (node is Field) { | |
| 406 node.initializer = null; | |
| 407 } else if (node is Constructor) { | |
| 408 node.initializers.clear(); | |
| 409 _clearFunction(node.function); | |
| 410 } | |
| 411 return node; | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 _clearFunction(FunctionNode function) { | |
| 416 function.body = null; | |
| 417 // TODO(sigmund): Fix directly the continuation transformer. The async/await | |
| 418 // continuation transformer fails if it finds a function with no body that | |
| 419 // it is marked with async/async*/sync*, we shouldn't be patching that here. | |
| 420 function.dartAsyncMarker = function.asyncMarker; | |
| 421 function.asyncMarker = AsyncMarker.Sync; | |
| 422 } | |
| 423 | |
| 424 /// Types appear to be encoded directly, so we have no need to preserve | |
| 425 /// typedefs. | |
| 426 // TODO(sigmund): revisit if this is not the case, the `inputError` in the | |
| 427 // analysis step is meant to detect this. | |
| 428 Typedef visitTypedef(Typedef node) => null; | |
| 429 | |
| 430 TreeNode defaultTreeNode(TreeNode node) => node; | |
| 431 } | |
| 432 | |
| 433 typedef bool Filter(Uri uri); | |
| OLD | NEW |