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:compiler/implementation/universe/universe.dart'; | 11 import 'package:compiler/implementation/universe/universe.dart'; |
12 | 12 |
13 import 'closed_world.dart'; | 13 import 'closed_world.dart'; |
14 import 'package:analyzer2dart/src/identifier_semantics.dart'; | |
Johnni Winther
2014/09/25 12:09:36
What is the motivation for using a package and not
Paul Berry
2014/09/25 18:22:48
Hmm, I hadn't actually given it much thought. Thi
| |
14 | 15 |
15 /** | 16 /** |
16 * The result of performing local reachability analysis on a method. | 17 * The result of performing local reachability analysis on a method. |
17 */ | 18 */ |
18 class MethodAnalysis { | 19 class MethodAnalysis { |
19 /** | 20 /** |
20 * The AST for the method. | 21 * The AST for the method. |
21 */ | 22 */ |
22 final Declaration declaration; | 23 final Declaration declaration; |
23 | 24 |
24 /** | 25 /** |
25 * The functions statically called by the method. | 26 * The functions statically called by the method. |
26 */ | 27 */ |
27 final List<LocalElement> calls = <LocalElement>[]; | 28 final List<ExecutableElement> calls = <ExecutableElement>[]; |
28 | 29 |
29 /** | 30 /** |
30 * The selectors used by the method to perform dynamic invocation. | 31 * The selectors used by the method to perform dynamic invocation. |
31 */ | 32 */ |
32 final List<Selector> invokes = <Selector>[]; | 33 final List<Selector> invokes = <Selector>[]; |
33 | 34 |
34 /** | 35 /** |
35 * The classes that are instantiated by the method. | 36 * The classes that are instantiated by the method. |
36 */ | 37 */ |
37 final List<ClassElement> instantiates = <ClassElement>[]; | 38 final List<ClassElement> instantiates = <ClassElement>[]; |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
202 } | 203 } |
203 } | 204 } |
204 return new Selector.call(node.methodName.name, null, arity, namedArguments); | 205 return new Selector.call(node.methodName.name, null, arity, namedArguments); |
205 } | 206 } |
206 | 207 |
207 class TreeShakingVisitor extends RecursiveAstVisitor { | 208 class TreeShakingVisitor extends RecursiveAstVisitor { |
208 final MethodAnalysis analysis; | 209 final MethodAnalysis analysis; |
209 | 210 |
210 TreeShakingVisitor(this.analysis); | 211 TreeShakingVisitor(this.analysis); |
211 | 212 |
212 /** | |
213 * Handle a true method call (a MethodInvocation that represents a call to | |
214 * a non-static method). | |
215 */ | |
216 void handleMethodCall(MethodInvocation node) { | |
217 analysis.invokes.add(createSelectorFromMethodInvocation(node)); | |
218 } | |
219 | |
220 @override | |
221 void visitFunctionDeclaration(FunctionDeclaration node) { | |
222 super.visitFunctionDeclaration(node); | |
223 } | |
224 | |
225 @override | 213 @override |
226 void visitInstanceCreationExpression(InstanceCreationExpression node) { | 214 void visitInstanceCreationExpression(InstanceCreationExpression node) { |
227 ConstructorElement staticElement = node.staticElement; | 215 ConstructorElement staticElement = node.staticElement; |
228 if (staticElement != null) { | 216 if (staticElement != null) { |
229 // TODO(paulberry): Really we should enqueue the constructor, and then | 217 // TODO(paulberry): Really we should enqueue the constructor, and then |
230 // when we visit it add the class to the class bucket. | 218 // when we visit it add the class to the class bucket. |
231 ClassElement classElement = staticElement.enclosingElement; | 219 ClassElement classElement = staticElement.enclosingElement; |
232 analysis.instantiates.add(classElement); | 220 analysis.instantiates.add(classElement); |
233 } else { | 221 } else { |
234 // TODO(paulberry): deal with this situation. This can happen, for | 222 // TODO(paulberry): deal with this situation. This can happen, for |
235 // example, in the case "main() => new Unresolved();" (which is a | 223 // example, in the case "main() => new Unresolved();" (which is a |
236 // warning, not an error). | 224 // warning, not an error). |
237 } | 225 } |
238 super.visitInstanceCreationExpression(node); | 226 super.visitInstanceCreationExpression(node); |
239 } | 227 } |
240 | 228 |
241 @override | 229 @override |
242 void visitMethodInvocation(MethodInvocation node) { | 230 void visitMethodInvocation(MethodInvocation node) { |
243 super.visitMethodInvocation(node); | 231 if (node.target != null) { |
244 Element staticElement = node.methodName.staticElement; | 232 node.target.accept(this); |
245 if (staticElement == null) { | 233 } |
246 if (node.realTarget != null) { | 234 node.argumentList.accept(this); |
247 // Calling a method that has no known element, e.g.: | 235 AccessSemantics semantics = classifyMethodInvocation(node); |
248 // dynamic x; | 236 switch (semantics.kind) { |
249 // x.foo(); | 237 case AccessKind.DYNAMIC: |
250 handleMethodCall(node); | 238 analysis.invokes.add(createSelectorFromMethodInvocation(node)); |
251 } else { | 239 break; |
252 // Calling a toplevel function which has no known element, e.g. | 240 case AccessKind.LOCAL_FUNCTION: |
253 // main() { | 241 case AccessKind.LOCAL_VARIABLE: |
254 // foo(); | 242 case AccessKind.PARAMETER: |
255 // } | 243 // Locals don't need to be tree shaken. |
256 // TODO(paulberry): deal with this case. May need to notify the back | 244 break; |
257 // end in case this makes it want to drag in some helper code. | 245 case AccessKind.STATIC_FIELD: |
246 // Invocation of a field. TODO(paulberry): handle this. | |
258 throw new UnimplementedError(); | 247 throw new UnimplementedError(); |
259 } | 248 case AccessKind.STATIC_METHOD: |
260 } else if (staticElement is MethodElement) { | 249 analysis.calls.add(semantics.element); |
261 // Invoking a method, e.g.: | 250 break; |
262 // class A { | 251 case AccessKind.STATIC_PROPERTY: |
263 // f() {} | 252 // Invocation of a property. TODO(paulberry): handle this. |
264 // } | 253 throw new UnimplementedError(); |
265 // main() { | 254 default: |
266 // new A().f(); | 255 // Unexpected access kind. |
267 // } | 256 throw new UnimplementedError(); |
268 // or via implicit this, i.e.: | |
269 // class A { | |
270 // f() {} | |
271 // foo() { | |
272 // f(); | |
273 // } | |
274 // } | |
275 // TODO(paulberry): if user-provided types are wrong, this may actually | |
276 // be the PropertyAccessorElement case. | |
277 // TODO(paulberry): do we need to do something different for static | |
278 // methods? | |
279 handleMethodCall(node); | |
280 } else if (staticElement is PropertyAccessorElement) { | |
281 // Invoking a callable getter, e.g.: | |
282 // typedef FunctionType(); | |
283 // class A { | |
284 // FunctionType get f { ... } | |
285 // } | |
286 // main() { | |
287 // new A().f(); | |
288 // } | |
289 // or via implicit this, i.e.: | |
290 // typedef FunctionType(); | |
291 // class A { | |
292 // FunctionType get f { ... } | |
293 // foo() { | |
294 // f(); | |
295 // } | |
296 // } | |
297 // This also covers the case where the getter is synthetic, because we | |
298 // are getting a field (TODO(paulberry): verify that this is the case). | |
299 // TODO(paulberry): deal with this case. | |
300 // TODO(paulberry): if user-provided types are wrong, this may actually | |
301 // be the MethodElement case. | |
302 throw new UnimplementedError(); | |
303 } else if (staticElement is MultiplyInheritedExecutableElement) { | |
304 // TODO(paulberry): deal with this case. | |
305 throw new UnimplementedError(); | |
306 } else if (staticElement is LocalElement) { | |
307 // Invoking a callable local, e.g.: | |
308 // typedef FunctionType(); | |
309 // main() { | |
310 // FunctionType f = ...; | |
311 // f(); | |
312 // } | |
313 // or: | |
314 // main() { | |
315 // f() { ... } | |
316 // f(); | |
317 // } | |
318 // or: | |
319 // f() {} | |
320 // main() { | |
321 // f(); | |
322 // } | |
323 // TODO(paulberry): for the moment we are assuming it's a toplevel | |
324 // function. | |
325 analysis.calls.add(staticElement); | |
326 } else if (staticElement is MultiplyDefinedElement) { | |
327 // TODO(paulberry): do we have to deal with this case? | |
328 throw new UnimplementedError(); | |
329 } | 257 } |
330 // TODO(paulberry): I believe all the other possibilities are errors, but | |
331 // we should double check. | |
332 } | 258 } |
333 | 259 |
334 @override | 260 @override |
335 void visitPropertyAccess(PropertyAccess node) { | 261 void visitPropertyAccess(PropertyAccess node) { |
336 // Accessing a getter or setter, e.g.: | 262 if (node.target != null) { |
337 // class A { | 263 node.target.accept(this); |
338 // get g() => ...; | 264 } |
339 // } | 265 _handlePropertyAccess(classifyPropertyAccess(node)); |
340 // main() { | 266 } |
341 // new A().g; | 267 |
342 // } | 268 @override |
343 // TODO(paulberry): do setters go through this path as well? | 269 visitPrefixedIdentifier(PrefixedIdentifier node) { |
344 // TODO(paulberry): handle cases where the property access is represented | 270 node.prefix.accept(this); |
345 // as a PrefixedIdentifier. | 271 _handlePropertyAccess(classifyPrefixedIdentifier(node)); |
346 super.visitPropertyAccess(node); | 272 } |
347 analysis.invokes.add(new Selector.getter(node.propertyName.name, null)); | 273 |
274 @override | |
275 visitSimpleIdentifier(SimpleIdentifier node) { | |
276 AccessSemantics semantics = classifyBareIdentifier(node); | |
277 if (semantics != null) { | |
278 _handlePropertyAccess(semantics); | |
279 } | |
280 } | |
281 | |
282 void _handlePropertyAccess(AccessSemantics semantics) { | |
283 switch (semantics.kind) { | |
284 case AccessKind.DYNAMIC: | |
285 if (semantics.isRead) { | |
286 analysis.invokes.add( | |
287 new Selector.getter(semantics.identifier.name, null)); | |
288 } | |
289 if (semantics.isWrite) { | |
290 // TODO(paulberry): implement. | |
291 throw new UnimplementedError(); | |
292 } | |
293 break; | |
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 } | |
348 } | 312 } |
349 } | 313 } |
OLD | NEW |