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