com.sun.tdk.jcov.instrument.BlockCodeMethodAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.tdk.jcov.instrument.BlockCodeMethodAdapter.java

Source

/*
 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.tdk.jcov.instrument;

import com.sun.tdk.jcov.instrument.CharacterRangeTableAttribute.CRTEntry;
import java.util.*;

import static org.objectweb.asm.Opcodes.*;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Attribute;

import org.objectweb.asm.tree.*;

/**
 * @author Dmitry Fazunenko
 * @author Alexey Fedorchenko
 */
class BlockCodeMethodAdapter extends OffsetRecordingMethodAdapter {

    private final MethodVisitor nextVisitor;
    private final List<DataExit> exits;
    private final Map<AbstractInsnNode, SimpleBasicBlock> insnToBB;
    private final InstrumentationParams params;

    public BlockCodeMethodAdapter(final MethodVisitor mv, final DataMethodWithBlocks method,
            final InstrumentationParams params) {
        super(new MethodNode(method.getAccess(), method.getName(), method.getVmSignature(), method.getSignature(),
                method.getExceptions()), method);
        this.nextVisitor = mv;
        this.insnToBB = new IdentityHashMap<AbstractInsnNode, SimpleBasicBlock>();
        this.exits = new ArrayList<DataExit>();
        this.params = params;
    }

    private SimpleBasicBlock getBB(AbstractInsnNode insn, int startBCI) {
        SimpleBasicBlock bb = insnToBB.get(insn);
        if (bb == null) {
            bb = new SimpleBasicBlock(method.rootId, startBCI);
            insnToBB.put(insn, bb);
        } else if (startBCI >= 0) {
            bb.setStartBCI(startBCI);
        }
        return bb;
    }

    private SimpleBasicBlock getBB(AbstractInsnNode insn) {
        return getBB(insn, -1);
    }

    private AbstractInsnNode peek(ListIterator iit) {
        // Do a next() to get the next instruction..
        // Then immediately do a previous to restore our position.
        if (iit.hasNext()) {
            AbstractInsnNode insn = (AbstractInsnNode) iit.next();
            iit.previous();
            return insn;
        }
        return null;
    }

