package com.ikokoon.serenity.instrumentation.coverage; import org.apache.log4j.Logger; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import com.ikokoon.toolkit.Toolkit; /** * This is an alternative class for adding coverage instructions line by line. The process is as follows: * * 1) Add an array of integers the size of the lines in the class, to the class.<br> * 2) At each line increment the indexed line in the array.<br> * 3) Add a method to the class to retrieve the array of line counters.<br> * 4) At the end of the processing, i.e. when the JVM exist walk over the classes and accumulate all the data.<br> * * This method could possibly be faster, with less overhead in the actual class, but has not been fully implemented nor tested as of the writing as * the original method for coverage is sufficient for current purposes. * * @author Michael Couck * @since 19.01.10 * @version 01.00 */ public class CoverageClassAdapterExt extends ClassAdapter implements Opcodes { private Logger logger = Logger.getLogger(this.getClass()); /** The name of the class that is being instrumented. */ private String className; private String linesArrayName = Toolkit.replaceAll(this.getClass().getName(), ".", "$") + "$Lines"; private String linesArrayDescription = Type.getInternalName(int[].class); private String linesArrayMethodName = "get$" + linesArrayName; private String linesArrayMethodDescription = Type.getMethodDescriptor(Type.getType(int[].class), new Type[0]); private String constantPoolInitMethodName = "<clinit>"; private String constantPoolInitMethodDescription = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]); public CoverageClassAdapterExt(ClassVisitor visitor, String className) { super(visitor); this.className = Toolkit.slashToDot(className); logger.debug("Constructor : " + className + ", " + linesArrayName + ", " + linesArrayDescription); } public MethodVisitor visitMethod(int access, String methodName, String methodDescription, String methodSignature, String[] exceptions) { logger.debug("visitMethod : " + access + ", " + methodName + ", " + methodDescription + ", " + methodSignature + ", " + exceptions); MethodVisitor methodVisitor = super.visitMethod(access, methodName, methodDescription, methodSignature, exceptions); CoverageMethodAdapterExt methodAdapter = new CoverageMethodAdapterExt(methodVisitor, className, linesArrayName, linesArrayDescription); logger.debug("Lines : " + CoverageMethodAdapterExt.lines); return methodAdapter; } public void visitEnd() { FieldVisitor fv = super.visitField(ACC_PRIVATE + ACC_STATIC, linesArrayName, linesArrayDescription, null, null); fv.visitEnd(); MethodVisitor mv = super.visitMethod(ACC_STATIC, constantPoolInitMethodName, constantPoolInitMethodDescription, null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(5, l0); mv.visitIntInsn(BIPUSH, 10); mv.visitIntInsn(NEWARRAY, T_INT); mv.visitFieldInsn(PUTSTATIC, Toolkit.dotToSlash(className), linesArrayName, linesArrayDescription); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLineNumber(3, l1); mv.visitInsn(RETURN); mv.visitMaxs(1, 0); mv.visitEnd(); mv = super.visitMethod(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, linesArrayMethodName, linesArrayMethodDescription, null, null); mv.visitCode(); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLineNumber(8, l2); mv.visitFieldInsn(GETSTATIC, Toolkit.dotToSlash(className), linesArrayName, linesArrayDescription); mv.visitInsn(ARETURN); mv.visitMaxs(1, 0); mv.visitEnd(); } }