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