ProbeVariableInserter.java

    1/*******************************************************************************
    2 * Copyright (c) 2009, 2010 Mountainminds GmbH & Co. KG and Contributors
    3 * All rights reserved. This program and the accompanying materials
    4 * are made available under the terms of the Eclipse Public License v1.0
    5 * which accompanies this distribution, and is available at
    6 * http://www.eclipse.org/legal/epl-v10.html
    7 *
    8 * Contributors:
    9 *    Marc R. Hoffmann - initial API and implementation
   10 *    
   11 *******************************************************************************/
   12package org.jacoco.core.instr;
   13
   14import org.objectweb.asm.Label;
   15import org.objectweb.asm.MethodAdapter;
   16import org.objectweb.asm.MethodVisitor;
   17import org.objectweb.asm.Opcodes;
   18import org.objectweb.asm.Type;
   19
   20/**
   21 * Simplified version of ASM's
   22 * {@link org.objectweb.asm.commons.LocalVariablesSorter} that inserts a local
   23 * variable at the very beginning of the method. This avoids maintaining mapping
   24 * tables and prevents ASM bug #314563.
   25 * 
   26 * @author Marc R. Hoffmann
   27 * @version 0.4.1.20101007204400
   28 */
   29class ProbeVariableInserter extends MethodAdapter {
   30
   31    /** Position of the inserted variable. */
   32    protected final int variable;
   33
   34    /** Index the inserted variable. */
   35    private final int variableIdx;
   36
   37    private boolean firstFrame = true;
   38
   39    /**
   40     * Creates a new {@link ProbeVariableInserter}.
   41     * 
   42     * @param access
   43     *            access flags of the adapted method.
   44     * @param desc
   45     *            the method's descriptor
   46     * @param mv
   47     *            the method visitor to which this adapter delegates calls
   48     */
   49    ProbeVariableInserter(final int access, final String desc,
   50            final MethodVisitor mv) {
   51        super(mv);
   52        int idx = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
   53        int pos = idx;
   54        for (final Type t : Type.getArgumentTypes(desc)) {
   55            idx++;
   56            pos += t.getSize();
   57        }
   58        variableIdx = idx;
   59        variable = pos;
   60    }
   61
   62    @Override
   63    public void visitVarInsn(final int opcode, final int var) {
   64        mv.visitVarInsn(opcode, map(var));
   65    }
   66
   67    @Override
   68    public void visitIincInsn(final int var, final int increment) {
   69        mv.visitIincInsn(map(var), increment);
   70    }
   71
   72    @Override
   73    public void visitMaxs(final int maxStack, final int maxLocals) {
   74        mv.visitMaxs(maxStack, maxLocals + 1);
   75    }
   76
   77    @Override
   78    public void visitLocalVariable(final String name, final String desc,
   79            final String signature, final Label start, final Label end,
   80            final int index) {
   81        mv.visitLocalVariable(name, desc, signature, start, end, map(index));
   82    }
   83
   84    private int map(final int var) {
   85        if (var < variable) {
   86            return var;
   87        } else {
   88            return var + 1;
   89        }
   90    }
   91
   92    @Override
   93    public void visitFrame(final int type, final int nLocal,
   94            final Object[] local, final int nStack, final Object[] stack) {
   95
   96        if (type != Opcodes.F_NEW) { // uncompressed frame
   97            throw new IllegalStateException(
   98                    "ClassReader.accept() should be called with EXPAND_FRAMES flag");
   99        }
  100
  101        if (firstFrame) {
  102            // The first frame is generated by ASM and represents the implicit
  103            // frame derived from the method signature only. This frame must not
  104            // yet be modified.
  105            mv.visitFrame(type, nLocal, local, nStack, stack);
  106            firstFrame = false;
  107            return;
  108        }
  109
  110        final Object[] newLocal = new Object[nLocal + 1];
  111        for (int i = 0; i <= local.length; i++) {
  112            if (i < variableIdx) {
  113                newLocal[i] = local[i];
  114                continue;
  115            }
  116            if (i > variableIdx) {
  117                newLocal[i] = local[i - 1];
  118                continue;
  119            }
  120            newLocal[i] = InstrSupport.DATAFIELD_DESC;
  121        }
  122        mv.visitFrame(type, nLocal + 1, newLocal, nStack, stack);
  123    }
  124
  125}