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}