View Javadoc
1   /*
2    * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package jdk.nashorn.internal.ir;
27  
28  import java.util.ArrayDeque;
29  import java.util.ArrayList;
30  import java.util.Deque;
31  import java.util.List;
32  
33  /**
34   * This is a subclass of lexical context used for filling
35   * blocks (and function nodes) with statements. When popping
36   * a block from the lexical context, any statements that have
37   * been generated in it are commited to the block. This saves
38   * unnecessary object mutations and lexical context replacement
39   */
40  public class BlockLexicalContext extends LexicalContext {
41      /** statement stack, each block on the lexical context maintains one of these, which is
42       *  committed to the block on pop */
43      private Deque<List<Statement>> sstack = new ArrayDeque<>();
44  
45      /** Last non debug statement emitted in this context */
46      protected Statement lastStatement;
47  
48      @Override
49      public <T extends LexicalContextNode> T push(final T node) {
50          T pushed = super.push(node);
51          if (node instanceof Block) {
52              sstack.push(new ArrayList<Statement>());
53          }
54          return pushed;
55      }
56  
57      /**
58       * Get the statement list from the stack, possibly filtered
59       * @return statement list
60       */
61      protected List<Statement> popStatements() {
62          return sstack.pop();
63      }
64  
65      /**
66       * Override this method to perform some additional processing on the block after its statements have been set. By
67       * default does nothing and returns the original block.
68       * @param block the block to operate on
69       * @return a modified block.
70       */
71      protected Block afterSetStatements(Block block) {
72          return block;
73      }
74  
75      @SuppressWarnings("unchecked")
76      @Override
77      public <T extends LexicalContextNode> T pop(final T node) {
78          T expected = node;
79          if (node instanceof Block) {
80              final List<Statement> newStatements = popStatements();
81              expected = (T)((Block)node).setStatements(this, newStatements);
82              expected = (T)afterSetStatements((Block)expected);
83              if (!sstack.isEmpty()) {
84                  lastStatement = lastStatement(sstack.peek());
85              }
86          }
87          return super.pop(expected);
88      }
89  
90      /**
91       * Append a statement to the block being generated
92       * @param statement statement to add
93       */
94      public void appendStatement(final Statement statement) {
95          assert statement != null;
96          sstack.peek().add(statement);
97          lastStatement = statement;
98      }
99  
100     /**
101      * Prepend a statement to the block being generated
102      * @param statement statement to prepend
103      * @return the prepended statement
104      */
105     public Node prependStatement(final Statement statement) {
106         assert statement != null;
107         sstack.peek().add(0, statement);
108         return statement;
109     }
110 
111     /**
112      * Get the last statement that was emitted into a block
113      * @return the last statement emitted
114      */
115     public Statement getLastStatement() {
116         return lastStatement;
117     }
118 
119     private static Statement lastStatement(final List<Statement> statements) {
120         final int s = statements.size();
121         return s == 0 ? null : statements.get(s - 1);
122     }
123 }