org.jacoco.core.internal.instr.ProbeVariableInserter.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.internal.instr.ProbeVariableInserter.java

Source

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

import java.util.HashMap;
import java.util.Map;

import org.jacoco.core.internal.flow.LabelInfo;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.FrameNode;

/**
 * Internal utility to add a local variable for the probe array at the beginning
 * of every method. The adapter adjusts all other local variables and frames
 * accordingly. In addition the adapter records the frames at certain labels to
 * re-insert them if required for jump probes.
 * 
 * Basically this is a simplified version of ASM's
 * {@link org.objectweb.asm.commons.LocalVariablesSorter} to avoid expensive
 * mapping tables and prevents ASM bug #314563.
 */
class ProbeVariableInserter extends MethodAdapter {

    /** Position of the inserted variable. */
    protected final int variable;

    /** Index the inserted variable. */
    private final int variableIdx;

    private boolean firstFrame = true;

    private Label lastLabel;

    private Map<Label, FrameNode> probeFrames;

    /**
     * Creates a new {@link ProbeVariableInserter}.
     * 
     * @param access
     *            access flags of the adapted method.
     * @param desc
     *            the method's descriptor
     * @param mv
     *            the method visitor to which this adapter delegates calls
     */
    ProbeVariableInserter(final int access, final String desc, final MethodVisitor mv) {
        super(mv);
        int idx = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
        int pos = idx;
        for (final Type t : Type.getArgumentTypes(desc)) {
            idx++;
            pos += t.getSize();
        }
        variableIdx = idx;
        variable = pos;
        lastLabel = null;
    }

    @Override
    public void visitLabel(final Label label) {
        mv.visitLabel(label);
        lastLabel = label;
    }

    @Override
    public void visitVarInsn(final int opcode, final int var) {
        mv.visitVarInsn(opcode, map(var));
    }

    @Override
    public void visitIincInsn(final int var, final int increment) {
        mv.visitIincInsn(map(var), increment);
    }

    @Override
    public void visitMaxs(final int maxStack, final int maxLocals) {
        mv.visitMaxs(maxStack, maxLocals + 1);
    }

    @Override
    public void visitLocalVariable(final String name, final String desc, final String signature, final Label start,
            final Label end, final int index) {
        mv.visitLocalVariable(name, desc, signature, start, end, map(index));
    }

    private int map(final int var) {
        if (var < variable) {
            return var;
        } else {
            return var + 1;
        }
    }

    @Override
    public void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack,
            final Object[] stack) {

        if (type != Opcodes.F_NEW) { // uncompressed frame
            throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
        }

        final Object[] newLocal = new Object[nLocal + 1];
        for (int i = 0; i <= local.length; i++) {
            if (i < variableIdx) {
                newLocal[i] = local[i];
                continue;
            }
            if (i > variableIdx) {
                newLocal[i] = local[i - 1];
                continue;
            }
            newLocal[i] = InstrSupport.DATAFIELD_DESC;
        }

        if (lastLabel != null) {
            if (LabelInfo.isMultiTarget(lastLabel)) {
                // Create map instance only if required:
                if (probeFrames == null) {
                    probeFrames = new HashMap<Label, FrameNode>();
                }
                probeFrames.put(lastLabel, new FrameNode(type, nLocal + 1, newLocal, nStack, stack));
            }
            lastLabel = null;
        }

        if (firstFrame) {
            // The first frame is generated by ASM and represents the implicit
            // frame derived from the method signature only. This frame must not
            // yet be modified.
            mv.visitFrame(type, nLocal, local, nStack, stack);
            firstFrame = false;
        } else {
            mv.visitFrame(type, nLocal + 1, newLocal, nStack, stack);
        }
    }

    /**
     * Inserts the frame again that was inserted after the given label.
     * 
     * @param label
     *            label of the frame to insert
     */
    protected void insertProbeFrame(final Label label) {
        if (probeFrames != null) {
            final FrameNode frame = probeFrames.get(label);
            if (frame != null) {
                frame.accept(mv);
            }
        }
    }

}