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.pass1; 027 028import java.util.HashMap; 029import java.util.Map; 030import java.util.concurrent.atomic.AtomicInteger; 031 032import net.sourceforge.cobertura.instrument.AbstractFindTouchPointsClassInstrumenter; 033 034import org.objectweb.asm.ClassVisitor; 035import org.objectweb.asm.MethodVisitor; 036import org.objectweb.asm.Opcodes; 037 038/** 039 * The same line can cause generation of many byte-code blocks connected to the same line. 040 * 041 * This especially occurs in case of 'finally' blocks. For example: 042 * 043 * <pre> 044 * 173: public void methodWithFinishBlock(FinishReturnTypeEnum f){ 045 * 174: try { 046 * 175: switch (f) { 047 * 176: case BY_RETURN: 048 * 177: System.out.println("will return"); 049 * 178: return; 050 * 179: case BY_THROW: 051 * 180: System.out.println("will throw"); 052 * 181: throw new IllegalStateException("Expected exception"); 053 * 182: default: 054 * 183: System.out.println("default"); 055 * 184: } 056 * 185: } finally { 057 * 186: if (f != null) { //This piece of code is generated in ASM 3 times. We should merge it into one block 058 * 187: System.out.println("Finish with: f="+f); 059 * 188: } 060 * 189: } 061 * 190} 062 * </pre> 063 * 064 * effects in generation such a JVM code: 065 * 066 * <pre> 067 * // access flags 1 068 * public methodWithFinishBlock(Ltest/performance/Test1$FinishReturnTypeEnum;)V 069 * TRYCATCHBLOCK L0 L1 L2 070 * TRYCATCHBLOCK L3 L2 L2 071 * L0 072 * LINENUMBER 175 L0 073 * INVOKESTATIC test/performance/Test1.$SWITCH_TABLE$test$performance$Test1$FinishReturnTypeEnum()[I 074 * ALOAD 1 075 * INVOKEVIRTUAL test/performance/Test1$FinishReturnTypeEnum.ordinal()I 076 * IALOAD 077 * TABLESWITCH 078 * 1: L4 079 * 2: L3 080 * default: L5 081 * L4 082 * LINENUMBER 177 L4 083 * GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 084 * LDC "will return" 085 * INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V 086 * L1 087 * LINENUMBER 186 L1 088 * ALOAD 1 089 * IFNULL L6 090 * L7 091 * LINENUMBER 187 L7 092 * GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 093 * NEW java/lang/StringBuilder 094 * DUP 095 * LDC "Finish with: f=" 096 * INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V 097 * ALOAD 1 098 * INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder; 099 * INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; 100 * INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V 101 * L6 102 * LINENUMBER 178 L6 103 * RETURN 104 * L3 105 * LINENUMBER 180 L3 106 * GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 107 * LDC "will throw" 108 * INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V 109 * L8 110 * LINENUMBER 181 L8 111 * NEW java/lang/IllegalStateException 112 * DUP 113 * LDC "Expected exception" 114 * INVOKESPECIAL java/lang/IllegalStateException.<init>(Ljava/lang/String;)V 115 * ATHROW 116 * L5 117 * LINENUMBER 183 L5 118 * GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 119 * LDC "default" 120 * INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V 121 * GOTO L9 122 * L2 123 * LINENUMBER 185 L2 124 * ASTORE 2 125 * L10 126 * LINENUMBER 186 L10 127 * ALOAD 1 128 * IFNULL L11 129 * L12 130 * LINENUMBER 187 L12 131 * GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 132 * NEW java/lang/StringBuilder 133 * DUP 134 * LDC "Finish with: f=" 135 * INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V 136 * ALOAD 1 137 * INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder; 138 * INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; 139 * INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V 140 * L11 141 * LINENUMBER 189 L11 142 * ALOAD 2 143 * ATHROW 144 * L9 145 * LINENUMBER 186 L9 146 * ALOAD 1 147 * IFNULL L13 148 * L14 149 * LINENUMBER 187 L14 150 * GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 151 * NEW java/lang/StringBuilder 152 * DUP 153 * LDC "Finish with: f=" 154 * INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V 155 * ALOAD 1 156 * INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder; 157 * INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; 158 * INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V 159 * L13 160 * LINENUMBER 190 L13 161 * RETURN 162 * L15 163 * LOCALVARIABLE this Ltest/performance/Test1; L0 L15 0 164 * LOCALVARIABLE f Ltest/performance/Test1$FinishReturnTypeEnum; L0 L15 1 165 * MAXSTACK = 4 166 * MAXLOCALS = 3 167 * </pre> 168 * 169 * <p>Note that 'LINENUMBER 186' instruction occurs many times and code after that instruction is nearly identical 170 * (see {@link CodeFootstamp} criteria of 'identity').</p> 171 * 172 * <p>On the other hand duplicated 'LINENUMBER 186' instruction could happened for for example for 'for' loop. In this 173 * case the code after this instruction is different.</p> 174 * 175 * <p>The goal of this class is to provide {@link #duplicatedLinesCollector} that is map of: 176 * (line number -> (duplicated lineId -> origin lineId)).</p> 177 */ 178 179public class DetectDuplicatedCodeClassVisitor extends ClassVisitor{ 180 /** 181 * map of (line number -> (duplicated lineId -> origin lineId)) 182 */ 183 private Map<Integer,Map<Integer,Integer>> duplicatedLinesCollector=new HashMap<Integer, Map<Integer,Integer>>(); 184 185 /** 186 * Name (internal asm) of currently processed class 187 **/ 188 private String className; 189 190 /** 191 * Every LINENUMBER instruction will have generated it's lineId. 192 * 193 * The generated ids must be the same as those generated by ( {@link AbstractFindTouchPointsClassInstrumenter#lineIdGenerator} ) 194 */ 195 private final AtomicInteger lineIdGenerator = new AtomicInteger(0); 196 197 public DetectDuplicatedCodeClassVisitor(ClassVisitor cv) { 198 super(Opcodes.ASM4, cv); 199 } 200 201 @Override 202 public void visit(int version, int access, 203 String name, String signature, String superName, String[] interfaces) { 204 super.visit(version, access, name, signature, superName, interfaces); 205 this.className = name; 206 } 207 208 @Override 209 public MethodVisitor visitMethod(int access, String methodName, String description, 210 String signature, String[] exceptions) { 211 MethodVisitor nestedVisitor=super.visitMethod(access, methodName, description, 212 signature, exceptions); 213 return new DetectDuplicatedCodeMethodVisitor(nestedVisitor, 214 duplicatedLinesCollector, className, methodName, description, lineIdGenerator); 215 } 216 217 /** 218 * Returns map of (line number -> (duplicated lineId -> origin lineId)) 219 */ 220 public Map<Integer, Map<Integer, Integer>> getDuplicatesLinesCollector() { 221 return duplicatedLinesCollector; 222 } 223}