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