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.concurrent.atomic.AtomicIntegerArray; 029 030import org.objectweb.asm.ClassVisitor; 031import org.objectweb.asm.FieldVisitor; 032import org.objectweb.asm.Label; 033import org.objectweb.asm.MethodVisitor; 034import org.objectweb.asm.Opcodes; 035import org.objectweb.asm.Type; 036 037/** 038 * <p>The {@link CodeProvider} uses {@link AtomicArrayCodeProvider} to store counters.</p> 039 * 040 * This implementation is totally thread-safe, but significantly slower then {@link FastArrayCodeProvider}. 041 * 042 * @author piotr.tabor@gmail.com 043 */ 044public class AtomicArrayCodeProvider extends AbstractCodeProvider implements CodeProvider { 045 /** 046 * Type of the generated field, that is used to store counters 047 */ 048 static final String COBERTURA_COUNTERS_FIELD_TYPE = Type.getType(AtomicIntegerArray.class).toString(); 049 050 public void generateCountersField(ClassVisitor cv) { 051 FieldVisitor fv=cv.visitField(Opcodes.ACC_STATIC|Opcodes.ACC_PUBLIC|Opcodes.ACC_FINAL|Opcodes.ACC_TRANSIENT, 052 COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE, null, null); 053 fv.visitEnd(); 054 } 055 056 public void generateCINITmethod(MethodVisitor mv,String className,int counters_cnt){ 057 mv.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 058 Label l1 = new Label(); 059 mv.visitJumpInsn(Opcodes.IFNONNULL, l1); 060 061 mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(AtomicIntegerArray.class)); 062 mv.visitInsn(Opcodes.DUP); 063 mv.visitLdcInsn(counters_cnt); 064 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, 065 Type.getInternalName(AtomicIntegerArray.class), "<init>", "(I)V"); 066 mv.visitFieldInsn(Opcodes.PUTSTATIC, className, 067 COBERTURA_COUNTERS_FIELD_NAME, 068 COBERTURA_COUNTERS_FIELD_TYPE); 069 generateRegisterClass(mv, className); 070 mv.visitLabel(l1); 071 } 072 073 public void generateCodeThatIncrementsCoberturaCounter(MethodVisitor nextMethodVisitor, Integer counterId,String className) { 074 /*cobertura_counters.incrementAndGet(i);*/ 075 /*cobertura_counters.*/nextMethodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 076 /*index:*/nextMethodVisitor.visitLdcInsn((int)counterId); 077 nextMethodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,Type.getInternalName(AtomicIntegerArray.class), "incrementAndGet", "(I)I"); 078 nextMethodVisitor.visitInsn(Opcodes.POP); 079 } 080 081 public void generateCodeThatIncrementsCoberturaCounterFromInternalVariable(MethodVisitor nextMethodVisitor, int lastJumpIdVariableIndex, String className) { 082 /*cobertura_counters.incrementAndGet(value('lastJumpIdVariableIndex'));*/ 083 /*cobertura_counters.*/nextMethodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 084 /*index:*/nextMethodVisitor.visitVarInsn(Opcodes.ILOAD, lastJumpIdVariableIndex); 085 nextMethodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,Type.getInternalName(AtomicIntegerArray.class), "incrementAndGet", "(I)I"); 086 nextMethodVisitor.visitInsn(Opcodes.POP); 087 } 088 089 /** 090 * <pre> 091 int[] __cobertura_get_and_reset_counters() { 092 int[] res = new int[counters.length()]; 093 for(int i=0; i<counters.length(); i++){ 094 res[i]=counters.getAndSet(i, 0); 095 } 096 return res; 097 } 098 </pre> 099 */ 100 public void generateCoberturaGetAndResetCountersMethod(ClassVisitor cv, String className){ 101 MethodVisitor mv = cv.visitMethod( 102 Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, 103 COBERTURA_GET_AND_RESET_COUNTERS_METHOD_NAME, 104 "()[I", 105 null,null); 106 107 mv.visitCode(); 108 mv.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 109 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/atomic/AtomicIntegerArray", "length", "()I"); 110 mv.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT); 111 mv.visitVarInsn(Opcodes.ASTORE, 0); 112 mv.visitInsn(Opcodes.ICONST_0); 113 mv.visitVarInsn(Opcodes.ISTORE, 1); 114 Label l3 = new Label(); 115 mv.visitJumpInsn(Opcodes.GOTO, l3); 116 Label l4 = new Label(); 117 mv.visitLabel(l4); 118 mv.visitVarInsn(Opcodes.ALOAD, 0); 119 mv.visitVarInsn(Opcodes.ILOAD, 1); 120 mv.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 121 mv.visitVarInsn(Opcodes.ILOAD, 1); 122 mv.visitInsn(Opcodes.ICONST_0); 123 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/atomic/AtomicIntegerArray", "getAndSet", "(II)I"); 124 mv.visitInsn(Opcodes.IASTORE); 125 mv.visitIincInsn(1, 1); 126 mv.visitLabel(l3); 127 mv.visitVarInsn(Opcodes.ILOAD, 1); 128 mv.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 129 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/atomic/AtomicIntegerArray", "length", "()I"); 130 mv.visitJumpInsn(Opcodes.IF_ICMPLT, l4); 131 mv.visitVarInsn(Opcodes.ALOAD, 0); 132 mv.visitInsn(Opcodes.ARETURN); 133 mv.visitMaxs(0, 0);//will be recalculated by writer 134 mv.visitEnd(); 135 } 136 137}