Block.java revision 1060:ca67ae7c46cb
1327Sjkh/*
2228990Suqs * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3327Sjkh * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4327Sjkh *
5327Sjkh * This code is free software; you can redistribute it and/or modify it
6327Sjkh * under the terms of the GNU General Public License version 2 only, as
7327Sjkh * published by the Free Software Foundation.  Oracle designates this
8327Sjkh * particular file as subject to the "Classpath" exception as provided
9327Sjkh * by Oracle in the LICENSE file that accompanied this code.
10327Sjkh *
11327Sjkh * This code is distributed in the hope that it will be useful, but WITHOUT
12327Sjkh * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13327Sjkh * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14327Sjkh * version 2 for more details (a copy is included in the LICENSE file that
15327Sjkh * accompanied this code).
16327Sjkh *
17327Sjkh * You should have received a copy of the GNU General Public License version
18327Sjkh * 2 along with this work; if not, write to the Free Software Foundation,
19327Sjkh * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20327Sjkh *
2193520Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2293520Sobrien * or visit www.oracle.com if you need additional information or have any
2393520Sobrien * questions.
24222035Sflz */
25327Sjkh
26327Sjkhpackage jdk.nashorn.internal.ir;
2730221Scharnier
2883663Ssobomaximport java.io.PrintWriter;
29327Sjkhimport java.util.ArrayList;
3089458Ssobomaximport java.util.Arrays;
31179352Skeramidaimport java.util.Collections;
32179352Skeramidaimport java.util.Comparator;
338051Sjkhimport java.util.LinkedHashMap;
3416549Sjkhimport java.util.List;
3513946Sjdpimport java.util.Map;
36327Sjkhimport jdk.nashorn.internal.codegen.Label;
37327Sjkhimport jdk.nashorn.internal.ir.annotations.Immutable;
3884745Ssobomaximport jdk.nashorn.internal.ir.visitor.NodeVisitor;
39147043Ssobomax
40147043Ssobomax/**
41327Sjkh * IR representation for a list of statements.
42327Sjkh */
43327Sjkh@Immutable
44327Sjkhpublic class Block extends Node implements BreakableNode, Terminal, Flags<Block> {
45194497Sbrian    /** List of statements */
46327Sjkh    protected final List<Statement> statements;
4711780Sjkh
48327Sjkh    /** Symbol table - keys must be returned in the order they were put in. */
49327Sjkh    protected final Map<String, Symbol> symbols;
5038933Sjkh
5184745Ssobomax    /** Entry label. */
52327Sjkh    private final Label entryLabel;
53327Sjkh
5484670Ssobomax    /** Break label. */
5584670Ssobomax    private final Label breakLabel;
567986Sjkh
57327Sjkh    /** Does the block/function need a new scope? */
58327Sjkh    protected final int flags;
5941530Sasami
6038933Sjkh    /**
6149637Sbillf     * @see JoinPredecessor
62102384Sobrien     */
63102384Sobrien    private final LocalVariableConversion conversion;
64102384Sobrien
65102384Sobrien    /** Flag indicating that this block needs scope */
66102384Sobrien    public static final int NEEDS_SCOPE = 1 << 0;
6795161Sobrien
6841530Sasami    /**
6941530Sasami     * Is this block tagged as terminal based on its contents
70213718Sflz     * (usually the last statement)
71213718Sflz     */
72213718Sflz    public static final int IS_TERMINAL = 1 << 2;
73213718Sflz
7441530Sasami    /**
7595161Sobrien     * Is this block the eager global scope - i.e. the original program. This isn't true for the
7641530Sasami     * outermost level of recompiles
7741530Sasami     */
7849637Sbillf    public static final int IS_GLOBAL_SCOPE = 1 << 3;
7995161Sobrien
80101302Sobrien    /**
81102384Sobrien     * Constructor
8295161Sobrien     *
8341530Sasami     * @param token      The first token of the block
8489458Ssobomax     * @param finish     The index of the last character
85213718Sflz     * @param flags      The flags of the block
86213718Sflz     * @param statements All statements in the block
8789458Ssobomax     */
8841530Sasami    public Block(final long token, final int finish, final int flags, final Statement... statements) {
89327Sjkh        super(token, finish);
90147043Ssobomax
91152210Skrion        this.statements = Arrays.asList(statements);
92152210Skrion        this.symbols    = new LinkedHashMap<>();
93152210Skrion        this.entryLabel = new Label("block_entry");
94152210Skrion        this.breakLabel = new Label("block_break");
95152210Skrion        final int len = statements.length;
96152210Skrion        final int terminalFlags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0;
97152210Skrion        this.flags = terminalFlags | flags;
98152210Skrion        this.conversion = null;
99152210Skrion    }
100152210Skrion
101152210Skrion    /**
102152210Skrion     * Constructs a new block
103152210Skrion     *
104152210Skrion     * @param token The first token of the block
105152210Skrion     * @param finish The index of the last character
106152210Skrion     * @param statements All statements in the block
107152210Skrion     */
108152210Skrion    public Block(final long token, final int finish, final Statement...statements){
109152210Skrion        this(token, finish, 0, statements);
110152210Skrion    }
111152210Skrion
112152210Skrion    /**
113152210Skrion     * Constructs a new block
114152210Skrion     *
115152210Skrion     * @param token The first token of the block
116152210Skrion     * @param finish The index of the last character
117152210Skrion     * @param statements All statements in the block
118152210Skrion     */
119152210Skrion    public Block(final long token, final int finish, final List<Statement> statements){
120152210Skrion        this(token, finish, 0, statements);
121152210Skrion    }
122152210Skrion
123152210Skrion    /**
124152210Skrion     * Constructor
125147043Ssobomax     *
12684670Ssobomax     * @param token      The first token of the block
12784670Ssobomax     * @param finish     The index of the last character
12884670Ssobomax     * @param flags      The flags of the block
12984670Ssobomax     * @param statements All statements in the block
13084670Ssobomax     */
13184670Ssobomax    public Block(final long token, final int finish, final int flags, final List<Statement> statements) {
13284670Ssobomax        this(token, finish, flags, statements.toArray(new Statement[statements.size()]));
13384670Ssobomax    }
13484670Ssobomax
13596388Salfred    private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, final LocalVariableConversion conversion) {
13696392Salfred        super(block, finish);
13784670Ssobomax        this.statements = statements;
13884670Ssobomax        this.flags      = flags;
13984670Ssobomax        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
14084670Ssobomax        this.entryLabel = new Label(block.entryLabel);
1417713Sjkh        this.breakLabel = new Label(block.breakLabel);
1427733Sjkh        this.conversion = conversion;
14396076Ssobomax    }
14474295Ssobomax
14574295Ssobomax    /**
14674295Ssobomax     * Is this block the outermost eager global scope - i.e. the primordial program?
1477991Sjkh     * Used for global anchor point for scope depth computation for recompilation code
1487733Sjkh     * @return true if outermost eager global scope
14974295Ssobomax     */
15074295Ssobomax    public boolean isGlobalScope() {
15174295Ssobomax        return getFlag(IS_GLOBAL_SCOPE);
15274295Ssobomax    }
15374295Ssobomax
15474295Ssobomax    /**
15574295Ssobomax     * Clear the symbols in the block.
15674295Ssobomax     * TODO: make this immutable.
15774295Ssobomax     */
15874295Ssobomax    public void clearSymbols() {
15974295Ssobomax        symbols.clear();
16096392Salfred    }
16174295Ssobomax
16274295Ssobomax    @Override
16390985Ssobomax    public Node ensureUniqueLabels(final LexicalContext lc) {
16474295Ssobomax        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
16590985Ssobomax    }
16674295Ssobomax
16790985Ssobomax    /**
16890985Ssobomax     * Assist in IR navigation.
16974295Ssobomax     *
17090985Ssobomax     * @param visitor IR navigating visitor.
17174295Ssobomax     * @return new or same node
17274295Ssobomax     */
17374295Ssobomax    @Override
17474295Ssobomax    public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
17596076Ssobomax        if (visitor.enterBlock(this)) {
17696076Ssobomax            return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, statements)));
17796076Ssobomax        }
17896076Ssobomax
17996076Ssobomax        return this;
18074295Ssobomax    }
1817992Sjkh
18274295Ssobomax    /**
1837733Sjkh     * Get a copy of the list for all the symbols defined in this block
1847713Sjkh     * @return symbol iterator
18574295Ssobomax     */
1867991Sjkh    public List<Symbol> getSymbols() {
1877733Sjkh        return Collections.unmodifiableList(new ArrayList<>(symbols.values()));
1887713Sjkh    }
18926473Sjkh
190113594Skris    /**
191113594Skris     * Retrieves an existing symbol defined in the current block.
192113594Skris     * @param name the name of the symbol
193113594Skris     * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't
194113594Skris     * define a symbol with this name.T
195113594Skris     */
196113594Skris    public Symbol getExistingSymbol(final String name) {
197113594Skris        return symbols.get(name);
198113594Skris    }
199113594Skris
200113594Skris    /**
201113594Skris     * Test if this block represents a <tt>catch</tt> block in a <tt>try</tt> statement.
202113594Skris     * This is used by the Splitter as catch blocks are not be subject to splitting.
203113594Skris     *
204113594Skris     * @return true if this block represents a catch block in a try statement.
205113594Skris     */
20626473Sjkh    public boolean isCatchBlock() {
20726473Sjkh        return statements.size() == 1 && statements.get(0) instanceof CatchNode;
20826473Sjkh    }
20926473Sjkh
21026473Sjkh    @Override
21126473Sjkh    public void toString(final StringBuilder sb, final boolean printType) {
21226473Sjkh        for (final Node statement : statements) {
213327Sjkh            statement.toString(sb, printType);
214327Sjkh            sb.append(';');
215327Sjkh        }
21631166Sjkh    }
217231300Seadler
218240682Sbapt    /**
219240682Sbapt     * Print symbols in block in alphabetical order, sorted on name
220240682Sbapt     * Used for debugging, see the --print-symbols flag
221240682Sbapt     *
222240682Sbapt     * @param stream print writer to output symbols to
223240682Sbapt     *
224240682Sbapt     * @return true if symbols were found
225240682Sbapt     */
226231300Seadler    public boolean printSymbols(final PrintWriter stream) {
22796066Ssobomax        final List<Symbol> values = new ArrayList<>(symbols.values());
22896066Ssobomax
22996066Ssobomax        Collections.sort(values, new Comparator<Symbol>() {
23096066Ssobomax            @Override
23196066Ssobomax            public int compare(final Symbol s0, final Symbol s1) {
232379Sjkh                return s0.getName().compareTo(s1.getName());
233379Sjkh            }
234379Sjkh        });
235379Sjkh
2361546Sjkh        for (final Symbol symbol : values) {
23781571Sobrien            symbol.print(stream);
238379Sjkh        }
23984750Ssobomax
24084750Ssobomax        return !values.isEmpty();
24196392Salfred    }
24284750Ssobomax
24384750Ssobomax    /**
24484750Ssobomax     * Tag block as terminal or non terminal
24584750Ssobomax     * @param lc          lexical context
2467986Sjkh     * @param isTerminal is block terminal
2477986Sjkh     * @return same block, or new if flag changed
2487986Sjkh     */
2497986Sjkh    public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
2507986Sjkh        return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
25127192Sjkh    }
2527986Sjkh
2537986Sjkh    @Override
2547986Sjkh    public int getFlags() {
2557986Sjkh        return flags;
256327Sjkh    }
2573577Sjkh
258327Sjkh    /**
259327Sjkh     * Is this a terminal block, i.e. does it end control flow like ending with a throw or return?
260327Sjkh     *
261327Sjkh     * @return true if this node statement is terminal
262327Sjkh     */
26376739Ssobomax    @Override
26476739Ssobomax    public boolean isTerminal() {
26576739Ssobomax        return getFlag(IS_TERMINAL);
26676739Ssobomax    }
2678000Sjkh
2688000Sjkh    /**
269327Sjkh     * Get the entry label for this block
270327Sjkh     * @return the entry label
271231300Seadler     */
272231300Seadler    public Label getEntryLabel() {
273231300Seadler        return entryLabel;
274327Sjkh    }
275327Sjkh
276327Sjkh    @Override
277131277Seik    public Label getBreakLabel() {
278327Sjkh        return breakLabel;
279327Sjkh    }
280327Sjkh
281131277Seik    @Override
282327Sjkh    public Block setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
283327Sjkh        if(this.conversion == conversion) {
284327Sjkh            return this;
285327Sjkh        }
286327Sjkh        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
287131277Seik    }
288327Sjkh
28941866Sjkh    @Override
29041866Sjkh    public LocalVariableConversion getLocalVariableConversion() {
29141866Sjkh        return conversion;
29241866Sjkh    }
293131277Seik
29441866Sjkh    /**
295327Sjkh     * Get the list of statements in this block
296327Sjkh     *
297327Sjkh     * @return a list of statements
298327Sjkh     */
299131277Seik    public List<Statement> getStatements() {
300327Sjkh        return Collections.unmodifiableList(statements);
30141866Sjkh    }
30241866Sjkh
30341866Sjkh    /**
30441866Sjkh     * Returns the number of statements in the block.
305131277Seik     * @return the number of statements in the block.
30641866Sjkh     */
307327Sjkh    public int getStatementCount() {
308327Sjkh        return statements.size();
309327Sjkh    }
310327Sjkh
311131277Seik    /**
312327Sjkh     * Returns the line number of the first statement in the block.
3134996Sjkh     * @return the line number of the first statement in the block, or -1 if the block has no statements.
3144996Sjkh     */
3154996Sjkh    public int getFirstStatementLineNumber() {
3164996Sjkh        if(statements == null || statements.isEmpty()) {
317131277Seik            return -1;
3184996Sjkh        }
3194996Sjkh        return statements.get(0).getLineNumber();
3204996Sjkh    }
3214996Sjkh
3224996Sjkh    /**
3234996Sjkh     * Reset the statement list for this block
324131277Seik     *
3254996Sjkh     * @param lc lexical context
3264996Sjkh     * @param statements new statement list
327327Sjkh     * @return new block if statements changed, identity of statements == block.statements
328327Sjkh     */
329327Sjkh    public Block setStatements(final LexicalContext lc, final List<Statement> statements) {
33039068Sjkh        if (this.statements == statements) {
33139068Sjkh            return this;
33296388Salfred        }
33396392Salfred        int lastFinish = 0;
33439068Sjkh        if (!statements.isEmpty()) {
335327Sjkh            lastFinish = statements.get(statements.size() - 1).getFinish();
33639068Sjkh        }
33739068Sjkh        return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols, conversion));
33896388Salfred    }
33996392Salfred
34039068Sjkh    /**
341327Sjkh     * Add or overwrite an existing symbol in the block
342327Sjkh     *
34341530Sasami     * @param lc     get lexical context
344327Sjkh     * @param symbol symbol
345327Sjkh     */
346327Sjkh    public void putSymbol(final LexicalContext lc, final Symbol symbol) {
347327Sjkh        symbols.put(symbol.getName(), symbol);
348327Sjkh    }
34933427Sjkh
350327Sjkh    /**
351327Sjkh     * Check whether scope is necessary for this Block
352327Sjkh     *
353327Sjkh     * @return true if this function needs a scope
35484745Ssobomax     */
355327Sjkh    public boolean needsScope() {
356179352Skeramida        return (flags & NEEDS_SCOPE) == NEEDS_SCOPE;
357327Sjkh    }
3588419Sjkh
35916549Sjkh    @Override
36084745Ssobomax    public Block setFlags(final LexicalContext lc, final int flags) {
3618419Sjkh        if (this.flags == flags) {
36213946Sjdp            return this;
36313946Sjdp        }
36413946Sjdp        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion));
36584745Ssobomax    }
366154102Skrion
367327Sjkh    @Override
368154102Skrion    public Block clearFlag(final LexicalContext lc, final int flag) {
3698419Sjkh        return setFlags(lc, flags & ~flag);
3708419Sjkh    }
3712389Sadam
37284745Ssobomax    @Override
3732389Sadam    public Block setFlag(final LexicalContext lc, final int flag) {
37484745Ssobomax        return setFlags(lc, flags | flag);
3758419Sjkh    }
376179352Skeramida
377179352Skeramida    @Override
378179352Skeramida    public boolean getFlag(final int flag) {
379179352Skeramida        return (flags & flag) == flag;
380179352Skeramida    }
381179352Skeramida
382179352Skeramida    /**
383179352Skeramida     * Set the needs scope flag.
384179352Skeramida     * @param lc lexicalContext
385179352Skeramida     * @return new block if state changed, otherwise this
3868419Sjkh     */
3878419Sjkh    public Block setNeedsScope(final LexicalContext lc) {
3888419Sjkh        if (needsScope()) {
38984745Ssobomax            return this;
39095161Sobrien        }
39195161Sobrien
39271373Ssobomax        return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols, conversion));
39371373Ssobomax    }
394213718Sflz
395213718Sflz    /**
396213718Sflz     * Computationally determine the next slot for this block,
397213718Sflz     * indexed from 0. Use this as a relative base when computing
39871373Ssobomax     * frames
39971373Ssobomax     * @return next slot
40071373Ssobomax     */
40171373Ssobomax    public int nextSlot() {
40271373Ssobomax        int next = 0;
40371373Ssobomax        for (final Symbol symbol : getSymbols()) {
40471373Ssobomax            if (symbol.hasSlot()) {
4051520Salm                next += symbol.slotCount();
4068419Sjkh            }
4078419Sjkh        }
4088419Sjkh        return next;
4098419Sjkh    }
4108419Sjkh
41113946Sjdp    @Override
41213946Sjdp    public boolean isBreakableWithoutLabel() {
41313946Sjdp        return false;
4148419Sjkh    }
415327Sjkh
41671373Ssobomax    @Override
4178419Sjkh    public List<Label> getLabels() {
41813946Sjdp        return Collections.unmodifiableList(Arrays.asList(entryLabel, breakLabel));
41939068Sjkh    }
42039068Sjkh
42196392Salfred    @Override
42239068Sjkh    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
42339068Sjkh        return Acceptor.accept(this, visitor);
42439068Sjkh    }
42596392Salfred}
42639068Sjkh