    private SimpleBasicBlock[] completeComputationOfCodeLabelNodes() {
        MethodNode methodNode = (MethodNode) mv;
        InsnList instructions = methodNode.instructions;
        int[] allToReal = new int[instructions.size()];
        int allIdx = 0;
        int insnIdx = 0;
        ListIterator iit = instructions.iterator();

        // Create the method entry block and basic block
        AbstractInsnNode insnFirst = peek(iit);
        SimpleBasicBlock bbFirst = getBB(insnFirst, 0);
        // DataBlock blockFirst = new DataBlockMethEnter();
        // bbFirst.add(blockFirst);

        while (iit.hasNext()) {
            AbstractInsnNode insn = (AbstractInsnNode) iit.next();
            allToReal[allIdx++] = insnIdx;
            int bci = bcis[insnIdx];
            int opcode = insn.getOpcode();
            if (opcode < 0) {
                // a pseudo-instruction
                if (insn.getType() == AbstractInsnNode.LINE) {
                    LineNumberNode lineNode = (LineNumberNode) insn;
                    method().addLineEntry(bci, lineNode.line);
                }
            } else {
                // a real instruction
                ++insnIdx; // advance the real instruction index

                //System.out.println( "#" + (insnIdx - 1) +
                //        " bci: " + bci + "  " +
                //        instr.toString().replace("org.objectweb.asm.tree.", "").replace("@", " @ ") +
                //        " [" + (opcode>=0? Constants.opcNames[opcode] : " pseudo") +"]");
                switch (opcode) {
                case IFEQ:
                case IFNE:
                case IFLT:
                case IFGE:
                case IFGT:
                case IFLE:
                case IF_ICMPEQ:
                case IF_ICMPNE:
                case IF_ICMPLT:
                case IF_ICMPGE:
                case IF_ICMPGT:
                case IF_ICMPLE:
                case IF_ACMPEQ:
                case IF_ACMPNE:
                case IFNULL:
                case IFNONNULL:
                case JSR: {
                    JumpInsnNode jumpInsn = (JumpInsnNode) insn;
                    LabelNode insnTrue = jumpInsn.label;
                    int bciFalse = bcis[insnIdx]; // fall-through

                    DataBranchCond branch = new DataBranchCond(method.rootId, bci, bciFalse - 1);
                    /* DataBlockTarget blockTrue = new DataBlockTargetCond(true);
                     DataBlockTarget blockFalse = new DataBlockTargetCond(false);
                     branch.addTarget(blockTrue);
                     branch.addTarget(blockFalse);
                     */
                    AbstractInsnNode insnFalse = peek(iit);
                    assert (insnFalse != null); // must be fall-through code
                    SimpleBasicBlock bbTrue = getBB(insnTrue);
                    SimpleBasicBlock bbFalse = getBB(insnFalse, bciFalse);

                    exits.add(branch);

                    // assign a new label for branch counting
                    // LabelNode nlab = new LabelNode();
                    // jumpInsn.label = nlab;  // branch to new label
                    //  bbTrue.add(blockTrue, nlab);

                    //bbFalse.add(blockFalse);
                    break;
                }

                case TABLESWITCH: {
                    TableSwitchInsnNode switchInsn = (TableSwitchInsnNode) insn;

                    // Create a block and basic-block the "default:" case
                    LabelNode insnDflt = switchInsn.dflt;
                    SimpleBasicBlock bbDefault = getBB(insnDflt);
                    DataBlockTargetDefault blockDefault = new DataBlockTargetDefault(bbDefault.rootId());

                    // assign a new default label for branch counting
                    //LabelNode nlab = new LabelNode();
                    //switchInsn.dflt = nlab;  // branch to new label
                    //bbDefault.add(blockDefault, nlab);

                    // Create the branch information
                    int bciEnd = bcis[insnIdx] - 1; // end of the switch
                    DataBranchSwitch branch = new DataBranchSwitch(method.rootId, bci, bciEnd, blockDefault);
                    // branch.addTarget(blockDefault);
                    exits.add(branch);

                    // Process the other cases
                    ListIterator lit = switchInsn.labels.listIterator();
                    int key = switchInsn.min;
                    while (lit.hasNext()) {
                        // Create a block and basic-block the case
                        LabelNode labCase = (LabelNode) lit.next();
                        SimpleBasicBlock bbCase = getBB(labCase);
                        //DataBlockTargetCase blockCase = new DataBlockTargetCase(key++);
                        //branch.addTarget(blockCase);

                        // assign a new label to the case for branch counting
                        //nlab = new LabelNode();
                        //  lit.set(nlab);
                        // bbCase.add(blockCase, nlab);
                    }
                    break;
                }

                case LOOKUPSWITCH: {
                    LookupSwitchInsnNode switchInsn = (LookupSwitchInsnNode) insn;

                    // Create a block and basic-block the "default:" case
                    LabelNode insnDflt = switchInsn.dflt;
                    SimpleBasicBlock bbDefault = getBB(insnDflt);
                    DataBlockTargetDefault blockDefault = new DataBlockTargetDefault(bbDefault.rootId());

                    // assign a new default label for branch counting
                    // LabelNode nlab = new LabelNode();
                    // switchInsn.dflt = nlab;  // branch to new label
                    // bbDefault.add(blockDefault, nlab);

                    // Create the branch information
                    int bciEnd = bcis[insnIdx] - 1; // end of the switch
                    DataBranchSwitch branch = new DataBranchSwitch(method.rootId, bci, bciEnd, blockDefault);
                    // branch.addTarget(blockDefault);
                    exits.add(branch);

                    // Process the other cases
                    ListIterator kit = switchInsn.keys.listIterator();
                    ListIterator lit = switchInsn.labels.listIterator();
                    while (lit.hasNext()) {
                        // Create a block and basic-block the case
                        LabelNode labCase = (LabelNode) lit.next();
                        SimpleBasicBlock bbCase = getBB(labCase);
                        Integer key = (Integer) kit.next();
                        //                            DataBlockTargetCase blockCase = new DataBlockTargetCase(key.intValue());
                        // branch.addTarget(blockCase);

                        // assign a new label to the case for branch counting
                        //nlab = new LabelNode();
                        //lit.set(nlab);
                        //bbCase.add(blockCase, nlab);
                    }
                    break;
                }

                case GOTO: {
                    JumpInsnNode jumpInsn = (JumpInsnNode) insn;

                    // Create origin info, a branch
                    int bciEnd = bcis[insnIdx] - 1;
                    DataBranchGoto branch = new DataBranchGoto(method.rootId, bci, bciEnd);
                    exits.add(branch);

                    // Create destination info, a block target
                    LabelNode insnTarget = jumpInsn.label;
                    SimpleBasicBlock bbTarget = getBB(insnTarget);
                    //DataBlockTarget blockTarget = new DataBlockTargetGoto();
                    //branch.addTarget(blockTarget);

                    // assign a new label for branch counting
                    //LabelNode nlab = new LabelNode();
                    //jumpInsn.label = nlab;  // branch to new label
                    //bbTarget.add(blockTarget, nlab);
                    break;
                }
                case ATHROW:
                case RET:
                case IRETURN:
                case LRETURN:
                case FRETURN:
                case DRETURN:
                case ARETURN:
                case RETURN: {
                    int bciNext = bcis[insnIdx];
                    DataExit exit = new DataExitSimple(method.rootId, bci, bciNext - 1, insn.getOpcode());
                    exits.add(exit);

                    AbstractInsnNode insnNext = peek(iit);
                    if (insnNext != null) {
                        // If there is code after this, it has to be the start of a
                        // new basic block
                        getBB(insnNext, bciNext);
                    }
                    break;
                }
                default:
                    break;
                }
                // try add src block
            }
        }

        // Now go through the try-catch blocks
        LabelNode previousHandler = null;
        for (Iterator tbit = methodNode.tryCatchBlocks.iterator(); tbit.hasNext();) {
            TryCatchBlockNode tcbn = (TryCatchBlockNode) tbit.next();
            LabelNode insnHandler = tcbn.handler;
            if (insnHandler != previousHandler) {
                previousHandler = insnHandler;

                // Create destination info, a block target
                SimpleBasicBlock bbCatch = getBB(insnHandler);

            }
        }

        if (method().getCharacterRangeTable() != null) {
            boolean newBlock = true;
            int skip = 0;
            iit = instructions.iterator();
            while (iit.hasNext()) {
                AbstractInsnNode insn = (AbstractInsnNode) iit.next();
                int index = instructions.indexOf(insn);
                int bci = bcis[allToReal[index]];
                if (bci == skip) {
                    continue;
                }

                if (insnToBB.get(insn) != null) {
                    skip = bcis[allToReal[instructions.indexOf(insn)]];
                }

                if (insn.getOpcode() < 0) {
                    continue;
                }

                for (CharacterRangeTableAttribute.CRTEntry entry : method().getCharacterRangeTable().getEntries()) {
                    if (entry.startBCI() == bci) {

                        if ((entry.flags & CRTEntry.CRT_STATEMENT) != 0 /*& newBlock*/) {
                            newBlock = false;
                            if (insnToBB.get(insn) == null) {
                                //System.out.println("Should add block at: " + bci + " in " + method().name +
                                //       " for " + Constants.opcNames[insn.getOpcode()]);
                                getBB(insn);
                                break;
                            }
                        }
                    } else {
                        if (entry.endBCI() == index && (entry.flags & CRTEntry.CRT_FLOW_TARGET) != 0) {
                            newBlock = true;
                        }
                    }

                }
            }

        }

        // Compute the startBCI for any basic blocks that don't have it'
        SimpleBasicBlock[] basicBlocks = new SimpleBasicBlock[insnToBB.size()];
        int i = 0;
        for (Map.Entry<AbstractInsnNode, SimpleBasicBlock> entry : insnToBB.entrySet()) {
            SimpleBasicBlock bb = entry.getValue();
            if (bb.startBCI() < 0) {
                AbstractInsnNode insn = entry.getKey();
                int index = instructions.indexOf(insn);
                int bci = bcis[allToReal[index]];
                bb.setStartBCI(bci);
            }
            basicBlocks[i++] = bb;
        }
        Arrays.sort(basicBlocks);

        return basicBlocks;
    }

