File: PeepholeOptimizationsPassTest.java

package info (click to toggle)
closure-compiler 20130227%2Bdfsg1-10.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 12,792 kB
  • sloc: java: 175,338; javascript: 20,728; xml: 371; makefile: 19; sh: 6
file content (258 lines) | stat: -rw-r--r-- 7,862 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
 * Copyright 2010 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

import java.util.List;
import java.util.Set;

/**
 * Unit tests for PeepholeOptimizationsPass.
 *
 */
public class PeepholeOptimizationsPassTest extends CompilerTestCase {

  private ImmutableList<AbstractPeepholeOptimization> currentPeepholePasses;

  @Override
  public void setUp() throws Exception {
    super.setUp();
    super.enableLineNumberCheck(true);
  }

  @Override
  public CompilerPass getProcessor(final Compiler compiler) {
    return new PeepholeOptimizationsPass(compiler,
        currentPeepholePasses.toArray(
            new AbstractPeepholeOptimization[currentPeepholePasses.size()]));
  }

  @Override
  protected int getNumRepetitions() {
    // Our tests do not require multiple passes to reach a fixed-point.
    return 1;
  }

  /**
   * PeepholeOptimizationsPass should handle the case when no peephole
   * optimizations are turned on.
   */
  public void testEmptyPass() {
    currentPeepholePasses = ImmutableList.<AbstractPeepholeOptimization>of();

    testSame("var x; var y;");
  }

  public void testOptimizationOrder() {
    /*
     * We need to make sure that: 1) We are only traversing the AST once 2) For
     * each node, we visit the optimizations in the client-supplied order
     *
     * To test this, we create two fake optimizations that each make an entry in
     * the visitationLog when they are passed a name node to optimize.
     *
     * Each entry is of the form nameX where 'name' is the name of the name node
     * visited and X is the identity of the optimization (1 or 2 in this case).
     * After the pass is run, we verify the correct ordering by querying the
     * log.
     *
     * Using a log, rather than, say, transforming nodes, allows us to ensure
     * not only that we are visiting each node but that our visits occur in the
     * right order (i.e. we need to make sure we're not traversing the entire
     * AST for the first optimization and then a second time for the second).
     */

    final List<String> visitationLog = Lists.newArrayList();

    AbstractPeepholeOptimization note1Applied =
        new AbstractPeepholeOptimization() {
      @Override
      public Node optimizeSubtree(Node node) {
        if (node.isName()) {
          visitationLog.add(node.getString() + "1");
        }

        return node;
      }
    };

    AbstractPeepholeOptimization note2Applied =
        new AbstractPeepholeOptimization() {
      @Override
      public Node optimizeSubtree(Node node) {
        if (node.isName()) {
          visitationLog.add(node.getString() + "2");
        }

        return node;
      }
    };

    currentPeepholePasses =
      ImmutableList.<
       AbstractPeepholeOptimization>of(note1Applied, note2Applied);

    test("var x; var y", "var x; var y");

    /*
     * We expect the optimization order to be: "x" visited by optimization1 "x"
     * visited by optimization2 "y" visited by optimization1 "y" visited by
     * optimization2
     */

    assertEquals(4, visitationLog.size());
    assertEquals("x1", visitationLog.get(0));
    assertEquals("x2", visitationLog.get(1));
    assertEquals("y1", visitationLog.get(2));
    assertEquals("y2", visitationLog.get(3));
  }

  /**
   * A peephole optimization that, given a subtree consisting of a VAR node,
   * removes children of that node named "x".
   */
  private static class RemoveNodesNamedXUnderVarOptimization
      extends AbstractPeepholeOptimization {
    @Override
    public Node optimizeSubtree(Node node) {
      if (node.isVar()) {
        Set<Node> nodesToRemove = Sets.newHashSet();

        for (Node child : node.children()) {
          if ("x".equals(child.getString())) {
            nodesToRemove.add(child);
          }
        }

        for (Node childToRemove : nodesToRemove) {
          node.removeChild(childToRemove);
          reportCodeChange();
        }
      }

      return node;
    }
  }

  /**
   * A peephole optimization that, given a subtree consisting of a name node
   * named "x" removes that node.
   */
  private static class RemoveNodesNamedXOptimization
      extends AbstractPeepholeOptimization {
    @Override
    public Node optimizeSubtree(Node node) {
      if (node.isName() && "x".equals(node.getString())) {
        node.getParent().removeChild(node);
        reportCodeChange();

        return null;
      }

      return node;
    }
  }

  /**
   * A peephole optimization that, given a subtree consisting of a name node
   * named "x" whose parent is a VAR node, removes the parent VAR node.
   */
  private static class RemoveParentVarsForNodesNamedX
      extends AbstractPeepholeOptimization {
    @Override
    public Node optimizeSubtree(Node node) {
      if (node.isName() && "x".equals(node.getString())) {
        Node parent = node.getParent();
        if (parent.isVar()) {
          parent.getParent().removeChild(parent);
          reportCodeChange();
          return null;
        }
      }
      return node;
    }
  }

  /**
   * A peephole optimization that, given a subtree consisting of a name node
   * named "y", replaces it with a name node named "x";
   */
  private static class RenameYToX extends AbstractPeepholeOptimization {
    @Override
    public Node optimizeSubtree(Node node) {
      if (node.isName() && "y".equals(node.getString())) {
        Node replacement = Node.newString(Token.NAME, "x");

        node.getParent().replaceChild(node, replacement);
        reportCodeChange();

        return replacement;
      }
      return node;
    }
  }

  public void testOptimizationRemovingSubtreeChild() {
    currentPeepholePasses = ImmutableList.<AbstractPeepholeOptimization>of(new
          RemoveNodesNamedXUnderVarOptimization());

    test("var x,y;", "var y;");
    test("var y,x;", "var y;");
    test("var x,y,x;", "var y;");
  }

  public void testOptimizationRemovingSubtree() {
    currentPeepholePasses = ImmutableList.<AbstractPeepholeOptimization>of(new
          RemoveNodesNamedXOptimization());

    test("var x,y;", "var y;");
    test("var y,x;", "var y;");
    test("var x,y,x;", "var y;");
  }

  public void testOptimizationRemovingSubtreeParent() {
    currentPeepholePasses = ImmutableList.<AbstractPeepholeOptimization>of(new
          RemoveParentVarsForNodesNamedX());

    test("var x; var y", "var y");
  }

  /**
   * Test the case where the first peephole optimization removes a node and the
   * second wants to remove (the now nonexistent) parent of that node.
   */
  public void testOptimizationsRemoveParentAfterRemoveChild() {
    currentPeepholePasses = ImmutableList.<AbstractPeepholeOptimization>of(
          new RemoveNodesNamedXOptimization(),
          new RemoveParentVarsForNodesNamedX());

    test("var x,y; var z;", "var y; var z;");
  }

  public void testOptimizationReplacingNode() {
    currentPeepholePasses = ImmutableList.<AbstractPeepholeOptimization>of(
          new RenameYToX(),
          new RemoveParentVarsForNodesNamedX());

    test("var y; var z;", "var z;");
  }
}