| Index: pkg/analyzer2dart/lib/src/tree_shaker.dart
|
| diff --git a/pkg/analyzer2dart/lib/src/tree_shaker.dart b/pkg/analyzer2dart/lib/src/tree_shaker.dart
|
| deleted file mode 100644
|
| index 6b9eaa67a95bd3ca3f7220eed3c28eff85fbe011..0000000000000000000000000000000000000000
|
| --- a/pkg/analyzer2dart/lib/src/tree_shaker.dart
|
| +++ /dev/null
|
| @@ -1,418 +0,0 @@
|
| -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library analyzer2dart.treeShaker;
|
| -
|
| -import 'dart:collection';
|
| -
|
| -import 'package:analyzer/analyzer.dart';
|
| -import 'package:analyzer/src/generated/element.dart';
|
| -import 'package:analyzer/src/generated/source.dart';
|
| -import 'package:analyzer/src/generated/resolver.dart';
|
| -import 'package:compiler/src/universe/universe.dart';
|
| -
|
| -import 'closed_world.dart';
|
| -import 'util.dart';
|
| -import 'semantic_visitor.dart';
|
| -import 'identifier_semantics.dart';
|
| -
|
| -/**
|
| - * The result of performing local reachability analysis on a method.
|
| - */
|
| -class MethodAnalysis {
|
| - /**
|
| - * The AST for the method.
|
| - */
|
| - final Declaration declaration;
|
| -
|
| - /**
|
| - * The functions statically called by the method.
|
| - */
|
| - final List<ExecutableElement> calls = <ExecutableElement>[];
|
| -
|
| - /**
|
| - * The fields and top-level variables statically accessed by the method.
|
| - */
|
| - // TODO(johnniwinther): Should we split this into reads and writes?
|
| - final List<PropertyInducingElement> accesses = <PropertyInducingElement>[];
|
| -
|
| - /**
|
| - * The selectors used by the method to perform dynamic invocation.
|
| - */
|
| - final List<Selector> invokes = <Selector>[];
|
| -
|
| - /**
|
| - * The classes that are instantiated by the method.
|
| - */
|
| - // TODO(johnniwinther,paulberry): Register instantiated types.
|
| - // TODO(johnniwinther,paulberry): Register checked types from is/as checks,
|
| - // catch clauses and (checked) type annotations.
|
| - final List<ClassElement> instantiates = <ClassElement>[];
|
| -
|
| - MethodAnalysis(this.declaration);
|
| -}
|
| -
|
| -/**
|
| - * The result of performing local reachability analysis on a class.
|
| - *
|
| - * TODO(paulberry): Do we need to do any other analysis of classes? (For
|
| - * example, detect annotations that are relevant to mirrors, detect that a
|
| - * class might be used for custom HTML elements, or collect inherited and
|
| - * mixed-in classes).
|
| - */
|
| -class ClassAnalysis {
|
| - /**
|
| - * The AST for the class.
|
| - */
|
| - final ClassDeclaration declaration;
|
| -
|
| - ClassAnalysis(this.declaration);
|
| -}
|
| -
|
| -/**
|
| - * This class is responsible for performing local analysis of the source code
|
| - * to provide the information needed to do tree shaking.
|
| - */
|
| -class LocalReachabilityComputer {
|
| - /**
|
| - * Perform local reachability analysis of [method].
|
| - */
|
| - MethodAnalysis analyzeMethod(ExecutableElement method) {
|
| - Declaration declaration = method.node;
|
| - MethodAnalysis analysis = new MethodAnalysis(declaration);
|
| - if (declaration != null) {
|
| - declaration.accept(new TreeShakingVisitor(analysis));
|
| - } else if (method is ConstructorElement) {
|
| - // This constructor has no associated declaration in the AST. Either it
|
| - // is a default constructor for an ordinary class, or it's a synthetic
|
| - // constructor associated with a mixin. For now we assume it's a default
|
| - // constructor, in which case all we need to do is record the class as
|
| - // being instantiated by this method. TODO(paulberry): handle the
|
| - // mixin case.
|
| - ClassElement instantiatedClass = method.enclosingElement;
|
| - analysis.instantiates.add(instantiatedClass);
|
| - if (instantiatedClass.supertype != null) {
|
| - ClassElement superClass = instantiatedClass.supertype.element;
|
| - ConstructorElement superConstructor = superClass.unnamedConstructor;
|
| - if (superConstructor != null) {
|
| - // TODO(johnniwinther): Register instantiated type and selector.
|
| - analysis.calls.add(superConstructor);
|
| - }
|
| - }
|
| - } else {
|
| - // This is an executable element with no associated declaration in the
|
| - // AST, and it's not a constructor. TODO(paulberry): can this ever
|
| - // happen?
|
| - throw new UnimplementedError();
|
| - }
|
| - return analysis;
|
| - }
|
| -
|
| - /**
|
| - * Perform local reachability analysis of [classElement].
|
| - */
|
| - ClassAnalysis analyzeClass(ClassElement classElement) {
|
| - return new ClassAnalysis(classElement.node);
|
| - }
|
| -
|
| - /**
|
| - * Determine which members of [classElement] are matched by the given
|
| - * [selector].
|
| - *
|
| - * [methods] is populated with all the class methods which are matched by the
|
| - * selector, [accessors] with all the getters and setters which are matched
|
| - * by the selector, and [fields] with all the fields which are matched by the
|
| - * selector.
|
| - */
|
| - void getMatchingClassMembers(ClassElement classElement, Selector selector,
|
| - List<MethodElement> methods, List<PropertyAccessorElement> accessors,
|
| - List<PropertyInducingElement> fields) {
|
| - // TODO(paulberry): should we walk through superclasses and mixins as well
|
| - // here? Or would it be better to make [TreeShaker] responsible for those
|
| - // relationships (since they are non-local)? Consider making use of
|
| - // InheritanceManager to do this.
|
| - for (MethodElement method in classElement.methods) {
|
| - // TODO(paulberry): account for arity and named arguments when matching
|
| - // the selector against the method.
|
| - if (selector.name == method.name) {
|
| - methods.add(method);
|
| - }
|
| - }
|
| - if (selector.kind == SelectorKind.GETTER) {
|
| - for (PropertyAccessorElement accessor in classElement.accessors) {
|
| - if (accessor.isGetter && selector.name == accessor.name) {
|
| - if (accessor.isSynthetic) {
|
| - // This accessor is implied by the corresponding field declaration.
|
| - fields.add(accessor.variable);
|
| - } else {
|
| - accessors.add(accessor);
|
| - }
|
| - }
|
| - }
|
| - } else if (selector.kind == SelectorKind.SETTER) {
|
| - // accessor.name uses the convention that setter names end in '='.
|
| - String selectorNameWithEquals = '${selector.name}=';
|
| - for (PropertyAccessorElement accessor in classElement.accessors) {
|
| - if (accessor.isSetter && selectorNameWithEquals == accessor.name) {
|
| - if (accessor.isSynthetic) {
|
| - // This accessor is implied by the corresponding field declaration.
|
| - // TODO(paulberry): should we distinguish reads and writes?
|
| - fields.add(accessor.variable);
|
| - } else {
|
| - accessors.add(accessor);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * This class is responsible for driving the tree shaking process, and
|
| - * and performing the global inferences necessary to determine which methods
|
| - * in the source program are reachable. It makes use of
|
| - * [LocalReachabilityComputer] to do local analysis of individual classes and
|
| - * methods.
|
| - */
|
| -class TreeShaker {
|
| - List<Element> _queue = <Element>[];
|
| - Set<Element> _alreadyEnqueued = new HashSet<Element>();
|
| - ClosedWorld _world;
|
| - Set<Selector> _selectors = new HashSet<Selector>();
|
| - final LocalReachabilityComputer _localComputer =
|
| - new LocalReachabilityComputer();
|
| -
|
| - TreeShaker(TypeProvider typeProvider, FunctionElement mainFunction)
|
| - : _world = new ClosedWorld(typeProvider, mainFunction);
|
| -
|
| - void _addElement(Element element) {
|
| - if (_alreadyEnqueued.add(element)) {
|
| - _queue.add(element);
|
| - }
|
| - }
|
| -
|
| - void _addSelector(Selector selector) {
|
| - if (_selectors.add(selector)) {
|
| - // New selector, so match it against all class methods.
|
| - _world.instantiatedClasses.forEach((ClassElement element, AstNode node) {
|
| - _matchClassToSelector(element, selector);
|
| - });
|
| - }
|
| - }
|
| -
|
| - void _matchClassToSelector(ClassElement classElement, Selector selector) {
|
| - List<MethodElement> methods = <MethodElement>[];
|
| - List<PropertyAccessorElement> accessors = <PropertyAccessorElement>[];
|
| - List<PropertyInducingElement> fields = <PropertyInducingElement>[];
|
| - _localComputer.getMatchingClassMembers(
|
| - classElement,
|
| - selector,
|
| - methods,
|
| - accessors,
|
| - fields);
|
| - methods.forEach(_addElement);
|
| - accessors.forEach(_addElement);
|
| - fields.forEach(_addElement);
|
| - }
|
| -
|
| - ClosedWorld shake() {
|
| - _addElement(_world.mainFunction);
|
| - while (_queue.isNotEmpty) {
|
| - Element element = _queue.removeLast();
|
| - if (element is ExecutableElement) {
|
| - MethodAnalysis analysis = _localComputer.analyzeMethod(element);
|
| - _world.executableElements[element] = analysis.declaration;
|
| - analysis.calls.forEach(_addElement);
|
| - analysis.invokes.forEach(_addSelector);
|
| - analysis.instantiates.forEach(_addElement);
|
| - analysis.accesses.forEach(_addElement);
|
| - } else if (element is ClassElement) {
|
| - ClassAnalysis analysis = _localComputer.analyzeClass(element);
|
| - _world.instantiatedClasses[element] = analysis.declaration;
|
| - for (Selector selector in _selectors) {
|
| - _matchClassToSelector(element, selector);
|
| - }
|
| - } else if (element is FieldElement) {
|
| - VariableDeclaration declaration = element.node;
|
| - _world.fields[element] = declaration;
|
| - } else if (element is TopLevelVariableElement) {
|
| - VariableDeclaration declaration = element.node;
|
| - _world.variables[element] = declaration;
|
| - } else {
|
| - throw new Exception(
|
| - 'Unexpected element type while tree shaking: '
|
| - '$element (${element.runtimeType})');
|
| - }
|
| - }
|
| - return _world;
|
| - }
|
| -}
|
| -
|
| -class TreeShakingVisitor extends SemanticVisitor {
|
| - final MethodAnalysis analysis;
|
| -
|
| - TreeShakingVisitor(this.analysis);
|
| -
|
| - Source get currentSource => analysis.declaration.element.source;
|
| -
|
| - @override
|
| - void visitInstanceCreationExpression(InstanceCreationExpression node) {
|
| - ConstructorElement staticElement = node.staticElement;
|
| - if (staticElement != null) {
|
| - analysis.calls.add(staticElement);
|
| - } else {
|
| - // TODO(paulberry): deal with this situation. This can happen, for
|
| - // example, in the case "main() => new Unresolved();" (which is a
|
| - // warning, not an error).
|
| - }
|
| - super.visitInstanceCreationExpression(node);
|
| - }
|
| -
|
| - @override
|
| - void visitDynamicInvocation(MethodInvocation node,
|
| - AccessSemantics semantics) {
|
| - analysis.invokes.add(createSelectorFromMethodInvocation(
|
| - node.argumentList, node.methodName.name));
|
| - }
|
| -
|
| - @override
|
| - void visitLocalFunctionInvocation(MethodInvocation node,
|
| - AccessSemantics semantics) {
|
| - // Locals don't need to be tree shaken.
|
| - }
|
| -
|
| - @override
|
| - void visitLocalVariableInvocation(MethodInvocation node,
|
| - AccessSemantics semantics) {
|
| - // Locals don't need to be tree shaken.
|
| - }
|
| -
|
| - @override
|
| - void visitParameterInvocation(MethodInvocation node,
|
| - AccessSemantics semantics) {
|
| - // Locals don't need to be tree shaken.
|
| - }
|
| -
|
| - @override
|
| - void visitStaticFieldInvocation(MethodInvocation node,
|
| - AccessSemantics semantics) {
|
| - // Invocation of a static field.
|
| - analysis.accesses.add(semantics.element);
|
| - analysis.invokes.add(createSelectorFromMethodInvocation(
|
| - node.argumentList, 'call'));
|
| - }
|
| -
|
| - @override
|
| - void visitStaticMethodInvocation(MethodInvocation node,
|
| - AccessSemantics semantics) {
|
| - analysis.calls.add(semantics.element);
|
| - }
|
| -
|
| - @override
|
| - void visitStaticPropertyInvocation(MethodInvocation node,
|
| - AccessSemantics semantics) {
|
| - // Invocation of a property. TODO(paulberry): handle this.
|
| - super.visitStaticPropertyInvocation(node, semantics);
|
| - }
|
| -
|
| - void handleDynamicAccess(AccessSemantics semantics) {
|
| - if (semantics.isRead) {
|
| - analysis.invokes.add(
|
| - new Selector.getter(semantics.identifier.name, null));
|
| - }
|
| - if (semantics.isWrite) {
|
| - // Selector.setter constructor uses the convention that setter names
|
| - // don't end in '='.
|
| - analysis.invokes.add(
|
| - new Selector.setter(semantics.identifier.name, null));
|
| - }
|
| - }
|
| -
|
| - @override
|
| - void visitDynamicAccess(AstNode node, AccessSemantics semantics) {
|
| - handleDynamicAccess(semantics);
|
| - }
|
| -
|
| - @override
|
| - void visitLocalFunctionAccess(AstNode node, AccessSemantics semantics) {
|
| - // Locals don't need to be tree shaken.
|
| - }
|
| -
|
| - @override
|
| - void visitLocalVariableAccess(AstNode node, AccessSemantics semantics) {
|
| - // Locals don't need to be tree shaken.
|
| - }
|
| -
|
| - @override
|
| - void visitParameterAccess(AstNode node, AccessSemantics semantics) {
|
| - // Locals don't need to be tree shaken.
|
| - }
|
| -
|
| - @override
|
| - void visitStaticFieldAccess(AstNode node, AccessSemantics semantics) {
|
| - analysis.accesses.add(semantics.element);
|
| - }
|
| -
|
| - @override
|
| - void visitStaticMethodAccess(AstNode node, AccessSemantics semantics) {
|
| - // Method tear-off. TODO(paulberry): implement.
|
| - super.visitStaticMethodAccess(node, semantics);
|
| - }
|
| -
|
| - @override
|
| - void visitStaticPropertyAccess(AstNode node, AccessSemantics semantics) {
|
| - // TODO(paulberry): implement.
|
| - super.visitStaticPropertyAccess(node, semantics);
|
| - }
|
| -
|
| - @override
|
| - void visitConstructorDeclaration(ConstructorDeclaration node) {
|
| - // TODO(paulberry): handle parameter list.
|
| - node.initializers.accept(this);
|
| - node.body.accept(this);
|
| - if (node.factoryKeyword == null) {
|
| - // This is a generative constructor. Figure out if it is redirecting.
|
| - // If it isn't, then the constructor instantiates the class so we need to
|
| - // add the class to analysis.instantiates. (If it is redirecting, then
|
| - // we don't need to, because the redirected-to constructor will take care
|
| - // of that).
|
| - if (node.initializers.length != 1 || node.initializers[0] is! RedirectingConstructorInvocation) {
|
| - ClassElement classElement = node.element.enclosingElement;
|
| - analysis.instantiates.add(node.element.enclosingElement);
|
| - if (!node.initializers.any((i) => i is SuperConstructorInvocation)) {
|
| - if (classElement.supertype != null) {
|
| - ClassElement superClass = classElement.supertype.element;
|
| - ConstructorElement superConstructor = superClass.unnamedConstructor;
|
| - if (superConstructor != null) {
|
| - // TODO(johnniwinther): Register instantiated type and selector.
|
| - analysis.calls.add(superConstructor);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - } else if (node.redirectedConstructor != null) {
|
| - if (node.redirectedConstructor.staticElement == null) {
|
| - // Factory constructor redirects to a non-existent constructor.
|
| - // TODO(paulberry): handle this.
|
| - throw new UnimplementedError();
|
| - } else {
|
| - analysis.calls.add(node.redirectedConstructor.staticElement);
|
| - }
|
| - }
|
| - }
|
| -
|
| - @override
|
| - void
|
| - visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
|
| - // Note: we don't have to worry about node.staticElement being
|
| - // null, because that would have been detected by the analyzer and
|
| - // reported as a compile time error.
|
| - analysis.calls.add(node.staticElement);
|
| - }
|
| -
|
| - @override
|
| - void handleAssignmentExpression(AssignmentExpression node) {
|
| - // Don't special-case assignment expressions.
|
| - }
|
| -}
|
|
|