    /**
     * Compute end BCIs for basic blocks, then set this info into detail blocks.
     * Assumes the basic blocks are sorted
     */
    private void computeEndBCIsAndFoldInExits(SimpleBasicBlock[] basicBlocks) {
        int ei = 0; // exit index
        SimpleBasicBlock prev = basicBlocks[0];
        for (int bi = 1; bi <= basicBlocks.length; ++bi) {
            SimpleBasicBlock curr = null;
            int start;
            if (bi == basicBlocks.length) {
                start = method().getBytecodeLength();
            } else {
                curr = basicBlocks[bi];
                start = curr.startBCI();
            }

            int prevStart = prev.startBCI();
            // Set the previous block to end just before the current starts
            int prevEnd = start - 1;
            prev.setEndBCI(prevEnd);

            // Now that we know the endBCI, we can determine if
            // any exits reside in this range
            DataExit exit = null;
            int exitStart;
            if (ei < exits.size()) {
                exit = exits.get(ei);
                exitStart = exit.startBCI();
            } else {
                exitStart = -1; // always go to the fall-into code
            }
            if (exitStart >= prevStart && exitStart <= prevEnd) {
                // The exit is in the prev block attach it
                prev.setExit(exit);
                // System.out.println("found " + ei + " BB: " + prev + " exit: " + exit);
                ++ei; // set-up to handle the next exit
            } else {
                // There is no exit from the prev block, so we fall
                // into the curr block (if any)
                if (curr != null) {
                    DataBlock fall = new DataBlockFallThrough(curr.rootId());
                    curr.add(fall);
                }
            }

            prev = curr;
        }
        // System.out.println("ei: " + ei + " / " + exits.size());
        assert (ei == exits.size());
    }

