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}