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