    private void insertInstrumentation() {
        MethodNode methodNode = (MethodNode) mv;
        InsnList instructions = methodNode.instructions;

        for (Map.Entry<AbstractInsnNode, SimpleBasicBlock> entry : insnToBB.entrySet()) {

            // Basic block 'bb' starts at instruction 'insn'
            AbstractInsnNode insn = entry.getKey();
            SimpleBasicBlock bb = entry.getValue();
            //   System.out.println("insn = " + insn);
            instructions.insert(insn, Instrumenter.instrumentation(bb, params.isDetectInternal()));
        }
    }

    @Override
    public void visitAttribute(Attribute attr) {
        super.visitAttribute(attr);
        if (attr instanceof CharacterRangeTableAttribute) {
            method().setCharacterRangeTable((CharacterRangeTableAttribute) attr);
        }
    }

    // the instruction list has been built, insert the instrumentation
    @Override
    public void visitEnd() {
        super.visitEnd();

        SimpleBasicBlock[] basicBlocks = completeComputationOfCodeLabelNodes();
        computeEndBCIsAndFoldInExits(basicBlocks);
        //debugDump();
        insertInstrumentation();
        method().setBasicBlocks(basicBlocks);

        // push the result to the writer
        MethodNode methodNode = (MethodNode) mv;
        methodNode.accept(nextVisitor);
    }
}