net.sourceforge.cobertura.instrument.pass3.AbstractCodeProvider.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.cobertura.instrument.pass3.AbstractCodeProvider.java

Source

/*
 * Cobertura - http://cobertura.sourceforge.net/
 *
 * Copyright (C) 2011 Piotr Tabor
 *
 * Note: This file is dual licensed under the GPL and the Apache
 * Source License (so that it can be used from both the main
 * Cobertura classes and the ant tasks).
 *
 * Cobertura is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 *
 * Cobertura 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 General Public License
 * along with Cobertura; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

package net.sourceforge.cobertura.instrument.pass3;

import net.sourceforge.cobertura.coveragedata.LightClassmapListener;
import net.sourceforge.cobertura.coveragedata.TouchCollector;
import net.sourceforge.cobertura.instrument.tp.*;
import org.objectweb.asm.*;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

/**
 * Common method used by most of {@link AbstractCodeProvider} implementations.
 * The methods realized here are independent on counters storing structure.
 *
 * @author piotr.tabor@gmail.com
 */
public abstract class AbstractCodeProvider implements CodeProvider {

    /**
     * CounterId used to store unnecessary events to avoid fake jump counting in
     * instrumented(generated) code
     */
    public static final int FAKE_COUNTER_ID = 0;

    public AbstractCodeProvider() {
        super();
    }

    public void generateCodeThatSetsJumpCounterIdVariable(MethodVisitor nextMethodVisitor, int new_value,
            int lastJumpIdVariableIndex) {
        nextMethodVisitor.visitLdcInsn(new_value);
        nextMethodVisitor.visitVarInsn(Opcodes.ISTORE, lastJumpIdVariableIndex);
    }

    public void generateCodeThatZeroJumpCounterIdVariable(MethodVisitor nextMethodVisitor,
            int lastJumpIdVariableIndex) {
        generateCodeThatSetsJumpCounterIdVariable(nextMethodVisitor, FAKE_COUNTER_ID, lastJumpIdVariableIndex);
    }

    public void generateCodeThatIncrementsCoberturaCounterIfVariableEqualsAndCleanVariable(
            MethodVisitor nextMethodVisitor, Integer neededJumpCounterIdVariableValue, Integer counterIdToIncrement,
            int lastJumpIdVariableIndex, String className) {

        nextMethodVisitor.visitLdcInsn((int) neededJumpCounterIdVariableValue);
        nextMethodVisitor.visitVarInsn(Opcodes.ILOAD, lastJumpIdVariableIndex);
        Label afterJump = new Label();
        nextMethodVisitor.visitJumpInsn(Opcodes.IF_ICMPNE, afterJump);
        generateCodeThatIncrementsCoberturaCounter(nextMethodVisitor, counterIdToIncrement, className);
        generateCodeThatZeroJumpCounterIdVariable(nextMethodVisitor, lastJumpIdVariableIndex);
        nextMethodVisitor.visitLabel(afterJump);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * The code injected by this implementation just registers the class using {@link TouchCollector#registerClass(Class)}. This way, during the
     * execution, touch collector knows that is responsible to ask the class after execution about a current status of the counters.
     */
    protected void generateRegisterClass(MethodVisitor mv, String className) {
        mv.visitLdcInsn(className);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(TouchCollector.class), "registerClass",
                "(Ljava/lang/String;)V");
    }

    final String CLASSMAP_LISTENER_INTERNALNAME = Type.getInternalName(LightClassmapListener.class);

    /**
     * {@inheritDoc}<br/><br/>
     * <p/>
     * Generates method (named  {@link #COBERTURA_CLASSMAP_METHOD_NAME}) with such a signature:
     * __cobertura_classmap( {@link LightClassmapListener} listener).</br>
     * <p/>
     * The method informs the listener about all lines, jumps and switches found, and about all counters tracking
     * the constructions.
     */
    public void generateCoberturaClassMapMethod(ClassVisitor cv, ClassMap classMap) {

        LinkedList<TouchPointDescriptor> touchPointDescriptors = new LinkedList<TouchPointDescriptor>(
                classMap.getTouchPointsInLineOrder());
        int parts = 0;
        for (int j = 0; touchPointDescriptors.size() > 0; j++) {
            List<TouchPointDescriptor> bufor = new LinkedList<TouchPointDescriptor>();
            for (int i = 0; i < 1000 && touchPointDescriptors.size() > 0; i++) {
                bufor.add(touchPointDescriptors.removeFirst());
            }
            classMapContent(cv, j, bufor);
            parts++;
        }

        MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, COBERTURA_CLASSMAP_METHOD_NAME,
                "(" + Type.getType(LightClassmapListener.class).toString() + ")V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);

        mv.visitInsn(Opcodes.DUP);
        mv.visitLdcInsn(classMap.getClassName());
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME, "setClazz",
                "(Ljava/lang/String;)V");

        if (classMap.getSource() != null) {
            mv.visitInsn(Opcodes.DUP);
            mv.visitLdcInsn(classMap.getSource());
            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME, "setSource",
                    "(Ljava/lang/String;)V");
        }

