org.jacoco.core.internal.analysis.dua.DuaMethodAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.internal.analysis.dua.DuaMethodAnalyzer.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2014 Mountainminds GmbH & Co. KG and Contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Marc R. Hoffmann - initial API and implementation
 *    
 *******************************************************************************/
package org.jacoco.core.internal.analysis.dua;

import java.util.HashMap;
import java.util.List;

import org.jacoco.core.analysis.ICounter;
import org.jacoco.core.analysis.dua.Dua;
import org.jacoco.core.analysis.dua.DuaMethodCoverage;
import org.jacoco.core.analysis.dua.IDua;
import org.jacoco.core.internal.flow.MethodProbesVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;

import br.usp.each.saeg.asm.defuse.DefUseAnalyzer;
import br.usp.each.saeg.asm.defuse.DefUseChain;
import br.usp.each.saeg.asm.defuse.DefUseFrame;
import br.usp.each.saeg.asm.defuse.DepthFirstDefUseChainSearch;
import br.usp.each.saeg.asm.defuse.Field;
import br.usp.each.saeg.asm.defuse.Local;
import br.usp.each.saeg.asm.defuse.Variable;
import br.usp.each.saeg.commons.ArrayUtils;

/**
 * A {@link MethodProbesVisitor} that analyzes which statements and branches of
 * a method has been executed based on given probe data.
 */
public class DuaMethodAnalyzer {

    private final DuaMethodCoverage coverage;
    private final MethodNode methodNode;

    private final String className;
    private final int methodProbeIndex;
    private final boolean[] probes;
    private DefUseChain[] duaI;
    private int[][] basicBlocks;
    private int[] leaders;
    private Variable[] variables;

    /**
     * New Method analyzer for the given probe data.
     * 
     * @param methodId
     * @param className
     * 
     * @param name
     *            method name
     * @param desc
     *            description of the method
     * @param signature
     *            optional parameterized signature
     * 
     * @param probes
     *            recorded probe date of the containing class or
     *            <code>null</code> if the class is not executed at all
     * @param methodProbeIndex
     */
    public DuaMethodAnalyzer(final int methodId, final String className, final MethodNode methodNode,
            final boolean[] probes, final int methodProbeIndex) {
        super();
        this.className = className;
        this.methodNode = methodNode;
        this.probes = probes;
        this.methodProbeIndex = methodProbeIndex;
        this.coverage = new DuaMethodCoverage(methodId, methodNode.name, methodNode.desc, methodNode.signature,
                ((methodNode.access & Opcodes.ACC_STATIC) != 0));
    }

    /**
     * Returns the coverage data for this method after this visitor has been
     * processed.
     * 
     * @return coverage data for this method
     */
    public DuaMethodCoverage getCoverage() {
        return coverage;
    }

    public void analyze() {
        List<LocalVariableNode> localVariables = methodNode.localVariables;

        //put in the hashmap the name of the source variables method
        HashMap<Integer, String> variables = new HashMap<Integer, String>();
        for (LocalVariableNode var : localVariables)
            variables.put(var.index, var.name);

        final int[] lines = getLines();
        transform(methodNode);
        int indexDua = 0;
        for (DefUseChain defUseChain : duaI) {
            DefUseChain bbchain = toBB(defUseChain); // transform given defusechain to BasicBlock
            if (bbchain != null) {
                int defLine = lines[defUseChain.def];
                int useLine = lines[defUseChain.use];
                int targetLines = -1;
                if (defUseChain.target != -1) {
                    targetLines = lines[defUseChain.target];
                }
                String varName = getName(defUseChain);
                if (varName != null) { // ignoring case of duas created by the compiler
                    int status = getStatus(indexDua);
                    IDua dua = new Dua(defLine, useLine, targetLines, varName, status);
                    coverage.addDua(dua);
                }
                indexDua++;

            }
        }
    }

    private DefUseChain toBB(DefUseChain c) {
        if (DefUseChain.isGlobal(c, leaders, basicBlocks)) {
            return new DefUseChain(leaders[c.def], leaders[c.use], c.target == -1 ? -1 : leaders[c.target], c.var);
        }
        return null;
    }

    private int getStatus(final int i) {
        int status = ICounter.NOT_COVERED;
        if (probes[methodProbeIndex + i]) {
            status = ICounter.FULLY_COVERED;
        }
        return status;
    }

    private int[] getLines() {
        final int[] lines = new int[methodNode.instructions.size()];
        for (int i = 0; i < lines.length; i++) {
            if (methodNode.instructions.get(i) instanceof LineNumberNode) {
                final LineNumberNode insn = (LineNumberNode) methodNode.instructions.get(i);
                lines[methodNode.instructions.indexOf(insn.start)] = insn.line;
            }
        }

        int line = 1;
        for (int i = 0; i < lines.length; i++) {
            if (lines[i] == 0) {
                lines[i] = line;
            } else {
                line = lines[i];
            }
        }
        return lines;
    }

    private String getName(final DefUseChain dua) {
        final Variable var = variables[dua.var];
        String name;
        if (var instanceof Field) {
            name = ((Field) var).name;
        } else {
            try {
                name = varName(dua.use, ((Local) var).var, methodNode);
            } catch (final Exception e) {
                name = null;
            }
        }

        return name;
    }

    private String varName(final int insn, final int index, final MethodNode mn) {
        for (final LocalVariableNode local : mn.localVariables) {
            if (local.index == index) {
                final int start = mn.instructions.indexOf(local.start);
                final int end = mn.instructions.indexOf(local.end);
                if (insn + 1 >= start && insn + 1 <= end) {
                    return local.name;
                }
            }
        }
        throw new RuntimeException("Variable not found");
    }

    private DefUseChain[] transform(final MethodNode methodNode) {
        final DefUseAnalyzer analyzer = new DefUseAnalyzer();
        try {
            analyzer.analyze(className, methodNode);
        } catch (final AnalyzerException e) {
            throw new RuntimeException(e);
        }

        final DefUseFrame[] frames = analyzer.getDefUseFrames();
        variables = analyzer.getVariables();
        final int[][] successors = analyzer.getSuccessors();
        final int[][] predecessors = analyzer.getPredecessors();
        basicBlocks = analyzer.getBasicBlocks();
        leaders = analyzer.getLeaders();
        //defuse with instructions
        duaI = new DepthFirstDefUseChainSearch().search(frames, variables, successors, predecessors);
        //defuse with basic blocks
        final DefUseChain[] chains = DefUseChain.toBasicBlock(
                new DepthFirstDefUseChainSearch().search(frames, variables, successors, predecessors), leaders,
                basicBlocks);

        return chains;
    }
}