001/* 002 * Cobertura - http://cobertura.sourceforge.net/ 003 * 004 * Copyright (C) 2011 Piotr Tabor 005 * 006 * Note: This file is dual licensed under the GPL and the Apache 007 * Source License (so that it can be used from both the main 008 * Cobertura classes and the ant tasks). 009 * 010 * Cobertura is free software; you can redistribute it and/or modify 011 * it under the terms of the GNU General Public License as published 012 * by the Free Software Foundation; either version 2 of the License, 013 * or (at your option) any later version. 014 * 015 * Cobertura is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 018 * General Public License for more details. 019 * 020 * You should have received a copy of the GNU General Public License 021 * along with Cobertura; if not, write to the Free Software 022 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 023 * USA 024 */ 025 026package net.sourceforge.cobertura.instrument.pass3; 027 028import java.util.Collection; 029import java.util.LinkedList; 030import java.util.List; 031 032import net.sourceforge.cobertura.coveragedata.LightClassmapListener; 033import net.sourceforge.cobertura.coveragedata.TouchCollector; 034import net.sourceforge.cobertura.instrument.tp.ClassMap; 035import net.sourceforge.cobertura.instrument.tp.JumpTouchPointDescriptor; 036import net.sourceforge.cobertura.instrument.tp.LineTouchPointDescriptor; 037import net.sourceforge.cobertura.instrument.tp.SwitchTouchPointDescriptor; 038import net.sourceforge.cobertura.instrument.tp.TouchPointDescriptor; 039 040import org.objectweb.asm.ClassVisitor; 041import org.objectweb.asm.Label; 042import org.objectweb.asm.MethodVisitor; 043import org.objectweb.asm.Opcodes; 044import org.objectweb.asm.Type; 045 046/** 047 * Common method used by most of {@link AbstractCodeProvider} implementations. 048 * The methods realized here are independent on counters storing structure. 049 * 050 * 051 * @author piotr.tabor@gmail.com 052 */ 053public abstract class AbstractCodeProvider implements CodeProvider { 054 055 /** 056 * CounterId used to store unnecessary events to avoid fake jump counting in 057 * instrumented(generated) code 058 */ 059 public static final int FAKE_COUNTER_ID = 0; 060 061 public AbstractCodeProvider() { 062 super(); 063 } 064 065 public void generateCodeThatSetsJumpCounterIdVariable( 066 MethodVisitor nextMethodVisitor, int new_value, 067 int lastJumpIdVariableIndex) { 068 nextMethodVisitor.visitLdcInsn(new_value); 069 nextMethodVisitor.visitVarInsn(Opcodes.ISTORE, lastJumpIdVariableIndex); 070 } 071 072 public void generateCodeThatZeroJumpCounterIdVariable( 073 MethodVisitor nextMethodVisitor, int lastJumpIdVariableIndex) { 074 generateCodeThatSetsJumpCounterIdVariable(nextMethodVisitor, 075 FAKE_COUNTER_ID, lastJumpIdVariableIndex); 076 } 077 078 public void generateCodeThatIncrementsCoberturaCounterIfVariableEqualsAndCleanVariable( 079 MethodVisitor nextMethodVisitor, 080 Integer neededJumpCounterIdVariableValue, 081 Integer counterIdToIncrement, int lastJumpIdVariableIndex, 082 String className) { 083 084 nextMethodVisitor.visitLdcInsn((int) neededJumpCounterIdVariableValue); 085 nextMethodVisitor.visitVarInsn(Opcodes.ILOAD, lastJumpIdVariableIndex); 086 Label afterJump = new Label(); 087 nextMethodVisitor.visitJumpInsn(Opcodes.IF_ICMPNE, afterJump); 088 generateCodeThatIncrementsCoberturaCounter(nextMethodVisitor, 089 counterIdToIncrement, className); 090 generateCodeThatZeroJumpCounterIdVariable(nextMethodVisitor, 091 lastJumpIdVariableIndex); 092 nextMethodVisitor.visitLabel(afterJump); 093 } 094 095 /** 096 * {@inheritDoc} 097 * 098 * The code injected by this implementation just registers the class using {@link TouchCollector#registerClass(Class)}. This way, during the 099 * execution, touch collector knows that is responsible to ask the class after execution about a current status of the counters. 100 */ 101 protected void generateRegisterClass(MethodVisitor mv, String className) { 102 mv.visitLdcInsn(Type.getObjectType(className)); 103 mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(TouchCollector.class), "registerClass","(Ljava/lang/Class;)V"); 104 } 105 106 final String CLASSMAP_LISTENER_INTERNALNAME = Type.getInternalName(LightClassmapListener.class); 107 108 /** 109 * {@inheritDoc}<br/><br/> 110 * 111 * Generates method (named {@link #COBERTURA_CLASSMAP_METHOD_NAME}) with such a signature: 112 * __cobertura_classmap( {@link LightClassmapListener} listener).</br> 113 * 114 * The method informs the listener about all lines, jumps and switches found, and about all counters tracking 115 * the constructions. 116 */ 117 public void generateCoberturaClassMapMethod(ClassVisitor cv, ClassMap classMap) { 118 119 LinkedList<TouchPointDescriptor> touchPointDescriptors = new LinkedList<TouchPointDescriptor>(classMap.getTouchPointsInLineOrder()); 120 int parts = 0; 121 for(int j=0; touchPointDescriptors.size() > 0; j++) { 122 List<TouchPointDescriptor> bufor = new LinkedList<TouchPointDescriptor>(); 123 for (int i = 0; i < 1000 && touchPointDescriptors.size() > 0; i++){ 124 bufor.add(touchPointDescriptors.removeFirst()); 125 } 126 classMapContent(cv, j, bufor); 127 parts++; 128 } 129 130 MethodVisitor mv=cv.visitMethod( 131 Opcodes.ACC_PUBLIC|Opcodes.ACC_STATIC, 132 COBERTURA_CLASSMAP_METHOD_NAME, 133 "("+Type.getType(LightClassmapListener.class).toString()+")V", 134 null,null); 135 mv.visitCode(); 136 mv.visitVarInsn(Opcodes.ALOAD,0); 137 138 mv.visitInsn(Opcodes.DUP); 139 mv.visitLdcInsn(Type.getObjectType(classMap.getClassName())); 140 mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,CLASSMAP_LISTENER_INTERNALNAME, "setClazz", "(Ljava/lang/Class;)V"); 141 142 if(classMap.getSource()!=null){ 143 mv.visitInsn(Opcodes.DUP); 144 mv.visitLdcInsn(classMap.getSource()); 145 mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,CLASSMAP_LISTENER_INTERNALNAME, "setSource", "(Ljava/lang/String;)V"); 146 } 147 148 for (int i=0; i < parts; i++) { 149 mv.visitInsn(Opcodes.DUP); 150 mv.visitMethodInsn(Opcodes.INVOKESTATIC, classMap.getClassName(), 151 COBERTURA_CLASSMAP_METHOD_NAME+"_"+i, "("+Type.getType(LightClassmapListener.class).toString()+")V"); 152 } 153 154 mv.visitInsn(Opcodes.POP); 155 mv.visitInsn(Opcodes.RETURN); 156 mv.visitMaxs(0, 0);//will be recalculated by writer 157 mv.visitEnd(); 158 } 159 160 enum Abcd { 161 A, B, C; 162 } 163 164 private void classMapContent(ClassVisitor cv, int nr, List<TouchPointDescriptor> touchPointDescriptors){ 165 MethodVisitor mv=cv.visitMethod( 166 Opcodes.ACC_PUBLIC|Opcodes.ACC_STATIC, 167 COBERTURA_CLASSMAP_METHOD_NAME + "_" + nr, 168 "("+Type.getType(LightClassmapListener.class).toString()+")V", 169 null,null); 170 mv.visitCode(); 171 mv.visitVarInsn(Opcodes.ALOAD,0); 172 for (TouchPointDescriptor tpd : touchPointDescriptors){ 173 mv.visitInsn(Opcodes.DUP); 174 mv.visitLdcInsn(tpd.getLineNumber()); 175 if (tpd instanceof LineTouchPointDescriptor){ 176 mv.visitLdcInsn(((LineTouchPointDescriptor) tpd).getCounterId()); 177 mv.visitLdcInsn(((LineTouchPointDescriptor) tpd).getMethodName()); 178 mv.visitLdcInsn(((LineTouchPointDescriptor) tpd).getMethodSignature()); 179 mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME,"putLineTouchPoint","(IILjava/lang/String;Ljava/lang/String;)V"); 180 }else if (tpd instanceof JumpTouchPointDescriptor){ 181 mv.visitLdcInsn(((JumpTouchPointDescriptor) tpd).getCounterIdForTrue()); 182 mv.visitLdcInsn(((JumpTouchPointDescriptor) tpd).getCounterIdForFalse()); 183 mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME,"putJumpTouchPoint","(III)V"); 184 }else if (tpd instanceof SwitchTouchPointDescriptor) { 185 SwitchTouchPointDescriptor stpd=(SwitchTouchPointDescriptor)tpd; 186 final String enum_sign = ((SwitchTouchPointDescriptor) tpd).getEnumType(); 187 if (enum_sign == null) { 188 mv.visitLdcInsn(Integer.MAX_VALUE); 189 } else { 190 mv.visitMethodInsn(Opcodes.INVOKESTATIC, enum_sign, "values", "()[L" + enum_sign + ";"); 191 mv.visitInsn(Opcodes.ARRAYLENGTH); 192 } 193 Collection<Integer> ci=stpd.getCountersForLabels(); 194 mv.visitLdcInsn(ci.size());//Size of a new table 195 mv.visitIntInsn(Opcodes.NEWARRAY,Opcodes.T_INT); 196 int i=0; 197 for(Integer counterId:ci){ 198 mv.visitInsn(Opcodes.DUP); //First for addition of items, second ad putSwitchTouchPoint parameter (or next loop iteration) 199 mv.visitLdcInsn(i); 200 mv.visitLdcInsn(counterId); 201 mv.visitInsn(Opcodes.IASTORE); 202 i++; 203 } 204 mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, CLASSMAP_LISTENER_INTERNALNAME,"putSwitchTouchPoint","(II[I)V"); 205 } 206 } 207 mv.visitInsn(Opcodes.POP); 208 mv.visitInsn(Opcodes.RETURN); 209 mv.visitMaxs(0, 0);//will be recalculated by writer 210 mv.visitEnd(); 211 } 212 213 /** 214 * Generates code that is injected into static constructor of an instrumented class. 215 * 216 * It is good place to initiate static fields inserted into a class ({@link #generateCountersField(ClassVisitor)}), 217 * or execute other code that should be executed when the class it used for the first time. Registering the class in 218 * {@link TouchCollector} would be a bright idea. 219 * 220 * It is expected that all counter will be set to zero after that operation. 221 * 222 * @param mv - {@link MethodVisitor} that is listener of code-generation events 223 * @param className - internal name (asm) of class being instrumented 224 * @param counters_cnt - information about how many counters are expected to be used by instrumentation code. 225 * In most cases the method is responsible for allocating objects that will be used to store counters. 226 */ 227 protected abstract void generateCINITmethod(MethodVisitor mv, String className, int counters_cnt); 228 229 public void generateCoberturaInitMethod(ClassVisitor cv, String className, int countersCnt) { 230 MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, 231 COBERTURA_INIT_METHOD_NAME, "()V", null, null); 232 mv.visitCode(); 233 generateCINITmethod(mv, className, countersCnt); 234 mv.visitInsn(Opcodes.RETURN); 235 mv.visitMaxs(0, 0); //will be recalculated by writer 236 mv.visitEnd(); 237 } 238 239 public void generateCallCoberturaInitMethod(MethodVisitor mv, String className) { 240 mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, COBERTURA_INIT_METHOD_NAME, "()V"); 241 } 242}