| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 analyzer2dart.treeShaker; | 5 library analyzer2dart.treeShaker; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/src/generated/element.dart'; | 10 import 'package:analyzer/src/generated/element.dart'; |
| 11 import 'package:analyzer/src/generated/source.dart'; |
| 11 import 'package:compiler/implementation/universe/universe.dart'; | 12 import 'package:compiler/implementation/universe/universe.dart'; |
| 12 | 13 |
| 13 import 'closed_world.dart'; | 14 import 'closed_world.dart'; |
| 15 import 'util.dart'; |
| 16 import 'semantic_visitor.dart'; |
| 14 import 'package:analyzer2dart/src/identifier_semantics.dart'; | 17 import 'package:analyzer2dart/src/identifier_semantics.dart'; |
| 15 | 18 |
| 16 /** | 19 /** |
| 17 * The result of performing local reachability analysis on a method. | 20 * The result of performing local reachability analysis on a method. |
| 18 */ | 21 */ |
| 19 class MethodAnalysis { | 22 class MethodAnalysis { |
| 20 /** | 23 /** |
| 21 * The AST for the method. | 24 * The AST for the method. |
| 22 */ | 25 */ |
| 23 final Declaration declaration; | 26 final Declaration declaration; |
| 24 | 27 |
| 25 /** | 28 /** |
| 26 * The functions statically called by the method. | 29 * The functions statically called by the method. |
| 27 */ | 30 */ |
| 28 final List<ExecutableElement> calls = <ExecutableElement>[]; | 31 final List<ExecutableElement> calls = <ExecutableElement>[]; |
| 29 | 32 |
| 30 /** | 33 /** |
| 34 * The fields and top-level variables statically accessed by the method. |
| 35 */ |
| 36 // TODO(johnniwinther): Should we split this into reads and writes? |
| 37 final List<PropertyInducingElement> accesses = <PropertyInducingElement>[]; |
| 38 |
| 39 /** |
| 31 * The selectors used by the method to perform dynamic invocation. | 40 * The selectors used by the method to perform dynamic invocation. |
| 32 */ | 41 */ |
| 33 final List<Selector> invokes = <Selector>[]; | 42 final List<Selector> invokes = <Selector>[]; |
| 34 | 43 |
| 35 /** | 44 /** |
| 36 * The classes that are instantiated by the method. | 45 * The classes that are instantiated by the method. |
| 37 */ | 46 */ |
| 38 final List<ClassElement> instantiates = <ClassElement>[]; | 47 final List<ClassElement> instantiates = <ClassElement>[]; |
| 39 | 48 |
| 40 MethodAnalysis(this.declaration); | 49 MethodAnalysis(this.declaration); |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 _addElement(_world.mainFunction); | 176 _addElement(_world.mainFunction); |
| 168 while (_queue.isNotEmpty) { | 177 while (_queue.isNotEmpty) { |
| 169 Element element = _queue.removeLast(); | 178 Element element = _queue.removeLast(); |
| 170 print('Tree shaker handling $element'); | 179 print('Tree shaker handling $element'); |
| 171 if (element is ExecutableElement) { | 180 if (element is ExecutableElement) { |
| 172 MethodAnalysis analysis = _localComputer.analyzeMethod(element); | 181 MethodAnalysis analysis = _localComputer.analyzeMethod(element); |
| 173 _world.executableElements[element] = analysis.declaration; | 182 _world.executableElements[element] = analysis.declaration; |
| 174 analysis.calls.forEach(_addElement); | 183 analysis.calls.forEach(_addElement); |
| 175 analysis.invokes.forEach(_addSelector); | 184 analysis.invokes.forEach(_addSelector); |
| 176 analysis.instantiates.forEach(_addElement); | 185 analysis.instantiates.forEach(_addElement); |
| 186 analysis.accesses.forEach(_addElement); |
| 177 } else if (element is ClassElement) { | 187 } else if (element is ClassElement) { |
| 178 ClassAnalysis analysis = _localComputer.analyzeClass(element); | 188 ClassAnalysis analysis = _localComputer.analyzeClass(element); |
| 179 _world.instantiatedClasses[element] = analysis.declaration; | 189 _world.instantiatedClasses[element] = analysis.declaration; |
| 180 for (Selector selector in _selectors) { | 190 for (Selector selector in _selectors) { |
| 181 _matchClassToSelector(element, selector); | 191 _matchClassToSelector(element, selector); |
| 182 } | 192 } |
| 183 } else if (element is FieldElement) { | 193 } else if (element is FieldElement) { |
| 184 VariableDeclaration declaration = element.node; | 194 VariableDeclaration declaration = element.node; |
| 185 _world.fields[element] = declaration; | 195 _world.fields[element] = declaration; |
| 196 } else if (element is TopLevelVariableElement) { |
| 197 VariableDeclaration declaration = element.node; |
| 198 _world.variables[element] = declaration; |
| 186 } else { | 199 } else { |
| 187 throw new Exception('Unexpected element type while tree shaking'); | 200 throw new Exception( |
| 201 'Unexpected element type while tree shaking: ' |
| 202 '$element (${element.runtimeType})'); |
| 188 } | 203 } |
| 189 } | 204 } |
| 190 print('Tree shaking done'); | 205 print('Tree shaking done'); |
| 191 return _world; | 206 return _world; |
| 192 } | 207 } |
| 193 } | 208 } |
| 194 | 209 |
| 195 Selector createSelectorFromMethodInvocation(MethodInvocation node) { | 210 class TreeShakingVisitor extends SemanticVisitor { |
| 196 int arity = 0; | |
| 197 List<String> namedArguments = <String>[]; | |
| 198 for (var x in node.argumentList.arguments) { | |
| 199 if (x is NamedExpression) { | |
| 200 namedArguments.add(x.name.label.name); | |
| 201 } else { | |
| 202 arity++; | |
| 203 } | |
| 204 } | |
| 205 return new Selector.call(node.methodName.name, null, arity, namedArguments); | |
| 206 } | |
| 207 | |
| 208 class TreeShakingVisitor extends RecursiveAstVisitor { | |
| 209 final MethodAnalysis analysis; | 211 final MethodAnalysis analysis; |
| 210 | 212 |
| 211 TreeShakingVisitor(this.analysis); | 213 TreeShakingVisitor(this.analysis); |
| 212 | 214 |
| 215 Source get currentSource => analysis.declaration.element.source; |
| 216 |
| 213 @override | 217 @override |
| 214 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 218 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 215 ConstructorElement staticElement = node.staticElement; | 219 ConstructorElement staticElement = node.staticElement; |
| 216 if (staticElement != null) { | 220 if (staticElement != null) { |
| 217 // TODO(paulberry): Really we should enqueue the constructor, and then | 221 // TODO(paulberry): Really we should enqueue the constructor, and then |
| 218 // when we visit it add the class to the class bucket. | 222 // when we visit it add the class to the class bucket. |
| 219 ClassElement classElement = staticElement.enclosingElement; | 223 ClassElement classElement = staticElement.enclosingElement; |
| 220 analysis.instantiates.add(classElement); | 224 analysis.instantiates.add(classElement); |
| 221 } else { | 225 } else { |
| 222 // TODO(paulberry): deal with this situation. This can happen, for | 226 // TODO(paulberry): deal with this situation. This can happen, for |
| 223 // example, in the case "main() => new Unresolved();" (which is a | 227 // example, in the case "main() => new Unresolved();" (which is a |
| 224 // warning, not an error). | 228 // warning, not an error). |
| 225 } | 229 } |
| 226 super.visitInstanceCreationExpression(node); | 230 super.visitInstanceCreationExpression(node); |
| 227 } | 231 } |
| 228 | 232 |
| 229 @override | 233 @override |
| 230 void visitMethodInvocation(MethodInvocation node) { | 234 void visitDynamicInvocation(MethodInvocation node, |
| 231 if (node.target != null) { | 235 AccessSemantics semantics) { |
| 232 node.target.accept(this); | 236 analysis.invokes.add( |
| 237 createSelectorFromMethodInvocation(node, node.methodName.name)); |
| 238 } |
| 239 |
| 240 @override |
| 241 void visitLocalFunctionInvocation(MethodInvocation node, |
| 242 AccessSemantics semantics) { |
| 243 // Locals don't need to be tree shaken. |
| 244 } |
| 245 |
| 246 @override |
| 247 void visitLocalVariableInvocation(MethodInvocation node, |
| 248 AccessSemantics semantics) { |
| 249 // Locals don't need to be tree shaken. |
| 250 } |
| 251 |
| 252 @override |
| 253 void visitParameterInvocation(MethodInvocation node, |
| 254 AccessSemantics semantics) { |
| 255 // Locals don't need to be tree shaken. |
| 256 } |
| 257 |
| 258 @override |
| 259 void visitStaticFieldInvocation(MethodInvocation node, |
| 260 AccessSemantics semantics) { |
| 261 // Invocation of a static field. |
| 262 analysis.accesses.add(semantics.element); |
| 263 analysis.invokes.add( |
| 264 createSelectorFromMethodInvocation(node, 'call')); |
| 265 } |
| 266 |
| 267 void visitStaticMethodInvocation(MethodInvocation node, |
| 268 AccessSemantics semantics) { |
| 269 analysis.calls.add(semantics.element); |
| 270 } |
| 271 |
| 272 void visitStaticPropertyInvocation(MethodInvocation node, |
| 273 AccessSemantics semantics) { |
| 274 // Invocation of a property. TODO(paulberry): handle this. |
| 275 super.visitStaticPropertyInvocation(node, semantics); |
| 276 } |
| 277 |
| 278 void visitDynamicAccess(AstNode node, AccessSemantics semantics) { |
| 279 if (semantics.isRead) { |
| 280 analysis.invokes.add( |
| 281 new Selector.getter(semantics.identifier.name, null)); |
| 233 } | 282 } |
| 234 node.argumentList.accept(this); | 283 if (semantics.isWrite) { |
| 235 AccessSemantics semantics = classifyMethodInvocation(node); | 284 // TODO(paulberry): implement. |
| 236 switch (semantics.kind) { | 285 return giveUp(node, '_handlePropertyAccess of ${semantics}.'); |
| 237 case AccessKind.DYNAMIC: | |
| 238 analysis.invokes.add(createSelectorFromMethodInvocation(node)); | |
| 239 break; | |
| 240 case AccessKind.LOCAL_FUNCTION: | |
| 241 case AccessKind.LOCAL_VARIABLE: | |
| 242 case AccessKind.PARAMETER: | |
| 243 // Locals don't need to be tree shaken. | |
| 244 break; | |
| 245 case AccessKind.STATIC_FIELD: | |
| 246 // Invocation of a field. TODO(paulberry): handle this. | |
| 247 throw new UnimplementedError(); | |
| 248 case AccessKind.STATIC_METHOD: | |
| 249 analysis.calls.add(semantics.element); | |
| 250 break; | |
| 251 case AccessKind.STATIC_PROPERTY: | |
| 252 // Invocation of a property. TODO(paulberry): handle this. | |
| 253 throw new UnimplementedError(); | |
| 254 default: | |
| 255 // Unexpected access kind. | |
| 256 throw new UnimplementedError(); | |
| 257 } | 286 } |
| 258 } | 287 } |
| 259 | 288 |
| 260 @override | 289 void visitLocalFunctionAccess(AstNode node, AccessSemantics semantics) { |
| 261 void visitPropertyAccess(PropertyAccess node) { | 290 // Locals don't need to be tree shaken. |
| 262 if (node.target != null) { | |
| 263 node.target.accept(this); | |
| 264 } | |
| 265 _handlePropertyAccess(classifyPropertyAccess(node)); | |
| 266 } | 291 } |
| 267 | 292 |
| 268 @override | 293 void visitLocalVariableAccess(AstNode node, AccessSemantics semantics) { |
| 269 visitPrefixedIdentifier(PrefixedIdentifier node) { | 294 // Locals don't need to be tree shaken. |
| 270 node.prefix.accept(this); | |
| 271 _handlePropertyAccess(classifyPrefixedIdentifier(node)); | |
| 272 } | 295 } |
| 273 | 296 |
| 274 @override | 297 void visitParameterAccess(AstNode node, AccessSemantics semantics) { |
| 275 visitSimpleIdentifier(SimpleIdentifier node) { | 298 // Locals don't need to be tree shaken. |
| 276 AccessSemantics semantics = classifySimpleIdentifier(node); | |
| 277 if (semantics != null) { | |
| 278 _handlePropertyAccess(semantics); | |
| 279 } | |
| 280 } | 299 } |
| 281 | 300 |
| 282 void _handlePropertyAccess(AccessSemantics semantics) { | 301 void visitStaticFieldAccess(AstNode node, AccessSemantics semantics) { |
| 283 switch (semantics.kind) { | 302 analysis.accesses.add(semantics.element); |
| 284 case AccessKind.DYNAMIC: | 303 } |
| 285 if (semantics.isRead) { | 304 |
| 286 analysis.invokes.add( | 305 void visitStaticMethodAccess(AstNode node, AccessSemantics semantics) { |
| 287 new Selector.getter(semantics.identifier.name, null)); | 306 // Method tear-off. TODO(paulberry): implement. |
| 288 } | 307 super.visitStaticMethodAccess(node, semantics); |
| 289 if (semantics.isWrite) { | 308 } |
| 290 // TODO(paulberry): implement. | 309 |
| 291 throw new UnimplementedError(); | 310 void visitStaticPropertyAccess(AstNode node, AccessSemantics semantics) { |
| 292 } | 311 // TODO(paulberry): implement. |
| 293 break; | 312 super.visitStaticPropertyAccess(node, semantics); |
| 294 case AccessKind.LOCAL_FUNCTION: | |
| 295 case AccessKind.LOCAL_VARIABLE: | |
| 296 case AccessKind.PARAMETER: | |
| 297 // Locals don't need to be tree shaken. | |
| 298 break; | |
| 299 case AccessKind.STATIC_FIELD: | |
| 300 // TODO(paulberry): implement. | |
| 301 throw new UnimplementedError(); | |
| 302 case AccessKind.STATIC_METHOD: | |
| 303 // Method tear-off. TODO(paulberry): implement. | |
| 304 break; | |
| 305 case AccessKind.STATIC_PROPERTY: | |
| 306 // TODO(paulberry): implement. | |
| 307 throw new UnimplementedError(); | |
| 308 default: | |
| 309 // Unexpected access kind. | |
| 310 throw new UnimplementedError(); | |
| 311 } | |
| 312 } | 313 } |
| 313 } | 314 } |
| OLD | NEW |