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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2009, 2015 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 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.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 MethodAnalyzer {

    private final DuaMethodCoverage coverage;
    private final MethodNode methodNode;
    private final String className;
    private final boolean[] probes;
    private final int methodProbeIndex;

    public int duasBBsize;

    /**
     * New Method analyzer for the given probe data.
     * 
     * @param methodId
     *            method Id
     * @param className
     *            class Name
     * @param methodNode
     *            method node
     * 
     * @param probes
     *            recorded probe date of the containing class or
     *            <code>null</code> if the class is not executed at all
     * @param methodProbeIndex
     */
    public MethodAnalyzer(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;
    }

    /**
     * Visits a method of the class.
     */
    public void visit() {
        final DefUseAnalyzer analyzer = getAnalyzer();

        final DefUseChain[] duasIns = new DepthFirstDefUseChainSearch().search(analyzer.getDefUseFrames(),
                analyzer.getVariables(), analyzer.getSuccessors(), analyzer.getPredecessors());

        final DefUseChain[] duasBB = DefUseChain.toBasicBlock(duasIns, analyzer.getLeaders(),
                analyzer.getBasicBlocks());
        duasBBsize = duasBB.length;

        final Variable[] variables = analyzer.getVariables();
        final int[] lines = getLines();

        for (int i = 0; i < duasIns.length; i++) {
            final DefUseChain dua = duasIns[i];
            if (!DefUseChain.isGlobal(dua, analyzer.getLeaders(), analyzer.getBasicBlocks())) {
                continue;
            }

            // def
            final int defLine = lines[dua.def];

            // use
            final int useLine = lines[dua.use];

            // target
            int targetLine = -1;
            if (dua.target != -1) {
                targetLine = lines[dua.target];
            }

            // var
            String varName = getVarName(dua, variables[dua.var]);
            if (varName == null) {
                varName = "random_" + Math.random();
            }

            // status
            final DefUseChain blockChain = toBasicBlock(dua, analyzer.getLeaders());
            final int status = getStatus(ArrayUtils.indexOf(duasBB, blockChain));

            final IDua finalDua = new Dua(i, defLine, useLine, targetLine, varName, status);
            coverage.addDua(finalDua);
        }

    }

    private DefUseAnalyzer getAnalyzer() {
        final DefUseAnalyzer analyzer = new DefUseAnalyzer();
        try {
            analyzer.analyze(className, methodNode);
        } catch (final AnalyzerException e) {
            throw new RuntimeException(e);
        }
        return analyzer;
    }

    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 getVarName(final DefUseChain dua, final Variable var) {
        if (var instanceof Field) {
            return ((Field) var).name;
        } else {
            try {
                return getName(dua.use, ((Local) var).var, methodNode);
            } catch (final Exception e) {
                return null;
            }
        }
    }

    private String getName(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 >= start && insn < end) {
                    return local.name;
                }
            }
        }
        throw new RuntimeException("Variable not found");
    }

    // TODO use new asm-defuse-0.0.4, when it is ready
    private static DefUseChain toBasicBlock(final DefUseChain chain, final int[] leaders) {
        return new DefUseChain(leaders[chain.def], leaders[chain.use],
                chain.isPredicateChain() ? leaders[chain.target] : -1, chain.var);
    }

}