1 /* 2 * Copyright (c) 2010, 2017, 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? 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 * Is this the function body block? May not be the first, if parameter list contains expressions. 89 */ 90 public static final int IS_BODY = 1 << 5; 91 92 /** 93 * Is this the parameter initialization block? If present, must be the first block, immediately wrapping the function body block. 94 */ 95 public static final int IS_PARAMETER_BLOCK = 1 << 6; 96 97 /** 98 * Marks the variable declaration block for case clauses of a switch statement. 99 */ 100 public static final int IS_SWITCH_BLOCK = 1 << 7; 101 102 /** 103 * Is this block tagged as breakable based on its contents 104 * (block having labelled break statement) 105 */ 106 public static final int IS_BREAKABLE = 1 << 8; 107 108 /** 109 * Constructor 110 * 111 * @param token The first token of the block 112 * @param finish The index of the last character 113 * @param flags The flags of the block 114 * @param statements All statements in the block 115 */ 116 public Block(final long token, final int finish, final int flags, final Statement... statements) { 117 super(token, finish); 118 119 this.statements = Arrays.asList(statements); 120 this.symbols = new LinkedHashMap<>(); 121 this.entryLabel = new Label("block_entry"); 122 this.breakLabel = new Label("block_break"); 123 final int len = statements.length; 124 final int terminalFlags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0; 125 this.flags = terminalFlags | flags; 126 this.conversion = null; 127 } 128 129 /** 130 * Constructs a new block 131 * 132 * @param token The first token of the block 133 * @param finish The index of the last character 134 * @param statements All statements in the block 135 */ 136 public Block(final long token, final int finish, final Statement...statements){ 137 this(token, finish, IS_SYNTHETIC, statements); 138 } 139 140 /** 141 * Constructs a new block 142 * 143 * @param token The first token of the block 144 * @param finish The index of the last character 145 * @param statements All statements in the block 146 */ 147 public Block(final long token, final int finish, final List<Statement> statements){ 148 this(token, finish, IS_SYNTHETIC, statements); 149 } 150 151 /** 152 * Constructor 153 * 154 * @param token The first token of the block 155 * @param finish The index of the last character 156 * @param flags The flags of the block 157 * @param statements All statements in the block 158 */ 159 public Block(final long token, final int finish, final int flags, final List<Statement> statements) { 160 this(token, finish, flags, statements.toArray(new Statement[0])); 161 } 162 163 private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, final LocalVariableConversion conversion) { 164 super(block, finish); 165 this.statements = statements; 166 this.flags = flags; 167 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 168 this.entryLabel = new Label(block.entryLabel); 169 this.breakLabel = new Label(block.breakLabel); 170 this.conversion = conversion; 171 } 172 173 /** 174 * Is this block the outermost eager global scope - i.e. the primordial program? 175 * Used for global anchor point for scope depth computation for recompilation code 176 * @return true if outermost eager global scope 177 */ 178 public boolean isGlobalScope() { 179 return getFlag(IS_GLOBAL_SCOPE); 180 } 181 182 /** 183 * Returns true if this block defines any symbols. 184 * @return true if this block defines any symbols. 185 */ 186 public boolean hasSymbols() { 187 return !symbols.isEmpty(); 188 } 189 190 /** 191 * Replaces symbols defined in this block with different symbols. Used to ensure symbol tables are 192 * immutable upon construction and have copy-on-write semantics. Note that this method only replaces the 193 * symbols in the symbol table, it does not act on any contained AST nodes that might reference the symbols. 194 * Those should be updated separately as this method is meant to be used as part of such an update pass. 195 * @param lc the current lexical context 196 * @param replacements the map of symbol replacements 197 * @return a new block with replaced symbols, or this block if none of the replacements modified the symbol 198 * table. 199 */ 200 public Block replaceSymbols(final LexicalContext lc, final Map<Symbol, Symbol> replacements) { 201 if (symbols.isEmpty()) { 202 return this; 203 } 204 final LinkedHashMap<String, Symbol> newSymbols = new LinkedHashMap<>(symbols); 205 for (final Map.Entry<String, Symbol> entry: newSymbols.entrySet()) { 206 final Symbol newSymbol = replacements.get(entry.getValue()); 207 assert newSymbol != null : "Missing replacement for " + entry.getKey(); 208 entry.setValue(newSymbol); 209 } 210 return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, newSymbols, conversion)); 211 } 212 213 /** 214 * Returns a copy of this block with a shallow copy of the symbol table. 215 * @return a copy of this block with a shallow copy of the symbol table. 216 */ 217 public Block copyWithNewSymbols() { 218 return new Block(this, finish, statements, flags, new LinkedHashMap<>(symbols), conversion); 219 } 220 221 @Override 222 public Node ensureUniqueLabels(final LexicalContext lc) { 223 return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion)); 224 } 225 226 /** 227 * Assist in IR navigation. 228 * 229 * @param visitor IR navigating visitor. 230 * @return new or same node 231 */ 232 @Override 233 public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { 234 if (visitor.enterBlock(this)) { 235 return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, statements))); 236 } 237 238 return this; 239 } 240 241 /** 242 * Get a copy of the list for all the symbols defined in this block 243 * @return symbol iterator 244 */ 245 public List<Symbol> getSymbols() { 246 return symbols.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(new ArrayList<>(symbols.values())); 247 } 248 249 /** 250 * Retrieves an existing symbol defined in the current block. 251 * @param name the name of the symbol 252 * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't 253 * define a symbol with this name.T 254 */ 255 public Symbol getExistingSymbol(final String name) { 256 return symbols.get(name); 257 } 258 259 /** 260 * Test if this block represents a <code>catch</code> block in a <code>try</code> statement. 261 * This is used by the Splitter as catch blocks are not be subject to splitting. 262 * 263 * @return true if this block represents a catch block in a try statement. 264 */ 265 public boolean isCatchBlock() { 266 return statements.size() == 1 && statements.get(0) instanceof CatchNode; 267 } 268 269 @Override 270 public void toString(final StringBuilder sb, final boolean printType) { 271 for (final Node statement : statements) { 272 statement.toString(sb, printType); 273 sb.append(';'); 274 } 275 } 276 277 /** 278 * Print symbols in block in alphabetical order, sorted on name 279 * Used for debugging, see the --print-symbols flag 280 * 281 * @param stream print writer to output symbols to 282 * 283 * @return true if symbols were found 284 */ 285 public boolean printSymbols(final PrintWriter stream) { 286 final List<Symbol> values = new ArrayList<>(symbols.values()); 287 288 Collections.sort(values, new Comparator<Symbol>() { 289 @Override 290 public int compare(final Symbol s0, final Symbol s1) { 291 return s0.getName().compareTo(s1.getName()); 292 } 293 }); 294 295 for (final Symbol symbol : values) { 296 symbol.print(stream); 297 } 298 299 return !values.isEmpty(); 300 } 301 302 /** 303 * Tag block as terminal or non terminal 304 * @param lc lexical context 305 * @param isTerminal is block terminal 306 * @return same block, or new if flag changed 307 */ 308 public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) { 309 return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL); 310 } 311 312 @Override 313 public int getFlags() { 314 return flags; 315 } 316 317 /** 318 * Is this a terminal block, i.e. does it end control flow like ending with a throw or return? 319 * 320 * @return true if this node statement is terminal 321 */ 322 @Override 323 public boolean isTerminal() { 324 return getFlag(IS_TERMINAL) && !getFlag(IS_BREAKABLE); 325 } 326 327 /** 328 * Get the entry label for this block 329 * @return the entry label 330 */ 331 public Label getEntryLabel() { 332 return entryLabel; 333 } 334 335 @Override 336 public Label getBreakLabel() { 337 return breakLabel; 338 } 339 340 @Override 341 public Block setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) { 342 if(this.conversion == conversion) { 343 return this; 344 } 345 return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion)); 346 } 347 348 @Override 349 public LocalVariableConversion getLocalVariableConversion() { 350 return conversion; 351 } 352 353 /** 354 * Get the list of statements in this block 355 * 356 * @return a list of statements 357 */ 358 public List<Statement> getStatements() { 359 return Collections.unmodifiableList(statements); 360 } 361 362 /** 363 * Returns the number of statements in the block. 364 * @return the number of statements in the block. 365 */ 366 public int getStatementCount() { 367 return statements.size(); 368 } 369 370 /** 371 * Returns the line number of the first statement in the block. 372 * @return the line number of the first statement in the block, or -1 if the block has no statements. 373 */ 374 public int getFirstStatementLineNumber() { 375 if(statements == null || statements.isEmpty()) { 376 return -1; 377 } 378 return statements.get(0).getLineNumber(); 379 } 380 381 /** 382 * Returns the last statement in the block. 383 * @return the last statement in the block, or null if the block has no statements. 384 */ 385 public Statement getLastStatement() { 386 return statements.isEmpty() ? null : statements.get(statements.size() - 1); 387 } 388 389 /** 390 * Reset the statement list for this block 391 * 392 * @param lc lexical context 393 * @param statements new statement list 394 * @return new block if statements changed, identity of statements == block.statements 395 */ 396 public Block setStatements(final LexicalContext lc, final List<Statement> statements) { 397 if (this.statements == statements) { 398 return this; 399 } 400 int lastFinish = 0; 401 if (!statements.isEmpty()) { 402 lastFinish = statements.get(statements.size() - 1).getFinish(); 403 } 404 return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags, symbols, conversion)); 405 } 406 407 /** 408 * Add or overwrite an existing symbol in the block 409 * 410 * @param symbol symbol 411 */ 412 public void putSymbol(final Symbol symbol) { 413 symbols.put(symbol.getName(), symbol); 414 } 415 416 /** 417 * Check whether scope is necessary for this Block 418 * 419 * @return true if this function needs a scope 420 */ 421 public boolean needsScope() { 422 return (flags & NEEDS_SCOPE) == NEEDS_SCOPE; 423 } 424 425 /** 426 * Check whether this block is synthetic or not. 427 * 428 * @return true if this is a synthetic block 429 */ 430 public boolean isSynthetic() { 431 return (flags & IS_SYNTHETIC) == IS_SYNTHETIC; 432 } 433 434 @Override 435 public Block setFlags(final LexicalContext lc, final int flags) { 436 if (this.flags == flags) { 437 return this; 438 } 439 return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags, symbols, conversion)); 440 } 441 442 @Override 443 public Block clearFlag(final LexicalContext lc, final int flag) { 444 return setFlags(lc, flags & ~flag); 445 } 446 447 @Override 448 public Block setFlag(final LexicalContext lc, final int flag) { 449 return setFlags(lc, flags | flag); 450 } 451 452 @Override 453 public boolean getFlag(final int flag) { 454 return (flags & flag) == flag; 455 } 456 457 /** 458 * Set the needs scope flag. 459 * @param lc lexicalContext 460 * @return new block if state changed, otherwise this 461 */ 462 public Block setNeedsScope(final LexicalContext lc) { 463 if (needsScope()) { 464 return this; 465 } 466 467 return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE, symbols, conversion)); 468 } 469 470 /** 471 * Computationally determine the next slot for this block, 472 * indexed from 0. Use this as a relative base when computing 473 * frames 474 * @return next slot 475 */ 476 public int nextSlot() { 477 int next = 0; 478 for (final Symbol symbol : getSymbols()) { 479 if (symbol.hasSlot()) { 480 next += symbol.slotCount(); 481 } 482 } 483 return next; 484 } 485 486 /** 487 * Determine whether this block needs to provide its scope object creator for use by its child nodes. 488 * This is only necessary for synthetic parent blocks of for-in loops with lexical declarations. 489 * 490 * @see ForNode#needsScopeCreator() 491 * @return true if child nodes need access to this block's scope creator 492 */ 493 public boolean providesScopeCreator() { 494 return needsScope() && isSynthetic() 495 && (getLastStatement() instanceof ForNode) 496 && ((ForNode) getLastStatement()).needsScopeCreator(); 497 } 498 499 @Override 500 public boolean isBreakableWithoutLabel() { 501 return false; 502 } 503 504 @Override 505 public List<Label> getLabels() { 506 return Collections.unmodifiableList(Arrays.asList(entryLabel, breakLabel)); 507 } 508 509 @Override 510 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 511 return Acceptor.accept(this, visitor); 512 } 513 514 /** 515 * Checks if this is a function body. 516 * 517 * @return true if the function body flag is set 518 */ 519 public boolean isFunctionBody() { 520 return getFlag(IS_BODY); 521 } 522 523 /** 524 * Checks if this is a parameter block. 525 * 526 * @return true if the parameter block flag is set 527 */ 528 public boolean isParameterBlock() { 529 return getFlag(IS_PARAMETER_BLOCK); 530 } 531 532 /** 533 * Checks whether this is a switch block. 534 * 535 * @return true if this is a switch block 536 */ 537 public boolean isSwitchBlock() { 538 return getFlag(IS_SWITCH_BLOCK); 539 } 540 }