org.jboss.byteman.agent.adapter.cfg.BBlock.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.byteman.agent.adapter.cfg.BBlock.java

Source

/*
* JBoss, Home of Professional Open Source
* Copyright 2009-10 Red Hat and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* @authors Andrew Dinn
*/
package org.jboss.byteman.agent.adapter.cfg;

import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.jboss.byteman.agent.adapter.OpcodesHelper;

import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;

/**
 * A Basic Block represents a segment of bytecode in a control flow graph. Basic blocks divide up the
 * code at control flow branch points and hence there is no normal control flow internal to a block. Normal
 * control flow will only transfer control from the end of one basic block to the start of another
 * basic block or to the caller (via a return or throw).
 * <p/>
 * If the block overlaps a try/catch region then exception control flow may transfer control
 * from any instruction lying within the try/catch region to the the start of another basic
 * block which handles the instruction. So, exception control flow may exit a block at a location
 * preceding the block end but may only enter at block start.
 */

public class BBlock {
    private CFG cfg;

    /**
     * the sequence of instructions contained in this basic block
     */
    private InstructionSequence instructions;
    /*
     * A collection of the outgoing links from this basic block to other basic blocks.
     * Every block except the last one has at least one outgoing link because link 0
     * identifies the linkage of blocks in the original bytecode ordering. Any subsequent
     * links identify outgoing non-excepting control flow from the basic block. The number
     * and significance of these subsequent links is determined by the type of the last
     * instruction. As a consequence iterations over this collection should be 1-based, not 0-based.
     *
     * Blocks ending in RETURN or ATHROW are terminal so they have no extra links.
     *
     * Blocks ending in GOTO have on extra link, identifying the target of the GOTO instruction.
     *
     * Blocks ending in an IF_XXX instruction have two extra links, the target branch if the
     * instruction computes to false followed by the target branch if the instruction computes to
     * true.
     */
    private FanOut outGoing;

    /**
     * an index for the block allocated by the CFG starting from 0 in block order
     */
    private int blockIdx;

    /**
     * details of all try catch blocks which are active inside this block. n.b. this must omit try
     * catch blocks which are open when the block is created then subsequently closed at offset 0.
     */
    private List<TryCatchDetails> activeTryStarts;
    /**
     * details of all try catch blocks which start in this block
     */
    private List<TryCatchDetails> tryStarts;
    /**
     * a list of all try catch blocks which end in this block
     */
    private List<TryCatchDetails> tryEnds;
    /**
     * a list of all try catch blocks whose handlers start in this block
     */
    private List<TryCatchDetails> handlerStarts;

    /**
     * a stack (reverse order list) containing the locations of all monitor enter instructions contained in this
     * block excluding those which have been closed by a corresponding exit in this block
     */
    private LinkedList<CodeLocation> monitorEnters;
    /**
     * a list of the location of all monitor exit instructions contained in this block
     */
    private LinkedList<CodeLocation> monitorExits;

    /**
     * construct a new basic block
     * @param cfg the control flow graph it belongs to
     * @param start the label for the start of the block
     * @param blockIdx the index of the block which respects the order of the bytecode segments
     * contained in each block.
     */
    public BBlock(CFG cfg, Label start, int blockIdx) {
        this.cfg = cfg;
        this.instructions = new InstructionSequence();
        this.outGoing = new FanOut(start);
        this.blockIdx = blockIdx;
        this.activeTryStarts = null;
        this.tryStarts = new LinkedList<TryCatchDetails>();
        this.tryEnds = new LinkedList<TryCatchDetails>();
        this.handlerStarts = new LinkedList<TryCatchDetails>();
        this.monitorEnters = new LinkedList<CodeLocation>();
        this.monitorExits = new LinkedList<CodeLocation>();
    }

    /**
     * obtain the control flow graph to which this block belongs
     * @return
     */
    public CFG getCFG() {
        return cfg;
    }

