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