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}