    /**
     * get the primary label which idenitfies tis block. It will be located in the block at offset 0.
     * @return
     */
    public Label getLabel() {
        return outGoing.getFrom();
    }

    /**
     * retrieve the index of this block in the block sequence.
     * @return
     */
    public int getBlockIdx() {
        return blockIdx;
    }

    /**
     * add an instruction to the sequence in the block
     * @param instruction an Opcode
     * @return the index of the newly added instruction
     */
    public int append(int instruction) {
        int index = instructions.add(instruction);
        if (instruction == Opcodes.MONITORENTER) {
            // push onto front of list
            monitorEnters.push(new CodeLocation(this, index));
        } else if (instruction == Opcodes.MONITOREXIT) {
            CodeLocation exit = new CodeLocation(this, index);
            // we need to keep track of exits for when we can collate monitor section ends
            // with try catch blocks which overlapthis block so we don't drop them here
            // even if there is a matching enter in this block
            monitorExits.add(exit);
            // however if there is an enter in this block then it belongs to this exit
            // so we pop it to ensurewe only retain active local enters
            // we also record the pairing so we can work back from the matched exit to
            //its enter. pairing with non-local enters is done at block end carry forward
            if (!monitorEnters.isEmpty()) {
                CodeLocation enter = monitorEnters.pop();
                cfg.addMonitorPair(enter, exit);
            }
        }
        return index;
    }

    /**
     * add an instruction with one int operand to thhe sequence in the block
     * @param instruction an Opcode
     * @param operand an int operand or the code for a String operand lcoated in the cfg name table
     * @return the index of the newly added instruction
     */
    public int append(int instruction, int operand) {
        return instructions.add(instruction, operand);
    }

    /**
     * add an instruction with two int operands to the sequence in the block
     * @param instruction an Opcode
     * @param operand1 an int operand or the code for a String operand lcoated in the cfg name table
     * @param operand2 an int operand or the code for a String operand lcoated in the cfg name table
     * @return the index of the newly added instruction
     */
    public int append(int instruction, int operand1, int operand2) {
        return instructions.add(instruction, operand1, operand2);
    }

    /**
     * add an instruction with three int operands to thhe sequence in the block
     * @param instruction an Opcode
     * @param operand1 an int operand or the code for a String operand lcoated in the cfg name table
     * @param operand2 an int operand or the code for a String operand lcoated in the cfg name table
     * @param operand3 an int operand or the code for a String operand lcoated in the cfg name table
     * @return the index of the newly added instruction
     */
    public int append(int instruction, int operand1, int operand2, int operand3) {
        return instructions.add(instruction, operand1, operand2, operand3);
    }

    /**
     * add an instruction with an arbitrary number of int operands to thhe sequence in the block
        
     * @param instruction an Opcode
     * @param operands an array containing int operands or codes for String operands lcoated in the cfg name table
     * @return the index of the newly added instruction
     */
    public int append(int instruction, int[] operands) {
        return instructions.add(instruction, operands);
    }

    /**
     * record details of a try catch block which starts in this block
     * @param details
     */
    public void addTryStarts(List<TryCatchDetails> details) {
        tryStarts.addAll(details);
    }

    /**
     * record details of a try catch block which ends in this block
     * @param details
     */
    public void addTryEnds(List<TryCatchDetails> details) {
        tryEnds.addAll(details);
    }

    /**
     * record details of a try catch block handler which starts in this block
     * @param details
     */
    public void addHandlerStarts(List<TryCatchDetails> details) {
        handlerStarts.addAll(details);
    }

    /**
     * set the list of try starts which are active somewhere in this block.
     * @param active
     */
    public void setActiveTryStarts(List<TryCatchDetails> active) {
        activeTryStarts = active;
    }

    /**
     * retrieve details of all try catch blocks which end in this block
     * @return
     */
    public Iterator<TryCatchDetails> getTryEnds() {
        return tryEnds.iterator();
    }

    /**
     * retrieve details of all try catch block handlers whcih start in this block
     * @return
     */
    public Iterator<TryCatchDetails> getHandlerStarts() {
        return handlerStarts.iterator();
    }