        for (int i = 0; i < parts; i++) {
            mv.visitInsn(Opcodes.DUP);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, classMap.getClassName(),
                    COBERTURA_CLASSMAP_METHOD_NAME + "_" + i,
                    "(" + Type.getType(LightClassmapListener.class).toString() + ")V");
        }

        mv.visitInsn(Opcodes.POP);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(0, 0);//will be recalculated by writer
        mv.visitEnd();
    }

    enum Abcd {
        A, B, C;
    }

    private void classMapContent(ClassVisitor cv, int nr, List<TouchPointDescriptor> touchPointDescriptors) {
        MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
                COBERTURA_CLASSMAP_METHOD_NAME + "_" + nr,
                "(" + Type.getType(LightClassmapListener.class).toString() + ")V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        for (TouchPointDescriptor tpd : touchPointDescriptors) {
            mv.visitInsn(Opcodes.DUP);
            mv.visitLdcInsn(tpd.getLineNumber());
            if (tpd instanceof LineTouchPointDescriptor) {
                mv.visitLdcInsn(((LineTouchPointDescriptor) tpd).getCounterId());
                mv.visitLdcInsn(((LineTouchPointDescriptor) tpd).getMethodName());
                mv.visitLdcInsn(((LineTouchPointDescriptor) tpd).getMethodSignature());
                mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME, "putLineTouchPoint",
                        "(IILjava/lang/String;Ljava/lang/String;)V");
            } else if (tpd instanceof JumpTouchPointDescriptor) {
                mv.visitLdcInsn(((JumpTouchPointDescriptor) tpd).getCounterIdForTrue());
                mv.visitLdcInsn(((JumpTouchPointDescriptor) tpd).getCounterIdForFalse());
                mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME, "putJumpTouchPoint",
                        "(III)V");
            } else if (tpd instanceof SwitchTouchPointDescriptor) {
                SwitchTouchPointDescriptor stpd = (SwitchTouchPointDescriptor) tpd;
                final String enum_sign = ((SwitchTouchPointDescriptor) tpd).getEnumType();
                if (enum_sign == null) {
                    mv.visitLdcInsn(Integer.MAX_VALUE);
                } else {
                    mv.visitMethodInsn(Opcodes.INVOKESTATIC, enum_sign, "values", "()[L" + enum_sign + ";");
                    mv.visitInsn(Opcodes.ARRAYLENGTH);
                }
                Collection<Integer> ci = stpd.getCountersForLabels();
                mv.visitLdcInsn(ci.size());//Size of a new table
                mv.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
                int i = 0;
                for (Integer counterId : ci) {
                    mv.visitInsn(Opcodes.DUP); //First for addition of items, second ad putSwitchTouchPoint parameter (or next loop iteration)
                    mv.visitLdcInsn(i);
                    mv.visitLdcInsn(counterId);
                    mv.visitInsn(Opcodes.IASTORE);
                    i++;
                }
                mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME, "putSwitchTouchPoint",
                        "(II[I)V");
            }
        }
        mv.visitInsn(Opcodes.POP);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(0, 0);//will be recalculated by writer
        mv.visitEnd();
    }

    /**
     * Generates code that is injected into static constructor of an instrumented class.
     * <p/>
     * It is good place to initiate static fields inserted into a class ({@link #generateCountersField(ClassVisitor)}),
     * or execute other code that should be executed when the class it used for the first time. Registering the class in
     * {@link TouchCollector} would be a bright idea.
     * <p/>
     * It is expected that all counter will be set to zero after that operation.
     *
     * @param mv           - {@link MethodVisitor} that is listener of code-generation events
     * @param className    - internal name (asm) of class being instrumented
     * @param counters_cnt - information about how many counters are expected to be used by instrumentation code.
     *                     In most cases the method is responsible for allocating objects that will be used to store counters.
     */
    protected abstract void generateCINITmethod(MethodVisitor mv, String className, int counters_cnt);

    public void generateCoberturaInitMethod(ClassVisitor cv, String className, int countersCnt) {
        MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, COBERTURA_INIT_METHOD_NAME,
                "()V", null, null);
        mv.visitCode();
        generateCINITmethod(mv, className, countersCnt);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(0, 0); //will be recalculated by writer
        mv.visitEnd();
    }

    public void generateCallCoberturaInitMethod(MethodVisitor mv, String className) {
        mv.visitCode(); // Since we are using the ASM Check Adapter, we need to visit the code before visiting any instructions.
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, COBERTURA_INIT_METHOD_NAME, "()V");
    }
}