Block.java revision 1060:ca67ae7c46cb
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
26package jdk.nashorn.internal.ir;
27
28import java.io.PrintWriter;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Collections;
32import java.util.Comparator;
33import java.util.LinkedHashMap;
34import java.util.List;
35import java.util.Map;
36import jdk.nashorn.internal.codegen.Label;
37import jdk.nashorn.internal.ir.annotations.Immutable;
38import jdk.nashorn.internal.ir.visitor.NodeVisitor;
39
40/**
41 * IR representation for a list of statements.
42 */
43@Immutable
44public class Block extends Node implements BreakableNode, Terminal, Flags<Block> {
45    /** List of statements */
46    protected final List<Statement> statements;
47
48    /** Symbol table - keys must be returned in the order they were put in. */
49    protected final Map<String, Symbol> symbols;
50
51    /** Entry label. */
52    private final Label entryLabel;
53
54    /** Break label. */
55    private final Label breakLabel;
56
57    /** Does the block/function need a new scope? */
58    protected final int flags;
59
60    /**
61     * @see JoinPredecessor
62     */
63    private final LocalVariableConversion conversion;
64
65    /** Flag indicating that this block needs scope */
66    public static final int NEEDS_SCOPE = 1 << 0;
67
68    /**
69     * Is this block tagged as terminal based on its contents
70     * (usually the last statement)
71     */
72    public static final int IS_TERMINAL = 1 << 2;
73
74    /**
75     * Is this block the eager global scope - i.e. the original program. This isn't true for the
76     * outermost level of recompiles
77     */
78    public static final int IS_GLOBAL_SCOPE = 1 << 3;
79
80    /**
81     * Constructor
82     *
83     * @param token      The first token of the block
84     * @param finish     The index of the last character
85     * @param flags      The flags of the block
86     * @param statements All statements in the block
87     */
88    public Block(final long token, final int finish, final int flags, final Statement... statements) {
89        super(token, finish);
90
91        this.statements = Arrays.asList(statements);
92        this.symbols    = new LinkedHashMap<>();
93        this.entryLabel = new Label("block_entry");
94        this.breakLabel = new Label("block_break");
95        final int len = statements.length;
96        final int terminalFlags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0;
97        this.flags = terminalFlags | flags;
98        this.conversion = null;
99    }
100
101    /**
102     * Constructs a new block
103     *
104     * @param token The first token of the block
105     * @param finish The index of the last character
106     * @param statements All statements in the block
107     */
108    public Block(final long token, final int finish, final Statement...statements){
109        this(token, finish, 0, statements);
110    }
111
112    /**
113     * Constructs a new block
114     *
115     * @param token The first token of the block
116     * @param finish The index of the last character
117     * @param statements All statements in the block
118     */
119    public Block(final long token, final int finish, final List<Statement> statements){
120        this(token, finish, 0, statements);
121    }
122
123    /**
124     * Constructor
125     *
126     * @param token      The first token of the block
127     * @param finish     The index of the last character
128     * @param flags      The flags of the block
129     * @param statements All statements in the block
130     */
131    public Block(final long token, final int finish, final int flags, final List<Statement> statements) {
132        this(token, finish, flags, statements.toArray(new Statement[statements.size()]));
133    }
134
135    private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, final LocalVariableConversion conversion) {
136        super(block, finish);
137        this.statements = statements;
138        this.flags      = flags;
139        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
140        this.entryLabel = new Label(block.entryLabel);
141        this.breakLabel = new Label(block.breakLabel);
142        this.conversion = conversion;
143    }
144
145    /**
146     * Is this block the outermost eager global scope - i.e. the primordial program?
147     * Used for global anchor point for scope depth computation for recompilation code
148     * @return true if outermost eager global scope
149     */
150    public boolean isGlobalScope() {
151        return getFlag(IS_GLOBAL_SCOPE);
152    }
153
154    /**
155     * Clear the symbols in the block.
156     * TODO: make this immutable.
157     */
158    public void clearSymbols() {
159        symbols.clear();
160    }
161
162    @Override
163    public Node ensureUniqueLabels(final LexicalContext lc) {
164        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
165    }
166
167    /**
168     * Assist in IR navigation.
169     *
170     * @param visitor IR navigating visitor.
171     * @return new or same node
172     */
173    @Override
174    public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
175        if (visitor.enterBlock(this)) {
176            return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, statements)));
177        }
178
179        return this;
180    }
181
182    /**
183     * Get a copy of the list for all the symbols defined in this block
184     * @return symbol iterator
185     */
186    public List<Symbol> getSymbols() {
187        return Collections.unmodifiableList(new ArrayList<>(symbols.values()));
188    }
189
190    /**
191     * Retrieves an existing symbol defined in the current block.
192     * @param name the name of the symbol
193     * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't
194     * define a symbol with this name.T
195     */
196    public Symbol getExistingSymbol(final String name) {
197        return symbols.get(name);
198    }
199
200    /**
201     * Test if this block represents a <tt>catch</tt> block in a <tt>try</tt> statement.
202     * This is used by the Splitter as catch blocks are not be subject to splitting.
203     *
204     * @return true if this block represents a catch block in a try statement.
205     */
206    public boolean isCatchBlock() {
207        return statements.size() == 1 && statements.get(0) instanceof CatchNode;
208    }
209
210    @Override
211    public void toString(final StringBuilder sb, final boolean printType) {
212        for (final Node statement : statements) {
213            statement.toString(sb, printType);
214            sb.append(';');
215        }
216    }
217
218    /**
219     * Print symbols in block in alphabetical order, sorted on name
220     * Used for debugging, see the --print-symbols flag
221     *
222     * @param stream print writer to output symbols to
223     *
224     * @return true if symbols were found
225     */
226    public boolean printSymbols(final PrintWriter stream) {
227        final List<Symbol> values = new ArrayList<>(symbols.values());
228
229        Collections.sort(values, new Comparator<Symbol>() {
230            @Override
231            public int compare(final Symbol s0, final Symbol s1) {
232                return s0.getName().compareTo(s1.getName());
233            }
234        });
235
236        for (final Symbol symbol : values) {
237            symbol.print(stream);
238        }
239
240        return !values.isEmpty();
241    }
242
243    /**
244     * Tag block as terminal or non terminal
245     * @param lc          lexical context
246     * @param isTerminal is block terminal
247     * @return same block, or new if flag changed
248     */
249    public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
250        return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
251    }
252
253    @Override
254    public int getFlags() {
255        return flags;
256    }
257
258    /**
259     * Is this a terminal block, i.e. does it end control flow like ending with a throw or return?
260     *
261     * @return true if this node statement is terminal
262     */
263    @Override
264    public boolean isTerminal() {
265        return getFlag(IS_TERMINAL);
266    }
267
268    /**
269     * Get the entry label for this block
270     * @return the entry label
271     */
272    public Label getEntryLabel() {
273        return entryLabel;
274    }
275
276    @Override
277    public Label getBreakLabel() {
278        return breakLabel;
279    }
280
281    @Override
282    public Block setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
283        if(this.conversion == conversion) {
284            return this;
285        }
286        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
287    }
288
289    @Override
290    public LocalVariableConversion getLocalVariableConversion() {
291        return conversion;
292    }
293
294    /**
295     * Get the list of statements in this block
296     *
297     * @return a list of statements
298     */
299    public List<Statement> getStatements() {
300        return Collections.unmodifiableList(statements);
301    }
302
303    /**
304     * Returns the number of statements in the block.
305     * @return the number of statements in the block.
306     */
307    public int getStatementCount() {
308        return statements.size();
309    }
310
311    /**
312     * Returns the line number of the first statement in the block.
313     * @return the line number of the first statement in the block, or -1 if the block has no statements.
314     */
315    public int getFirstStatementLineNumber() {
316        if(statements == null || statements.isEmpty()) {
317            return -1;
318        }
319        return statements.get(0).getLineNumber();
320    }
321
322    /**
323     * Reset the statement list for this block
324     *
325     * @param lc lexical context
326     * @param statements new statement list
327     * @return new block if statements changed, identity of statements == block.statements
328     */
329    public Block setStatements(final LexicalContext lc, final List<Statement> statements) {
330        if (this.statements == statements) {
331            return this;
332        }
333        int lastFinish = 0;
334        if (!statements.isEmpty()) {
335            lastFinish = statements.get(statements.size() - 1).getFinish();
336        }
337        return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols, conversion));
338    }
339
340    /**
341     * Add or overwrite an existing symbol in the block
342     *
343     * @param lc     get lexical context
344     * @param symbol symbol
345     */
346    public void putSymbol(final LexicalContext lc, final Symbol symbol) {
347        symbols.put(symbol.getName(), symbol);
348    }
349
350    /**
351     * Check whether scope is necessary for this Block
352     *
353     * @return true if this function needs a scope
354     */
355    public boolean needsScope() {
356        return (flags & NEEDS_SCOPE) == NEEDS_SCOPE;
357    }
358
359    @Override
360    public Block setFlags(final LexicalContext lc, final int flags) {
361        if (this.flags == flags) {
362            return this;
363        }
364        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
365    }
366
367    @Override
368    public Block clearFlag(final LexicalContext lc, final int flag) {
369        return setFlags(lc, flags & ~flag);
370    }
371
372    @Override
373    public Block setFlag(final LexicalContext lc, final int flag) {
374        return setFlags(lc, flags | flag);
375    }
376
377    @Override
378    public boolean getFlag(final int flag) {
379        return (flags & flag) == flag;
380    }
381
382    /**
383     * Set the needs scope flag.
384     * @param lc lexicalContext
385     * @return new block if state changed, otherwise this
386     */
387    public Block setNeedsScope(final LexicalContext lc) {
388        if (needsScope()) {
389            return this;
390        }
391
392        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols, conversion));
393    }
394
395    /**
396     * Computationally determine the next slot for this block,
397     * indexed from 0. Use this as a relative base when computing
398     * frames
399     * @return next slot
400     */
401    public int nextSlot() {
402        int next = 0;
403        for (final Symbol symbol : getSymbols()) {
404            if (symbol.hasSlot()) {
405                next += symbol.slotCount();
406            }
407        }
408        return next;
409    }
410
411    @Override
412    public boolean isBreakableWithoutLabel() {
413        return false;
414    }
415
416    @Override
417    public List<Label> getLabels() {
418        return Collections.unmodifiableList(Arrays.asList(entryLabel, breakLabel));
419    }
420
421    @Override
422    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
423        return Acceptor.accept(this, visitor);
424    }
425}
426