com.github.leeyazhou.cobertura.instrument.pass1.DetectDuplicatedCodeClassVisitor Maven / Gradle / Ivy
Show all versions of cobertura Show documentation
/*
* Cobertura - http://cobertura.sourceforge.net/
*
* Copyright (C) 2011 Piotr Tabor
*
* Note: This file is dual licensed under the GPL and the Apache
* Source License (so that it can be used from both the main
* Cobertura classes and the ant tasks).
*
* Cobertura is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Cobertura is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cobertura; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
package com.github.leeyazhou.cobertura.instrument.pass1;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.CheckClassAdapter;
import com.github.leeyazhou.cobertura.instrument.AbstractFindTouchPointsClassInstrumenter;
/**
* The same line can cause generation of many byte-code blocks connected to the
* same line.
*
* This especially occurs in case of 'finally' blocks. For example:
*
*
*
* 173: public void methodWithFinishBlock(FinishReturnTypeEnum f){
* 174: try {
* 175: switch (f) {
* 176: case BY_RETURN:
* 177: System.out.println("will return");
* 178: return;
* 179: case BY_THROW:
* 180: System.out.println("will throw");
* 181: throw new IllegalStateException("Expected exception");
* 182: default:
* 183: System.out.println("default");
* 184: }
* 185: } finally {
* 186: if (f != null) { //This piece of code is generated in ASM 3 times. We should merge it into one block
* 187: System.out.println("Finish with: f="+f);
* 188: }
* 189: }
* 190}
*
*
* effects in generation such a JVM code:
*
*
*
* // access flags 1
* public methodWithFinishBlock(Ltest/performance/Test1$FinishReturnTypeEnum;)V
* TRYCATCHBLOCK L0 L1 L2
* TRYCATCHBLOCK L3 L2 L2
* L0
* LINENUMBER 175 L0
* INVOKESTATIC test/performance/Test1.$SWITCH_TABLE$test$performance$Test1$FinishReturnTypeEnum()[I
* ALOAD 1
* INVOKEVIRTUAL test/performance/Test1$FinishReturnTypeEnum.ordinal()I
* IALOAD
* TABLESWITCH
* 1: L4
* 2: L3
* default: L5
* L4
* LINENUMBER 177 L4
* GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
* LDC "will return"
* INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
* L1
* LINENUMBER 186 L1
* ALOAD 1
* IFNULL L6
* L7
* LINENUMBER 187 L7
* GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
* NEW java/lang/StringBuilder
* DUP
* LDC "Finish with: f="
* INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
* ALOAD 1
* INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
* INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
* INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
* L6
* LINENUMBER 178 L6
* RETURN
* L3
* LINENUMBER 180 L3
* GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
* LDC "will throw"
* INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
* L8
* LINENUMBER 181 L8
* NEW java/lang/IllegalStateException
* DUP
* LDC "Expected exception"
* INVOKESPECIAL java/lang/IllegalStateException.<init>(Ljava/lang/String;)V
* ATHROW
* L5
* LINENUMBER 183 L5
* GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
* LDC "default"
* INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
* GOTO L9
* L2
* LINENUMBER 185 L2
* ASTORE 2
* L10
* LINENUMBER 186 L10
* ALOAD 1
* IFNULL L11
* L12
* LINENUMBER 187 L12
* GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
* NEW java/lang/StringBuilder
* DUP
* LDC "Finish with: f="
* INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
* ALOAD 1
* INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
* INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
* INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
* L11
* LINENUMBER 189 L11
* ALOAD 2
* ATHROW
* L9
* LINENUMBER 186 L9
* ALOAD 1
* IFNULL L13
* L14
* LINENUMBER 187 L14
* GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
* NEW java/lang/StringBuilder
* DUP
* LDC "Finish with: f="
* INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
* ALOAD 1
* INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
* INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
* INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
* L13
* LINENUMBER 190 L13
* RETURN
* L15
* LOCALVARIABLE this Ltest/performance/Test1; L0 L15 0
* LOCALVARIABLE f Ltest/performance/Test1$FinishReturnTypeEnum; L0 L15 1
* MAXSTACK = 4
* MAXLOCALS = 3
*
*
*
* Note that 'LINENUMBER 186' instruction occurs many times and code after that
* instruction is nearly identical (see {@link CodeFootstamp} criteria of
* 'identity').
*
*
*
* On the other hand duplicated 'LINENUMBER 186' instruction could happened for
* for example for 'for' loop. In this case the code after this instruction is
* different.
*
*
*
* The goal of this class is to provide {@link #duplicatedLinesCollector} that
* is map of: (line number -> (duplicated lineId -> origin lineId)).
*
*/
public class DetectDuplicatedCodeClassVisitor extends ClassVisitor {
/**
* map of (line number -> (duplicated lineId -> origin lineId))
*/
private Map> duplicatedLinesCollector = new HashMap>();
/**
* Name (internal asm) of currently processed class
*/
private String className;
/**
* Every LINENUMBER instruction will have generated it's lineId.
*
* The generated ids must be the same as those generated by (
* {@link AbstractFindTouchPointsClassInstrumenter#lineIdGenerator} )
*/
private final AtomicInteger lineIdGenerator = new AtomicInteger(0);
public DetectDuplicatedCodeClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, new CheckClassAdapter(cv, false));
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.className = name;
}
@Override
public MethodVisitor visitMethod(int access, String methodName, String description, String signature,
String[] exceptions) {
MethodVisitor nestedVisitor = super.visitMethod(access, methodName, description, signature, exceptions);
return new DetectDuplicatedCodeMethodVisitor(nestedVisitor, duplicatedLinesCollector, className, methodName,
description, lineIdGenerator);
}
/**
* @return Returns map of (line number -> (duplicated lineId -> origin
* lineId))
*/
public Map> getDuplicatesLinesCollector() {
return duplicatedLinesCollector;
}
}