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