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}