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.LinkedHashMap; 030import java.util.LinkedList; 031import java.util.List; 032import java.util.Map; 033import java.util.concurrent.atomic.AtomicInteger; 034 035import net.sourceforge.cobertura.instrument.ContextMethodAwareMethodAdapter; 036 037import org.apache.log4j.Logger; 038import org.objectweb.asm.Label; 039import org.objectweb.asm.MethodVisitor; 040 041/** 042 * Detects duplicates in a single analyzed method of ASM code. Applies found information about 043 * duplicates into {@link #duplicatesCollector} structure. 044 * 045 * See {@link DetectDuplicatedCodeClassVisitor} about reasons for the class. 046 * 047 * @author piotr.tabor@gmail.com 048 */ 049public class DetectDuplicatedCodeMethodVisitor extends ContextMethodAwareMethodAdapter{ 050 private final Logger logger=Logger.getLogger(DetectDuplicatedCodeClassVisitor.class); 051 /** 052 * Map of (lineNumber -> (list of pairs: lineId,{@link CodeFootstamp} for the lineId))). 053 */ 054 private final Map<Integer,List<LineIdWithCodeFootstamp>> line2label2codefootstamp=new LinkedHashMap<Integer, List<LineIdWithCodeFootstamp>>(); 055 056 /** 057 * Map of (lineNumber -> (duplicate lineId -> origin lineId)). This structure is filled with a new reasults at the {@link #visitEnd()} method. 058 */ 059 private final Map<Integer,Map<Integer,Integer>> duplicatesCollector; 060 061 /** 062 * Represents pair of lineId and {@link CodeFootstamp} connected with that {@link #lineId} 063 */ 064 private static class LineIdWithCodeFootstamp{ 065 private Integer lineId; 066 private CodeFootstamp footstamp; 067 public LineIdWithCodeFootstamp(Integer lineId, CodeFootstamp footstamp) { 068 super(); 069 this.lineId = lineId; 070 this.footstamp = footstamp; 071 } 072 } 073 074 /** 075 * {@link CodeFootstamp} of currently analyzed block of code. We will append to that variable 076 * events that we will see in the current block of code. 077 */ 078 private CodeFootstamp currentLineFootstamp; 079 080 public DetectDuplicatedCodeMethodVisitor(MethodVisitor mv,Map<Integer,Map<Integer,Integer>> duplicatesCollector, String className, String methodName, String methodSignature,AtomicInteger lineIdGenerator) { 081 super(mv,className, methodName, methodSignature,lineIdGenerator); 082 this.duplicatesCollector=duplicatesCollector; 083 } 084 085//---------------- Visit event and put it into footstamp methods ---------------------- 086 087 /** 088 * <p>Starts a new block and assigns a new {@link #currentLineFootstamp}.</p> 089 * 090 * <p>Put's the new {@link LineIdWithCodeFootstamp} into {@link #line2label2codefootstamp} 091 */ 092 @Override 093 public void visitLineNumber(int lineNumber, Label label) { 094 super.visitLineNumber(lineNumber, label); 095 096 currentLineFootstamp=new CodeFootstamp(); 097 List<LineIdWithCodeFootstamp> footstamps=(line2label2codefootstamp.get(lineNumber)); 098 if(footstamps==null){ 099 footstamps=new LinkedList<LineIdWithCodeFootstamp>(); 100 line2label2codefootstamp.put(lineNumber, footstamps); 101 } 102 footstamps.add(new LineIdWithCodeFootstamp(lastLineId, currentLineFootstamp)); 103 } 104 105 @Override 106 public void visitLabel(Label label) { 107 if (currentLineFootstamp!=null){ 108 currentLineFootstamp.visitLabel(label); 109 } 110 super.visitLabel(label); 111 } 112 113 @Override 114 public void visitFieldInsn(int access, String name, String description, String signature) { 115 if (currentLineFootstamp!=null){ 116 currentLineFootstamp.visitFieldInsn(access,name,description,signature); 117 } 118 super.visitFieldInsn(access, name, description, signature); 119 } 120 121 @Override 122 public void visitInsn(int opCode) { 123 if (currentLineFootstamp!=null){ 124 currentLineFootstamp.visitInsn(opCode); 125 } 126 super.visitInsn(opCode); 127 } 128 129 @Override 130 public void visitIntInsn(int opCode, int variable) { 131 if (currentLineFootstamp!=null){ 132 currentLineFootstamp.visitIntInsn(opCode,variable); 133 } 134 super.visitIntInsn(opCode, variable); 135 } 136 137 @Override 138 public void visitIincInsn(int opCode, int variable) { 139 if (currentLineFootstamp!=null){ 140 currentLineFootstamp.visitIintInsn(opCode,variable); 141 } 142 super.visitIincInsn(opCode, variable); 143 } 144 145 @Override 146 public void visitJumpInsn(int opCode, Label label) { 147 if (currentLineFootstamp!=null){ 148 currentLineFootstamp.visitJumpInsn(opCode,label); 149 } 150 super.visitJumpInsn(opCode, label); 151 } 152 @Override 153 public void visitLdcInsn(Object obj) { 154 if (currentLineFootstamp!=null){ 155 currentLineFootstamp.visitLdcInsn(obj); 156 } 157 super.visitLdcInsn(obj); 158 } 159 160 @Override 161 public void visitMethodInsn(int opCode, String className, String methodName, String description) { 162 if (currentLineFootstamp!=null){ 163 currentLineFootstamp.visitMethodInsn(opCode,className,methodName,description); 164 } 165 super.visitMethodInsn(opCode, className, methodName, description); 166 } 167 @Override 168 public void visitMultiANewArrayInsn(String type, int arg1) { 169 if (currentLineFootstamp!=null){ 170 currentLineFootstamp.visitMultiANewArrayInsn(type,arg1); 171 } 172 super.visitMultiANewArrayInsn(type, arg1); 173 } 174 @Override 175 public void visitLookupSwitchInsn(Label arg0, int[] arg1, Label[] arg2) { 176 if (currentLineFootstamp!=null){ 177 currentLineFootstamp.visitLookupSwitchInsn(arg0,arg1,arg2); 178 } 179 super.visitLookupSwitchInsn(arg0, arg1, arg2); 180 } 181 182 @Override 183 public void visitTableSwitchInsn(int arg0, int arg1, Label arg2,Label[] arg3) { 184 if (currentLineFootstamp!=null){ 185 currentLineFootstamp.visitTableSwitchInsn(arg0,arg1,arg2,arg3); 186 } 187 super.visitTableSwitchInsn(arg0, arg1, arg2, arg3); 188 } 189 190 @Override 191 public void visitEnd() { 192 super.visitEnd(); 193 putDuplicatedLinesIntoMap(duplicatesCollector); 194 } 195 196 /** 197 * Analyzes (compare) all footstamps stored in {@link #line2label2codefootstamp} and add found duplicated 198 * into {@link #duplicatesCollector} 199 */ 200 public void putDuplicatedLinesIntoMap(Map<Integer,Map<Integer,Integer>> res){ 201 for(Map.Entry<Integer, List<LineIdWithCodeFootstamp>> l:line2label2codefootstamp.entrySet()){ 202 Map<Integer,Integer> r=putDuplicates(l.getValue()); 203 if(r!=null){ 204 res.put(l.getKey(), r); 205 } 206 207 if(logger.isDebugEnabled()){ 208 for(LineIdWithCodeFootstamp pair:l.getValue()){ 209 logger.debug("SIGNATURE:"+l.getKey()+":"+pair.lineId+":"+pair.footstamp); 210 } 211 } 212 } 213 } 214 215 /** 216 * <p>Analyzes (compares) a list of duplicates connected to the line with the same number and 217 * add found duplicated into {@link #duplicatesCollector}</p> 218 * 219 * @param listOfFootstamps - list of footstamps connected with a source-code line with the same number 220 * 221 * @return A map (duplicate lineId -> origin lineId ) of duplicates found in the listOfFootstams, or NULL 222 * if no such duplicates has been found. 223 */ 224 private Map<Integer, Integer> putDuplicates(List<LineIdWithCodeFootstamp> listOfFootstamps) { 225 Map<CodeFootstamp,Integer> reversedMap = new HashMap<CodeFootstamp, Integer>(); 226 Map<Integer,Integer> result = new HashMap<Integer, Integer>(); 227 for (LineIdWithCodeFootstamp lcf : listOfFootstamps){ 228 lcf.footstamp.finalize(); 229 if (lcf.footstamp.isMeaningful()) { 230 Integer found = reversedMap.get(lcf.footstamp); 231 if (found != null){ 232 result.put(lcf.lineId, found); 233 } else { 234 reversedMap.put(lcf.footstamp, lcf.lineId); 235 } 236 } 237 } 238 return result.size() > 0 ? result : null; 239 } 240 241}