org.jacoco.core.internal.instr.MethodInstrumenter.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.internal.instr.MethodInstrumenter.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Marc R. Hoffmann - initial API and implementation
 *    
 *******************************************************************************/
package org.jacoco.core.internal.instr;

import org.jacoco.core.internal.flow.MethodProbesVisitor;
import org.jacoco.core.internal.flow.LabelInfo;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * This method adapter inserts probes as requested by the
 * {@link MethodProbesVisitor} events.
 */
class MethodInstrumenter extends MethodProbesVisitor {

    private final IProbeInserter probeInserter;
    private final IFrameInserter frameInserter;

    /**
     * Create a new instrumenter instance for the given method.
     * 
     * @param mv
     *            next method visitor in the chain
     * @param probeInserter
     *            call-back to insert probes where required
     * @param frameInserter
     *            call-back to insert additional frames where required
     */
    public MethodInstrumenter(final MethodVisitor mv, final IProbeInserter probeInserter,
            final IFrameInserter frameInserter) {
        super(mv);
        this.probeInserter = probeInserter;
        this.frameInserter = frameInserter;
    }

    // === IMethodProbesVisitor ===

    @Override
    public void visitProbe(final int probeId) {
        probeInserter.insertProbe(probeId);
    }

    @Override
    public void visitInsnWithProbe(final int opcode, final int probeId) {
        probeInserter.insertProbe(probeId);
        mv.visitInsn(opcode);
    }

    @Override
    public void visitJumpInsnWithProbe(final int opcode, final Label label, final int probeId) {
        if (opcode == Opcodes.GOTO) {
            probeInserter.insertProbe(probeId);
            mv.visitJumpInsn(Opcodes.GOTO, label);
        } else {
            final Label intermediate = new Label();
            mv.visitJumpInsn(getInverted(opcode), intermediate);
            probeInserter.insertProbe(probeId);
            mv.visitJumpInsn(Opcodes.GOTO, label);
            mv.visitLabel(intermediate);
            frameInserter.insertFrame();
        }
    }

    private int getInverted(final int opcode) {
        switch (opcode) {
        case Opcodes.IFEQ:
            return Opcodes.IFNE;
        case Opcodes.IFNE:
            return Opcodes.IFEQ;
        case Opcodes.IFLT:
            return Opcodes.IFGE;
        case Opcodes.IFGE:
            return Opcodes.IFLT;
        case Opcodes.IFGT:
            return Opcodes.IFLE;
        case Opcodes.IFLE:
            return Opcodes.IFGT;
        case Opcodes.IF_ICMPEQ:
            return Opcodes.IF_ICMPNE;
        case Opcodes.IF_ICMPNE:
            return Opcodes.IF_ICMPEQ;
        case Opcodes.IF_ICMPLT:
            return Opcodes.IF_ICMPGE;
        case Opcodes.IF_ICMPGE:
            return Opcodes.IF_ICMPLT;
        case Opcodes.IF_ICMPGT:
            return Opcodes.IF_ICMPLE;
        case Opcodes.IF_ICMPLE:
            return Opcodes.IF_ICMPGT;
        case Opcodes.IF_ACMPEQ:
            return Opcodes.IF_ACMPNE;
        case Opcodes.IF_ACMPNE:
            return Opcodes.IF_ACMPEQ;
        case Opcodes.IFNULL:
            return Opcodes.IFNONNULL;
        case Opcodes.IFNONNULL:
            return Opcodes.IFNULL;
        }
        throw new IllegalArgumentException();
    }

    @Override
    public void visitTableSwitchInsnWithProbes(final int min, final int max, final Label dflt,
            final Label[] labels) {
        // 1. Calculate intermediate labels:
        LabelInfo.resetDone(dflt);
        LabelInfo.resetDone(labels);
        final Label newDflt = createIntermediate(dflt);
        final Label[] newLabels = createIntermediates(labels);
        mv.visitTableSwitchInsn(min, max, newDflt, newLabels);

        // 2. Insert probes:
        insertIntermediateProbes(dflt, labels);
    }

    @Override
    public void visitLookupSwitchInsnWithProbes(final Label dflt, final int[] keys, final Label[] labels) {
        // 1. Calculate intermediate labels:
        LabelInfo.resetDone(dflt);
        LabelInfo.resetDone(labels);
        final Label newDflt = createIntermediate(dflt);
        final Label[] newLabels = createIntermediates(labels);
        mv.visitLookupSwitchInsn(newDflt, keys, newLabels);

        // 2. Insert probes:
        insertIntermediateProbes(dflt, labels);
    }

    private Label[] createIntermediates(final Label[] labels) {
        final Label[] intermediates = new Label[labels.length];
        for (int i = 0; i < labels.length; i++) {
            intermediates[i] = createIntermediate(labels[i]);
        }
        return intermediates;
    }

    private Label createIntermediate(final Label label) {
        final Label intermediate;
        if (LabelInfo.getProbeId(label) == LabelInfo.NO_PROBE) {
            intermediate = label;
        } else {
            if (LabelInfo.isDone(label)) {
                intermediate = LabelInfo.getIntermediateLabel(label);
            } else {
                intermediate = new Label();
                LabelInfo.setIntermediateLabel(label, intermediate);
                LabelInfo.setDone(label);
            }
        }
        return intermediate;
    }

    private void insertIntermediateProbe(final Label label) {
        final int probeId = LabelInfo.getProbeId(label);
        if (probeId != LabelInfo.NO_PROBE && !LabelInfo.isDone(label)) {
            mv.visitLabel(LabelInfo.getIntermediateLabel(label));
            frameInserter.insertFrame();
            probeInserter.insertProbe(probeId);
            mv.visitJumpInsn(Opcodes.GOTO, label);
            LabelInfo.setDone(label);
        }
    }

    private void insertIntermediateProbes(final Label dflt, final Label[] labels) {
        LabelInfo.resetDone(dflt);
        LabelInfo.resetDone(labels);
        insertIntermediateProbe(dflt);
        for (final Label l : labels) {
            insertIntermediateProbe(l);
        }
    }

}