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