    /**
     * retrieve details of all try catch blocks which are capable of generating an exception in this block
     * @return
     */
    public List<TryCatchDetails> getActiveTryStarts() {
        return activeTryStarts;
    }

    /**
     * retrieve a list of all monitor enter instruction locations occurring in this block
     * @return
     */
    public Iterator<CodeLocation> getMonitorEnters() {
        return monitorEnters.iterator();
    }

    /**
     * retrieve a list of all monitor exit instruction locations occurring in this block
     * @return
     */
    public Iterator<CodeLocation> getMonitorExits() {
        return monitorExits.iterator();
    }

    /**
     * retrieve a count of all monitor enter instruction locations occurring in this block
     * @return
     */
    public int getMonitorEnterCount() {
        return monitorEnters.size();
    }

    /**
     * retrieve a count of all monitor exit instruction locations occuring in this block
     * @return
     */
    public int getMonitorExitCount() {
        return monitorExits.size();
    }

    /**
     * return the number of instructions in the blocks instructuion sequence equivalent to the
     * index of the next instruction added to the block.
     * @return
     */
    public int getInstructionCount() {
        return instructions.size();
    }

    /**
     * retirn the instruction at a given index.
     * @param index
     * @return
     */
    public int getInstruction(int index) {
        return instructions.get(index);
    }

    /**
     * retrieve the integer operand or encoded name associated with a particular instruction
     * @param index the index  of the instruction in the block
     * @param argIndex the index of the argument in the sequence of arguments presented when the instruction
     * was inserted intot he block.
     * @return
     */
    public int getInstructionArg(int index, int argIndex) {
        return instructions.getArg(index, argIndex);
    }

    /**
     * install an outgoing normal control flow link
     * @param label
     */
    public void append(Label label) {
        outGoing.append(label);
    }

    /**
     * return the label of the next block in line in the block sequence in bytecode order.
     * @return
     */
    public Label next() {
        return outGoing.getTo(0);
    }

    /**
     * return the label of the first normal control flow link
     * @return
     */
    public Label firstOut() {
        return outGoing.getTo(1);
    }

    /**
     * return the label of the second normal control flow link
     * @return
     */
    public Label secondOut() {
        return outGoing.getTo(2);
    }

    // n.b. index for out link is 1-based rather than 0-based as entry 0 is the next link
    /**
     * return the label of the nth normal control flow link
     * @return
     */
    public Label nthOut(int n) {
        return outGoing.getTo(n);
    }

    /**
     * return a count of the normal control flow links from this block.
     * @return  the number of outgoingnormalcontrol flow links. n.b. iterations over
     * the links should count from 1 to nOuts() inclusive. this is the size of a 1-based
     * collection
     */
    public int nOuts() {
        return outGoing.getToCount() - 1;
    }

    /**
     * return a string represenattion of this block
     * @return
     */
    public String toString() {
        StringBuffer buf = new StringBuffer();
        printTo(buf);
        return buf.toString();
    }

