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? 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 }