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