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.io.PrintWriter;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Collections;
32  import java.util.Comparator;
33  import java.util.LinkedHashMap;
34  import java.util.List;
35  import java.util.Map;
36  
37  import jdk.nashorn.internal.codegen.Label;
38  import jdk.nashorn.internal.codegen.types.Type;
39  import jdk.nashorn.internal.ir.annotations.Immutable;
40  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
41  
42  import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
43  
44  /**
45   * IR representation for a list of statements.
46   */
47  @Immutable
48  public class Block extends Node implements BreakableNode, Flags<Block> {
49      /** List of statements */
50      protected final List<Statement> statements;
51  
52      /** Symbol table - keys must be returned in the order they were put in. */
53      protected final Map<String, Symbol> symbols;
54  
55      /** Entry label. */
56      protected final Label entryLabel;
57  
58      /** Break label. */
59      private final Label breakLabel;
60  
61      /** Does the block/function need a new scope? */
62      protected final int flags;
63  
64      /** Flag indicating that this block needs scope */
65      public static final int NEEDS_SCOPE = 1 << 0;
66  
67      /**
68       * Flag indicating whether this block needs
69       * self symbol assignment at the start. This is used only for
70       * blocks that are the bodies of function nodes who refer to themselves
71       * by name. It causes codegen to insert a var [fn_name] = __callee__
72       * at the start of the body
73       */
74      public static final int NEEDS_SELF_SYMBOL = 1 << 1;
75  
76      /**
77       * Is this block tagged as terminal based on its contents
78       * (usually the last statement)
79       */
80      public static final int IS_TERMINAL = 1 << 2;
81  
82      /**
83       * Constructor
84       *
85       * @param token      token
86       * @param finish     finish
87       * @param statements statements
88       */
89      public Block(final long token, final int finish, final Statement... statements) {
90          super(token, finish);
91  
92          this.statements = Arrays.asList(statements);
93          this.symbols    = new LinkedHashMap<>();
94          this.entryLabel = new Label("block_entry");
95          this.breakLabel = new Label("block_break");
96          final int len = statements.length;
97          this.flags = (len > 0 && statements[len - 1].hasTerminalFlags()) ? IS_TERMINAL : 0;
98      }
99  
100     /**
101      * Constructor
102      *
103      * @param token      token
104      * @param finish     finish
105      * @param statements statements
106      */
107     public Block(final long token, final int finish, final List<Statement> statements) {
108         this(token, finish, statements.toArray(new Statement[statements.size()]));
109     }
110 
111     private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols) {
112         super(block);
113         this.statements = statements;
114         this.flags      = flags;
115         this.symbols    = new LinkedHashMap<>(symbols); //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
116         this.entryLabel = new Label(block.entryLabel);
117         this.breakLabel = new Label(block.breakLabel);
118         this.finish     = finish;
119     }
120 
121     /**
122      * Clear the symbols in a block
123      * TODO: make this immutable
124      */
125     public void clearSymbols() {
126         symbols.clear();
127     }
128 
129     @Override
130     public Node ensureUniqueLabels(final LexicalContext lc) {
131         return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
132     }
133 
134     /**
135      * Assist in IR navigation.
136      *
137      * @param visitor IR navigating visitor.
138      * @return new or same node
139      */
140     @Override
141     public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
142         if (visitor.enterBlock(this)) {
143             return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Statement.class, statements)));
144         }
145 
146         return this;
147     }
148 
149     /**
150      * Get an iterator for all the symbols defined in this block
151      * @return symbol iterator
152      */
153     public List<Symbol> getSymbols() {
154         return Collections.unmodifiableList(new ArrayList<>(symbols.values()));
155     }
156 
157     /**
158      * Retrieves an existing symbol defined in the current block.
159      * @param name the name of the symbol
160      * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't
161      * define a symbol with this name.T
162      */
163     public Symbol getExistingSymbol(final String name) {
164         return symbols.get(name);
165     }
166 
167     /**
168      * Test if this block represents a <tt>catch</tt> block in a <tt>try</tt> statement.
169      * This is used by the Splitter as catch blocks are not be subject to splitting.
170      *
171      * @return true if this block represents a catch block in a try statement.
172      */
173     public boolean isCatchBlock() {
174         return statements.size() == 1 && statements.get(0) instanceof CatchNode;
175     }
176 
177     @Override
178     public void toString(final StringBuilder sb) {
179         for (final Node statement : statements) {
180             statement.toString(sb);
181             sb.append(';');
182         }
183     }
184 
185     /**
186      * Print symbols in block in alphabetical order, sorted on name
187      * Used for debugging, see the --print-symbols flag
188      *
189      * @param stream print writer to output symbols to
190      *
191      * @return true if symbols were found
192      */
193     public boolean printSymbols(final PrintWriter stream) {
194         final List<Symbol> values = new ArrayList<>(symbols.values());
195 
196         Collections.sort(values, new Comparator<Symbol>() {
197             @Override
198             public int compare(final Symbol s0, final Symbol s1) {
199                 return s0.getName().compareTo(s1.getName());
200             }
201         });
202 
203         for (final Symbol symbol : values) {
204             symbol.print(stream);
205         }
206 
207         return !values.isEmpty();
208     }
209 
210     /**
211      * Tag block as terminal or non terminal
212      * @param lc          lexical context
213      * @param isTerminal is block terminal
214      * @return same block, or new if flag changed
215      */
216     public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
217         return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
218     }
219 
220     /**
221      * Set the type of the return symbol in this block if present.
222      * @param returnType the new type
223      * @return this block
224      */
225     public Block setReturnType(final Type returnType) {
226         final Symbol symbol = getExistingSymbol(RETURN.symbolName());
227         if (symbol != null) {
228             symbol.setTypeOverride(returnType);
229         }
230         return this;
231     }
232 
233     @Override
234     public boolean isTerminal() {
235         return getFlag(IS_TERMINAL);
236     }
237 
238     /**
239      * Get the entry label for this block
240      * @return the entry label
241      */
242     public Label getEntryLabel() {
243         return entryLabel;
244     }
245 
246     @Override
247     public Label getBreakLabel() {
248         return breakLabel;
249     }
250 
251     /**
252      * Get the list of statements in this block
253      *
254      * @return a list of statements
255      */
256     public List<Statement> getStatements() {
257         return Collections.unmodifiableList(statements);
258     }
259 
260     /**
261      * Reset the statement list for this block
262      *
263      * @param lc lexical context
264      * @param statements new statement list
265      * @return new block if statements changed, identity of statements == block.statements
266      */
267     public Block setStatements(final LexicalContext lc, final List<Statement> statements) {
268         if (this.statements == statements) {
269             return this;
270         }
271         int lastFinish = 0;
272         if (!statements.isEmpty()) {
273             lastFinish = statements.get(statements.size() - 1).getFinish();
274         }
275         return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols));
276     }
277 
278     /**
279      * Add or overwrite an existing symbol in the block
280      *
281      * @param lc     get lexical context
282      * @param symbol symbol
283      */
284     public void putSymbol(final LexicalContext lc, final Symbol symbol) {
285         symbols.put(symbol.getName(), symbol);
286     }
287 
288     /**
289      * Check whether scope is necessary for this Block
290      *
291      * @return true if this function needs a scope
292      */
293     public boolean needsScope() {
294         return (flags & NEEDS_SCOPE) == NEEDS_SCOPE;
295     }
296 
297     @Override
298     public Block setFlags(final LexicalContext lc, int flags) {
299         if (this.flags == flags) {
300             return this;
301         }
302         return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
303     }
304 
305     @Override
306     public Block clearFlag(final LexicalContext lc, int flag) {
307         return setFlags(lc, flags & ~flag);
308     }
309 
310     @Override
311     public Block setFlag(final LexicalContext lc, int flag) {
312         return setFlags(lc, flags | flag);
313     }
314 
315     @Override
316     public boolean getFlag(final int flag) {
317         return (flags & flag) == flag;
318     }
319 
320     /**
321      * Set the needs scope flag.
322      * @param lc lexicalContext
323      * @return new block if state changed, otherwise this
324      */
325     public Block setNeedsScope(final LexicalContext lc) {
326         if (needsScope()) {
327             return this;
328         }
329 
330         return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols));
331     }
332 
333     /**
334      * Computationally determine the next slot for this block,
335      * indexed from 0. Use this as a relative base when computing
336      * frames
337      * @return next slot
338      */
339     public int nextSlot() {
340         int next = 0;
341         for (final Symbol symbol : getSymbols()) {
342             if (symbol.hasSlot()) {
343                 next += symbol.slotCount();
344             }
345         }
346         return next;
347     }
348 
349     @Override
350     public boolean isBreakableWithoutLabel() {
351         return false;
352     }
353 
354     @Override
355     public List<Label> getLabels() {
356         return Collections.singletonList(breakLabel);
357     }
358 
359     @Override
360     public Node accept(NodeVisitor<? extends LexicalContext> visitor) {
361         return Acceptor.accept(this, visitor);
362     }
363 }