org.jacoco.core.internal.flow.LabelFlowAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.internal.flow.LabelFlowAnalyzer.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2013 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.flow;

import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;

/**
 * Method visitor to collect flow related information about the {@link Label}s
 * within a class. It calculates the properties "multitarget" and "successor"
 * that can afterwards be obtained via {@link LabelInfo}.
 */
public final class LabelFlowAnalyzer extends MethodVisitor {

    /**
     * Marks all labels of the method with control flow information.
     * 
     * @param method
     *            Method to mark labels
     */
    public static void markLabels(final MethodNode method) {
        // We do not use the accept() method as ASM resets labels after every
        // call to accept()
        final MethodVisitor lfa = new LabelFlowAnalyzer();
        if (method.tryCatchBlocks != null) {
            for (int i = method.tryCatchBlocks.size(); --i >= 0;) {
                ((TryCatchBlockNode) method.tryCatchBlocks.get(i)).accept(lfa);
            }
        }
        method.instructions.accept(lfa);
    }

    /**
     * <code>true</code> if the current instruction is a potential successor of
     * the previous instruction. Accessible for testing.
     */
    boolean successor = false;

    /**
     * <code>true</code> for the very first instruction only. Accessible for
     * testing.
     */
    boolean first = true;

    /**
     * Create new instance.
     */
    public LabelFlowAnalyzer() {
        super(Opcodes.ASM4);
    }

    @Override
    public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
        // Enforce probes at the beginning and end of the block:
        LabelInfo.setTarget(start);
        LabelInfo.setTarget(handler);
    }

    @Override
    public void visitJumpInsn(final int opcode, final Label label) {
        LabelInfo.setTarget(label);
        if (opcode == Opcodes.JSR) {
            throw new AssertionError("Subroutines not supported.");
        }
        successor = opcode != Opcodes.GOTO;
        first = false;
    }

    @Override
    public void visitLabel(final Label label) {
        if (first) {
            LabelInfo.setTarget(label);
        }
        if (successor) {
            LabelInfo.setSuccessor(label);
        }
    }

    @Override
    public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) {
        visitSwitchInsn(dflt, labels);
    }

    @Override
    public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
        visitSwitchInsn(dflt, labels);
    }

    private void visitSwitchInsn(final Label dflt, final Label[] labels) {
        LabelInfo.resetDone(dflt);
        LabelInfo.resetDone(labels);
        setTargetIfNotDone(dflt);
        for (final Label l : labels) {
            setTargetIfNotDone(l);
        }
        successor = false;
        first = false;
    }

    private static void setTargetIfNotDone(final Label label) {
        if (!LabelInfo.isDone(label)) {
            LabelInfo.setTarget(label);
            LabelInfo.setDone(label);
        }
    }

    @Override
    public void visitInsn(final int opcode) {
        switch (opcode) {
        case Opcodes.RET:
            throw new AssertionError("Subroutines not supported.");
        case Opcodes.IRETURN:
        case Opcodes.LRETURN:
        case Opcodes.FRETURN:
        case Opcodes.DRETURN:
        case Opcodes.ARETURN:
        case Opcodes.RETURN:
        case Opcodes.ATHROW:
            successor = false;
            break;
        default:
            successor = true;
            break;
        }
        first = false;
    }

    @Override
    public void visitIntInsn(final int opcode, final int operand) {
        successor = true;
        first = false;
    }

    @Override
    public void visitVarInsn(final int opcode, final int var) {
        successor = true;
        first = false;
    }

    @Override
    public void visitTypeInsn(final int opcode, final String type) {
        successor = true;
        first = false;
    }

    @Override
    public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
        successor = true;
        first = false;
    }

    @Override
    public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
        successor = true;
        first = false;
    }

    @Override
    public void visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm,
            final Object... bsmArgs) {
        successor = true;
        first = false;
    }

    @Override
    public void visitLdcInsn(final Object cst) {
        successor = true;
        first = false;
    }

    @Override
    public void visitIincInsn(final int var, final int increment) {
        successor = true;
        first = false;
    }

    @Override
    public void visitMultiANewArrayInsn(final String desc, final int dims) {
        successor = true;
        first = false;
    }

}