Thu, 25 Jul 2013 11:56:12 +0200
8021244: Inconsistent stackmap with splitter threshold set very low
Reviewed-by: sundar, lagergren
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 */
26 package jdk.nashorn.internal.ir;
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;
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;
42 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
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;
52 /** Symbol table - keys must be returned in the order they were put in. */
53 protected final Map<String, Symbol> symbols;
55 /** Entry label. */
56 protected final Label entryLabel;
58 /** Break label. */
59 private final Label breakLabel;
61 /** Does the block/function need a new scope? */
62 protected final int flags;
64 /** Flag indicating that this block needs scope */
65 public static final int NEEDS_SCOPE = 1 << 0;
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;
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;
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);
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 }
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 }
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 }
121 /**
122 * Clear the symbols in a block
123 * TODO: make this immutable
124 */
125 public void clearSymbols() {
126 symbols.clear();
127 }
129 @Override
130 public Node ensureUniqueLabels(final LexicalContext lc) {
131 return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols));
132 }
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 }
146 return this;
147 }
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 }
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 }
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 }
177 @Override
178 public void toString(final StringBuilder sb) {
179 for (final Node statement : statements) {
180 statement.toString(sb);
181 sb.append(';');
182 }
183 }
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());
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 });
203 for (final Symbol symbol : values) {
204 symbol.print(stream);
205 }
207 return !values.isEmpty();
208 }
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 }
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 }
233 @Override
234 public boolean isTerminal() {
235 return getFlag(IS_TERMINAL);
236 }
238 /**
239 * Get the entry label for this block
240 * @return the entry label
241 */
242 public Label getEntryLabel() {
243 return entryLabel;
244 }
246 @Override
247 public Label getBreakLabel() {
248 return breakLabel;
249 }
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 }
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 }
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 }
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 }
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 }
305 @Override
306 public Block clearFlag(final LexicalContext lc, int flag) {
307 return setFlags(lc, flags & ~flag);
308 }
310 @Override
311 public Block setFlag(final LexicalContext lc, int flag) {
312 return setFlags(lc, flags | flag);
313 }
315 @Override
316 public boolean getFlag(final int flag) {
317 return (flags & flag) == flag;
318 }
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 }
330 return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols));
331 }
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 }
349 @Override
350 public boolean isBreakableWithoutLabel() {
351 return false;
352 }
354 @Override
355 public List<Label> getLabels() {
356 return Collections.singletonList(breakLabel);
357 }
359 @Override
360 public Node accept(NodeVisitor<? extends LexicalContext> visitor) {
361 return Acceptor.accept(this, visitor);
362 }
363 }