    /**
     * write a string represenattion of this block to the buffer
     * @param buf the buffer to be written to
     * @return
     */
    void printTo(StringBuffer buf) {
        int blockIdx = this.getBlockIdx();
        buf.append(this.getLabel().getOffset());
        buf.append(": BB ");
        buf.append(blockIdx);
        buf.append("\n");
        FanOut containsFanOut = cfg.getContains(this);
        Iterator<Label> containsIter;
        Label containedLabel;
        int containedPosition;
        if (containsFanOut != null) {
            containsIter = containsFanOut.iterator();
            if (containsIter.hasNext()) {
                containedLabel = containsIter.next();
                containedPosition = cfg.getBlockInstructionIdx(containedLabel);
            } else {
                containedLabel = null;
                containedPosition = -1;
            }
        } else {
            containsIter = null;
            containedLabel = null;
            containedPosition = -1;
        }

        int instructionCount = this.getInstructionCount();
        for (int i = 0; i < instructionCount; i++) {
            // we will never enter this if containedPosition is -1 which safeguards us when containsIter
            // is null or containedLabel is null
            while (containedPosition == i) {
                buf.append(containedLabel.getOffset());
                buf.append(": ");
                buf.append(containedLabel);
                buf.append(" +");
                buf.append(containedPosition);
                if (cfg.tryCatchStart(containedLabel)) {
                    List<TryCatchDetails> detailsList = cfg.tryCatchStartDetails(containedLabel);
                    int detailsCount = detailsList.size();
                    for (int j = 0; j < detailsCount; j++) {
                        TryCatchDetails details = detailsList.get(j);
                        Label handlerLabel = details.getHandler();
                        CodeLocation handlerLocation = cfg.getLocation(handlerLabel);
                        buf.append(" try ");
                        buf.append(details.getType());
                        buf.append(" ");
                        if (handlerLocation != null) {
                            buf.append(handlerLabel.getOffset());
                        } else {
                            buf.append("??");
                        }
                        buf.append(" ");
                        buf.append(handlerLabel);
                    }
                }
                if (cfg.tryCatchEnd(containedLabel)) {
                    List<TryCatchDetails> detailsList = cfg.tryCatchEndDetails(containedLabel);
                    int detailsCount = detailsList.size();
                    for (int j = 0; j < detailsCount; j++) {
                        TryCatchDetails details = detailsList.get(j);
                        Label handlerLabel = details.getHandler();
                        CodeLocation handlerLocation = cfg.getLocation(handlerLabel);
                        buf.append(" catch ");
                        buf.append(details.getType());
                        buf.append(" ");
                        if (handlerLocation != null) {
                            buf.append(handlerLabel.getOffset());
                        } else {
                            buf.append("??");
                        }
                        buf.append(" ");
                        buf.append(handlerLabel);
                    }
                }
                if (cfg.tryCatchHandlerStart(containedLabel)) {
                    List<TryCatchDetails> detailsList = cfg.tryCatchHandlerStartDetails(containedLabel);
                    int detailsCount = detailsList.size();
                    for (int j = 0; j < detailsCount; j++) {
                        TryCatchDetails details = detailsList.get(j);
                        buf.append(" handle ");
                        buf.append(details.getType());
                        buf.append(" ");
                        buf.append(details.getStart().getOffset());
                        buf.append(" ");
                        buf.append(details.getEnd().getOffset());
                    }
                }
                if (cfg.triggerStart(containedLabel)) {
                    buf.append(" trigger start");
                    TriggerDetails details = cfg.triggerStartDetails(containedLabel);
                }
                if (cfg.triggerEnd(containedLabel)) {
                    buf.append(" trigger end");
                }
                buf.append("\n");
                List<CodeLocation> openEnters = cfg.getOpenMonitorEnters(containedLabel);
                if (openEnters != null) {
                    int openCount = openEnters.size();
                    if (openCount > 0) {
                        buf.append("open monitors: ");
                        for (int j = 0; j < openCount; j++) {
                            CodeLocation l = openEnters.get(j);
                            buf.append(" BB");
                            buf.append(l.getBlock().getBlockIdx());
                            buf.append(".");
                            buf.append(l.getInstructionIdx());
                        }
                        buf.append('\n');
                    }
                }
                containedLabel = (containsIter.hasNext() ? containsIter.next() : null);
                containedPosition = (containedLabel != null ? cfg.getBlockInstructionIdx(containedLabel) : -1);
            }
            // buf.append("   ");
            buf.append(blockIdx);
            buf.append(".");
            buf.append(i);
            buf.append(": ");
            int opcode = this.getInstruction(i);
            switch (OpcodesHelper.insnType(opcode)) {
            case OpcodesHelper.INSN_NONE: {
                // print the instruction name
                buf.append(OpcodesHelper.insnName(opcode));
                if (opcode == Opcodes.MONITOREXIT) {
                    CodeLocation exit = new CodeLocation(this, i);
                    CodeLocation enter = cfg.getPairedEnter(exit);
                    // print the corresponding open instruction
                    buf.append(" (enter: ");
                    buf.append(enter);
                    buf.append(")");
                }
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_INT: {
                // just print the instruction name and one integer argument
                int intValue = this.getInstructionArg(i, 0);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(intValue);
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_LDC: {
                // print the instruction and one constant argument
                int nameIdx = this.getInstructionArg(i, 0);
                String name = cfg.getName(nameIdx);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(name);
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_VAR: {
                // print the instruction and the var idx
                int varIdx = this.getInstructionArg(i, 0);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(varIdx);
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_IINC: {
                // print the instruction and the var idx
                int increment = this.getInstructionArg(i, 0);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(increment);
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_JUMP: {
                // note that we may not have generated the code for the jump target yet
                Label targetLabel = this.firstOut();
                CodeLocation targetLocation = cfg.getLocation(targetLabel);
                int targetPos = (targetLocation != null ? targetLabel.getOffset() : -1);
                switch (opcode) {
                case Opcodes.IFEQ:
                    buf.append("IFEQ ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IFNE:
                    buf.append("IFNE ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IFLT:
                    buf.append("IFLT ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IFGE:
                    buf.append("IFGE ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IFGT:
                    buf.append("IFGT ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IFLE:
                    buf.append("IFLE ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ICMPEQ:
                    buf.append("IF_ICMPEQ ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ICMPNE:
                    buf.append("IF_ICMPNE ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ICMPLT:
                    buf.append("IF_ICMPLT ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ICMPGE:
                    buf.append("IF_ICMPGE ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ICMPGT:
                    buf.append("IF_ICMPGT ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ICMPLE:
                    buf.append("IF_ICMPLE ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ACMPEQ:
                    buf.append("IF_ACMPEQ ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IF_ACMPNE:
                    buf.append("IF_ACMPNE ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.GOTO:
                    buf.append("GOTO ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.JSR:
                    buf.append("JSR ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IFNULL:
                    buf.append("IFNULL ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                case Opcodes.IFNONNULL:
                    buf.append("IFNONNULL ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                    buf.append("\n");
                    break;
                }
            }
                break;
            case OpcodesHelper.INSN_TSWITCH: {
                Label targetLabel;
                CodeLocation targetLocation;
                int targetPos;
                // print the instruction followed by the jump table discriminant min and max and then
                // the jump labels
                int min = this.getInstructionArg(i, 0);
                int max = this.getInstructionArg(i, 1);
                int count = (max + 1 - min);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(min);
                buf.append(" ");
                buf.append(max);
                buf.append("\n");
                for (int j = 1; j <= count; j++) {
                    // note that we may not have generated the code for the jump target yet
                    targetLabel = this.nthOut(j);
                    targetLocation = cfg.getLocation(targetLabel);
                    targetPos = (targetLocation != null ? targetLabel.getOffset() : -1);
                    buf.append("    ");
                    buf.append(min + j);
                    buf.append(" : ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                }
                targetLabel = this.firstOut();
                targetLocation = cfg.getLocation(targetLabel);
                targetPos = (targetLocation != null ? targetLabel.getOffset() : -1);
                buf.append("    dflt : ");
                if (targetPos >= 0) {
                    buf.append(targetPos);
                    buf.append(" BB ");
                    buf.append(targetLocation.getBlock().getBlockIdx());
                    buf.append(" ");
                    buf.append(targetLabel);
                } else {
                    buf.append("??  ");
                    buf.append(targetLabel);
                }
            }
                break;
            case OpcodesHelper.INSN_LOOKUP: {
                Label targetLabel;
                CodeLocation targetLocation;
                int targetPos;
                // print the instruction followed by each jump table discriminant and label
                int count = this.getInstructionArg(i, 0);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append("\n");
                for (int j = 1; j <= count; j++) {
                    // note that we may not have generated the code for the jump target yet
                    targetLabel = this.nthOut(j);
                    targetLocation = cfg.getLocation(targetLabel);
                    targetPos = (targetLocation != null ? targetLabel.getOffset() : -1);
                    buf.append("    ");
                    buf.append(this.getInstructionArg(i, j));
                    buf.append(" : ");
                    if (targetPos >= 0) {
                        buf.append(targetPos);
                        buf.append(" BB ");
                        buf.append(targetLocation.getBlock().getBlockIdx());
                        buf.append(" ");
                        buf.append(targetLabel);
                    } else {
                        buf.append("??  ");
                        buf.append(targetLabel);
                    }
                }
                targetLabel = this.firstOut();
                targetLocation = cfg.getLocation(targetLabel);
                targetPos = (targetLocation != null ? targetLabel.getOffset() : -1);
                buf.append("    dflt : ");
                if (targetPos >= 0) {
                    buf.append(targetPos);
                    buf.append(" BB ");
                    buf.append(targetLocation.getBlock().getBlockIdx());
                    buf.append(" ");
                    buf.append(targetLabel);
                } else {
                    buf.append("??  ");
                    buf.append(targetLabel);
                }
            }
                break;
            case OpcodesHelper.INSN_FIELD:
            case OpcodesHelper.INSN_METHOD: {
                // print the instruction with the owner, name and descriptor
                int idx1 = this.getInstructionArg(i, 0);
                int idx2 = this.getInstructionArg(i, 1);
                int idx3 = this.getInstructionArg(i, 2);
                String owner = cfg.getName(idx1);
                String name = cfg.getName(idx2);
                String desc = cfg.getName(idx3);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(owner);
                buf.append(" ");
                buf.append(name);
                buf.append(" ");
                buf.append(desc);
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_TYPE: {
                // print the instruction with the type name
                int idx = this.getInstructionArg(i, 0);
                String name = cfg.getName(idx);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(name);
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_MULTIANEWARRAY: {
                // print the instruction with the typename and the dimension count
                int idx = this.getInstructionArg(i, 0);
                int dims = this.getInstructionArg(i, 1);
                String name = cfg.getName(idx);
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append(" ");
                buf.append(name);
                buf.append(" ");
                buf.append(dims);
                buf.append("\n");
            }
                break;
            case OpcodesHelper.INSN_UNUSED: {
                // print the instruction
                buf.append(OpcodesHelper.insnName(opcode));
                buf.append("!!!\n");
            }
                break;
            }
        }
        // print the active starts for this block
        if (activeTryStarts != null) {
            Iterator<TryCatchDetails> activeStartsIter = activeTryStarts.iterator();
            while (activeStartsIter.hasNext()) {
                TryCatchDetails details = activeStartsIter.next();
                Label label = details.getStart();
                BBlock block = cfg.getBlock(label);
                buf.append("try: ");
                if (block != null) {
                    buf.append(label.getOffset());
                    buf.append(" ");
                    buf.append(block.getBlockIdx());
                    buf.append(".");
                    buf.append(cfg.getBlockInstructionIdx(label));
                } else {
                    buf.append(label);
                }
                buf.append(" catch: ");
                label = details.getEnd();
                block = cfg.getBlock(label);
                if (block != null) {
                    buf.append(label.getOffset());
                    buf.append(" ");
                    buf.append(block.getBlockIdx());
                    buf.append(".");
                    buf.append(cfg.getBlockInstructionIdx(label));
                } else {
                    buf.append(label);
                }
                buf.append(" handle: ");
                label = details.getHandler();
                block = cfg.getBlock(label);
                if (block != null) {
                    buf.append(label.getOffset());
                    buf.append(" ");
                    buf.append(block.getBlockIdx());
                    buf.append(".");
                    buf.append(cfg.getBlockInstructionIdx(label));
                } else {
                    buf.append(label);
                }
                buf.append(" ");
                buf.append(details.getType());
                buf.append("\n");
            }
        }
    }
}