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.Set; 029import java.util.concurrent.atomic.AtomicInteger; 030 031import net.sourceforge.cobertura.CoverageIgnore; 032import net.sourceforge.cobertura.instrument.ContextMethodAwareMethodAdapter; 033 034import org.objectweb.asm.AnnotationVisitor; 035import org.objectweb.asm.Label; 036import org.objectweb.asm.MethodVisitor; 037import org.objectweb.asm.Opcodes; 038import org.objectweb.asm.Type; 039 040public class DetectIgnoredCodeMethodVisitor extends ContextMethodAwareMethodAdapter { 041 final String superName; 042 final Set<Integer> ignoredLineIds; 043 final Set<String> ignoredMethodNamesAndSignatures; 044 045 final Set<String> ignoreMethodAnnotations; 046 final boolean ignoreTrivial; 047 048 enum IgnoredStatus { 049 POSSIBLE_TRIVIAL_GETTER, 050 POSSIBLE_TRIVIAL_SETTER, 051 POSSIBLE_TRIVIAL_INIT, 052 IGNORED_BY_ANNOTATION, 053 NOT_IGNORED; 054 055 boolean isTrivial(){ 056 return (this == POSSIBLE_TRIVIAL_GETTER) 057 || (this == POSSIBLE_TRIVIAL_SETTER) 058 || (this == POSSIBLE_TRIVIAL_INIT); 059 } 060 } 061 062 public IgnoredStatus ignoredStatus; 063 064 public DetectIgnoredCodeMethodVisitor(MethodVisitor mv, 065 Set<Integer> ignoredLineIds, Set<String> ignoredMethodNamesAndSignatures, 066 boolean ignoreTrivial, Set<String> ignoreMethodAnnotations, 067 String className, String superName, String methodName, String description, 068 AtomicInteger lineIdGenerator) { 069 super(mv,className, methodName, description, lineIdGenerator); 070 this.superName = superName; 071 this.ignoredLineIds = ignoredLineIds; 072 this.ignoredMethodNamesAndSignatures = ignoredMethodNamesAndSignatures; 073 this.ignoreTrivial = ignoreTrivial; 074 this.ignoredStatus = checkForTrivialSignature(methodName, description); 075 this.ignoreMethodAnnotations = ignoreMethodAnnotations; 076 } 077 078 private static IgnoredStatus checkForTrivialSignature(String name, String desc) { 079 Type[] args = Type.getArgumentTypes(desc); 080 Type ret = Type.getReturnType(desc); 081 if (name.equals("<init>")) { 082 return IgnoredStatus.POSSIBLE_TRIVIAL_INIT; 083 } 084 085 // a "setter" method must: 086 // - have a name starting with "set" 087 // - take one arguments 088 // - return nothing (void) 089 if (name.startsWith("set") && args.length == 1 && ret.equals(Type.VOID_TYPE)) { 090 return IgnoredStatus.POSSIBLE_TRIVIAL_SETTER; 091 } 092 093 // a "getter" method must: 094 // - have a name starting with "get", "is", or "has" 095 // - take no arguments 096 // - return a value (non-void) 097 if ((name.startsWith("get") || name.startsWith("is") || name.startsWith("has")) && 098 args.length == 0 && !ret.equals(Type.VOID_TYPE)) { 099 return IgnoredStatus.POSSIBLE_TRIVIAL_GETTER; 100 } 101 102 return IgnoredStatus.NOT_IGNORED; 103 } 104 105 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 106 // Check to see if this annotation is one of the ones that we use to 107 // trigger us to ignore this method 108 String clazz = Type.getObjectType(desc.substring(1).replace(';', ' ').trim()).getClassName(); 109 if (ignoreMethodAnnotations.contains(clazz) 110 || desc.equals(Type.getDescriptor(CoverageIgnore.class))){ 111 ignoredStatus = IgnoredStatus.IGNORED_BY_ANNOTATION; 112 } 113 return super.visitAnnotation(desc, visible); 114 } 115 116 @Override 117 public void visitJumpInsn(int arg0, Label arg1) { 118 markNotTrivial(); 119 super.visitJumpInsn(arg0, arg1); 120 } 121 122 public void visitFieldInsn(int opcode, String string, String string1, String string2){ 123 super.visitFieldInsn(opcode, string, string1, string2); 124 if (ignoredStatus.isTrivial()) { 125 // trivial opcodes for accessing class fields are: 126 // - GETFIELD or PUTFIELD 127 if ((ignoredStatus == IgnoredStatus.POSSIBLE_TRIVIAL_GETTER && opcode != Opcodes.GETFIELD) || 128 (ignoredStatus == IgnoredStatus.POSSIBLE_TRIVIAL_SETTER && opcode != Opcodes.PUTFIELD) || 129 (ignoredStatus == IgnoredStatus.POSSIBLE_TRIVIAL_INIT && opcode != Opcodes.PUTFIELD)) { 130 markNotTrivial(); 131 } 132 } 133 } 134 135 public void visitVarInsn(int opcode, int i1) { 136 super.visitVarInsn(opcode, i1); 137 if (ignoredStatus.isTrivial() && 138 opcode != Opcodes.ILOAD && 139 opcode != Opcodes.LLOAD && 140 opcode != Opcodes.FLOAD && 141 opcode != Opcodes.DLOAD && 142 opcode != Opcodes.ALOAD) { 143 markNotTrivial(); 144 } 145 } 146 147 @Override 148 public void visitTypeInsn(int arg0, String arg1) { 149 super.visitTypeInsn(arg0, arg1); 150 markNotTrivial(); 151 } 152 153 @Override 154 public void visitLookupSwitchInsn(Label arg0, int[] arg1, Label[] arg2) { 155 super.visitLookupSwitchInsn(arg0, arg1, arg2); 156 markNotTrivial(); 157 } 158 159 @Override 160 public void visitTableSwitchInsn(int arg0, int arg1, Label arg2, 161 Label[] arg3) { 162 super.visitTableSwitchInsn(arg0, arg1, arg2, arg3); 163 markNotTrivial(); 164 } 165 166 @Override 167 public void visitMultiANewArrayInsn(String arg0, int arg1) { 168 super.visitMultiANewArrayInsn(arg0, arg1); 169 markNotTrivial(); 170 } 171 172 @Override 173 public void visitIincInsn(int arg0, int arg1) { 174 super.visitIincInsn(arg0, arg1); 175 markNotTrivial(); 176 } 177 178 @Override 179 public void visitLdcInsn(Object arg0) { 180 super.visitLdcInsn(arg0); 181 markNotTrivial(); 182 } 183 184 @Override 185 public void visitIntInsn(int arg0, int arg1) { 186 super.visitIntInsn(arg0, arg1); 187 markNotTrivial(); 188 } 189 190 @Override 191 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 192 if (ignoredStatus.isTrivial() && 193 !(ignoredStatus == IgnoredStatus.POSSIBLE_TRIVIAL_INIT 194 && name.equals("<init>") && owner.equals(superName) && opcode == Opcodes.INVOKESPECIAL)) { 195 markNotTrivial(); 196 } 197 super.visitMethodInsn(opcode, owner, name, desc); 198 } 199 200 @Override 201 public void visitEnd() { 202 super.visitEnd(); 203 if ((ignoredStatus == IgnoredStatus.IGNORED_BY_ANNOTATION) 204 || (ignoreTrivial && ignoredStatus.isTrivial())) { 205 ignoredMethodNamesAndSignatures.add(methodName + methodSignature); 206 } 207 } 208 209 public void markNotTrivial(){ 210 if (ignoredStatus.isTrivial()) { 211 ignoredStatus = IgnoredStatus.NOT_IGNORED; 212 } 213 } 214 215}