/* Block Copyright (C) 2000-2002 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; see the file COPYING.LESSER. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package net.sf.jode.bytecode; import java.io.PrintWriter; /** *

Represents a single basic block. It contains a list of * instructions and the successor blocks.

* *

All jump instructions must be at the end of the block. These * jump instructions are opc_lookupswitch, * opc_ifxxx, opc_jsr, opc_ret, * opc_xreturn and opc_return. * An opc_goto is implicit if the basic block doesn't end * with a jump instructions, or if it ends with an conditional jump or * jsr.

* *

The jump instructions don't remember their destinations, instead * the Block does it. This are the successor block. There are * several cases:

* * * *

If any successor is null it represents end of * method, i.e. a return instruction. You can also use * null successors for conditional jumps and switch * instruction. You normally shouldn't use opc_return * instructions. They are only necessary, if you want to return with * a non-empty stack.

* * @author Jochen Hoenicke * @see net.sf.jode.bytecode.BasicBlocks * @see net.sf.jode.bytecode.Instruction */ public final class Block { /** * The opcodes of the instructions in this block. */ private Instruction[] instrs; /** * The blockNr of successor blocks */ private Block[] succs; /** * The catching blocks. Set by BasicBlocks. */ Handler[] catchers; /** * The blockNr of this block. Set by BasicBlocks. */ int blockNr; /** * The number of items this block takes from the stack with * respect to the stack items at the beginning of the block. */ int maxpop; /** * The maximum number of items the stack may grow. */ int maxpush; /** * The difference stack items after the block minus stack items * before block. */ int delta; /** * The stack height at the beginning of this block. * Only valid after the block was inserted in a BasicBlocks and * the updateMaxStackLocals() of BasicBlocks was called. */ int stackHeight; /** * Creates a new block uninitialized block. You mustn't really * use it (except as successor for other blocks) until you have * set the code. */ public Block() { } /** * Gets the list of instructions. The returned list should not be * modified, except that the instructions (but not their opcodes) * may be modified. */ public Instruction[] getInstructions() { return instrs; } /** * Gets the successor array. The last successor is the next basic * block that is jumped to via goto or the default part of a * switch. For conditional jumps and jsrs the second successor gives * the destination. */ public Block[] getSuccs() { return succs; } /** * Gets the exception handlers whose try region contains this * block. You can't set them since they are calculated * automatically. * @return the exception handlers. * @see BasicBlocks#setBlocks */ public Handler[] getHandlers() { return catchers; } /** * Gets the block number. The block numbers are consecutive number * from 0 to the number of blocks in a method. The equation *
 BasicBlocks.getBlock()[i].getBlockNr() == i 
* always holds (as long as you don't do something dirty, like adding * the same block to different BasicBlocks, or to the same but more * than once). * @return the block number. */ public int getBlockNr() { return blockNr; } private void initCode() { int size = instrs.length; maxpop = maxpush = 0; int depth = 0; int poppush[] = new int[2]; boolean needGoto = true; for (int i = 0; i < size; i++) { instrs[i].getStackPopPush(poppush); depth -= poppush[0]; if (maxpop < -depth) maxpop = -depth; depth += poppush[1]; if (maxpush < depth) maxpush = depth; int opcode = instrs[i].getOpcode(); switch (opcode) { case Opcodes.opc_goto: throw new IllegalArgumentException("goto in block"); case Opcodes.opc_lookupswitch: if (succs.length != instrs[i].getValues().length + 1) throw new IllegalArgumentException ("number of successors for switch doesn't match"); if (i != size - 1) throw new IllegalArgumentException ("switch in the middle!"); needGoto = false; break; case Opcodes.opc_ret: case Opcodes.opc_athrow: case Opcodes.opc_ireturn: case Opcodes.opc_lreturn: case Opcodes.opc_freturn: case Opcodes.opc_dreturn: case Opcodes.opc_areturn: case Opcodes.opc_return: if (succs.length != 0) throw new IllegalArgumentException ("throw or return with successor."); if (i != size - 1) throw new IllegalArgumentException ("return in the middle!"); needGoto = false; break; case Opcodes.opc_ifeq: case Opcodes.opc_ifne: case Opcodes.opc_iflt: case Opcodes.opc_ifge: case Opcodes.opc_ifgt: case Opcodes.opc_ifle: case Opcodes.opc_if_icmpeq: case Opcodes.opc_if_icmpne: case Opcodes.opc_if_icmplt: case Opcodes.opc_if_icmpge: case Opcodes.opc_if_icmpgt: case Opcodes.opc_if_icmple: case Opcodes.opc_if_acmpeq: case Opcodes.opc_if_acmpne: case Opcodes.opc_ifnull: case Opcodes.opc_ifnonnull: case Opcodes.opc_jsr: if (succs.length != 2) throw new IllegalArgumentException ("successors inappropriate for if/jsr"); if (succs[0] == null && opcode == Opcodes.opc_jsr) throw new IllegalArgumentException ("null successors inappropriate for jsr"); if (i != size - 1) throw new IllegalArgumentException ("if/jsr in the middle!"); needGoto = false; } } delta = depth; if (needGoto && succs.length != 1) throw new IllegalArgumentException("no single successor block"); } /** * Returns the stack height at the beginning of the block. This * is automatically calculated, when the block is inserted in a * basic block. */ public int getStackHeight () { return stackHeight; } public void getStackPopPush (int[] poppush) { poppush[0] = maxpop; poppush[1] = delta + maxpop; return; } /** * Set the code, i.e. instructions and successor blocks. * The instructions must be valid and match the successors. */ public void setCode(Instruction[] instrs, Block[] succs) { this.instrs = instrs; this.succs = succs; initCode(); } public void dumpCode(PrintWriter output) { output.println(" "+this+":"); for (int i = 0; i < instrs.length; i++) { Instruction instr = instrs[i]; if (i == instrs.length - 1 && succs != null) { int opcode = instr.getOpcode(); if (opcode == Opcodes.opc_lookupswitch) { // Special case for switch: output.println("\tswitch"); int[] values = instr.getValues(); for (int j = 0; j < values.length; j++) output.println("\t case"+values[j] +": goto "+succs[j]); output.println("\t default: goto"+ succs[values.length]); return; } else if (succs.length > 1) { output.println("\t"+instr.getDescription() +" "+succs[0]); break; } } output.println("\t"+instr.getDescription()); } if (succs != null && succs.length > 0) { if (succs[succs.length-1] == null) output.println("\treturn"); else output.println("\tgoto "+succs[succs.length-1]); } } public String toString() { return "Block_"+blockNr; } }