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 org.objectweb.asm.ClassVisitor; 029import org.objectweb.asm.FieldVisitor; 030import org.objectweb.asm.Label; 031import org.objectweb.asm.MethodVisitor; 032import org.objectweb.asm.Opcodes; 033 034/** 035 * <p>The {@link CodeProvider} uses int[] to store counters.</p> 036 * 037 * <p>This implementation is not fully thread-safe, but significantly (10-100x) then {@link AtomicArrayCodeProvider}.</p> 038 * 039 * <p>What does it mean 'not fully thead-safe' ? 040 * <ul> 041 * <li>Using this provider will never cause throwing any exception because of concurrency problems</li> 042 * <li>A code coverage results acquired using this code-provider will be exactly the same as using thread-safe provider)</li> * 043 * <li>There could happen small (experiments showed around 1-3%) in value of specific counters because of race-condition.</li> 044 * </ul> 045 * </p> 046 * 047 * <p> 048 * The reason of the race condition is fact that instruction: __cobertura_counters[counter_id]++ is translated into 049 * sequence of operations: <ol> 050 * <li>get value of __cobertura_counters[counter_id]</li> 051 * <li>increment value</li> 052 * <li>store value into __cobertura_counters[counter_id]</li> 053 * </ol> 054 * This mean that in case of race condition we can miss some increments. But if a counter was hit at least once, we 055 * are sure that we will increment the counter at least one. For code coverage results fact of being hit is crucial. 056 * </p> 057 * 058 * @author piotr.tabor@gmail.com 059 */ 060public class FastArrayCodeProvider extends AbstractCodeProvider implements CodeProvider { 061 062 /** 063 * Type of the generated field, that is used to store counters 064 */ 065 static final String COBERTURA_COUNTERS_FIELD_TYPE = "[I"; 066 067 public void generateCodeThatIncrementsCoberturaCounterFromInternalVariable(MethodVisitor nextMethodVisitor, int lastJumpIdVariableIndex, String className) { 068 /*cobertura_counters[value('lastJumpIdVariableIndex')]++;*/ 069 /*cobertura_counters.*/nextMethodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 070 /*index:*/nextMethodVisitor.visitVarInsn(Opcodes.ILOAD, lastJumpIdVariableIndex); 071 nextMethodVisitor.visitInsn(Opcodes.DUP2); 072 nextMethodVisitor.visitInsn(Opcodes.IALOAD); 073 nextMethodVisitor.visitLdcInsn(1); 074 nextMethodVisitor.visitInsn(Opcodes.IADD); 075 nextMethodVisitor.visitInsn(Opcodes.IASTORE); 076 } 077 078 public void generateCodeThatIncrementsCoberturaCounter(MethodVisitor nextMethodVisitor, Integer counterId,String className) { 079 /*cobertura_counters[value('lastJumpIdVariableIndex')]++;*/ 080 /*cobertura_counters.*/nextMethodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 081 /*index:*/nextMethodVisitor.visitLdcInsn((int)counterId); 082 nextMethodVisitor.visitInsn(Opcodes.DUP2); 083 nextMethodVisitor.visitInsn(Opcodes.IALOAD); 084 nextMethodVisitor.visitLdcInsn(1); 085 nextMethodVisitor.visitInsn(Opcodes.IADD); 086 nextMethodVisitor.visitInsn(Opcodes.IASTORE); 087 } 088 089 090 public void generateCountersField(ClassVisitor cv) { 091 /*final tooks 270ms, no-modifier 310ms, volatile 500ms*/ 092 FieldVisitor fv=cv.visitField(Opcodes.ACC_STATIC|Opcodes.ACC_PUBLIC|Opcodes.ACC_FINAL|Opcodes.ACC_TRANSIENT, 093 COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE, null, null); 094 fv.visitEnd(); 095 } 096 097// static int x[]; 098// 099// static void abc() { 100// if (x == null) { 101// x = new int[5]; 102// } 103// } 104 105 public void generateCINITmethod(MethodVisitor mv,String className,int counters_cnt) { 106 mv.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 107 Label l1 = new Label(); 108 mv.visitJumpInsn(Opcodes.IFNONNULL, l1); 109 mv.visitLdcInsn(counters_cnt); 110 mv.visitIntInsn(Opcodes.NEWARRAY,Opcodes.T_INT); 111 mv.visitFieldInsn(Opcodes.PUTSTATIC, className, 112 COBERTURA_COUNTERS_FIELD_NAME, 113 COBERTURA_COUNTERS_FIELD_TYPE); 114 generateRegisterClass(mv, className); 115 mv.visitLabel(l1); 116 } 117 118 public void generateCoberturaGetAndResetCountersMethod(ClassVisitor cv, String className){ 119 MethodVisitor mv = cv.visitMethod( 120 Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, 121 COBERTURA_GET_AND_RESET_COUNTERS_METHOD_NAME, 122 "()[I", 123 null,null); 124 mv.visitCode(); 125 /*cobertura_counters.*/mv.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 126 mv.visitVarInsn(Opcodes.ASTORE, 0); 127 /*cobertura_counters.*/mv.visitFieldInsn(Opcodes.GETSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 128 mv.visitInsn(Opcodes.ARRAYLENGTH); 129 130 mv.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT); 131 mv.visitFieldInsn(Opcodes.PUTSTATIC, className, COBERTURA_COUNTERS_FIELD_NAME, COBERTURA_COUNTERS_FIELD_TYPE); 132 mv.visitVarInsn(Opcodes.ALOAD, 0); 133 mv.visitInsn(Opcodes.ARETURN); 134 mv.visitMaxs(0, 0);//will be recalculated by writer 135 mv.visitEnd(); 136 } 137 138}