• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors
3  * This program and the accompanying materials are made available under
4  * the terms of the Eclipse Public License 2.0 which is available at
5  * http://www.eclipse.org/legal/epl-2.0
6  *
7  * SPDX-License-Identifier: EPL-2.0
8  *
9  * Contributors:
10  *    Marc R. Hoffmann - initial API and implementation
11  *
12  *******************************************************************************/
13 package org.jacoco.core.internal.instr;
14 
15 import org.objectweb.asm.AnnotationVisitor;
16 import org.objectweb.asm.Label;
17 import org.objectweb.asm.MethodVisitor;
18 import org.objectweb.asm.Opcodes;
19 import org.objectweb.asm.Type;
20 import org.objectweb.asm.TypePath;
21 
22 /**
23  * Internal utility to add probes into the control flow of a method. The code
24  * for a probe simply sets a certain slot of a boolean array to true. In
25  * addition the probe array has to be retrieved at the beginning of the method
26  * and stored in a local variable.
27  */
28 class ProbeInserter extends MethodVisitor implements IProbeInserter {
29 
30 	private final IProbeArrayStrategy arrayStrategy;
31 
32 	/**
33 	 * <code>true</code> if method is a class or interface initialization
34 	 * method.
35 	 */
36 	private final boolean clinit;
37 
38 	/** Position of the inserted variable. */
39 	private final int variable;
40 
41 	/** Maximum stack usage of the code to access the probe array. */
42 	private int accessorStackSize;
43 
44 	/**
45 	 * Creates a new {@link ProbeInserter}.
46 	 *
47 	 * @param access
48 	 *            access flags of the adapted method
49 	 * @param name
50 	 *            the method's name
51 	 * @param desc
52 	 *            the method's descriptor
53 	 * @param mv
54 	 *            the method visitor to which this adapter delegates calls
55 	 * @param arrayStrategy
56 	 *            callback to create the code that retrieves the reference to
57 	 *            the probe array
58 	 */
ProbeInserter(final int access, final String name, final String desc, final MethodVisitor mv, final IProbeArrayStrategy arrayStrategy)59 	ProbeInserter(final int access, final String name, final String desc,
60 			final MethodVisitor mv, final IProbeArrayStrategy arrayStrategy) {
61 		super(InstrSupport.ASM_API_VERSION, mv);
62 		this.clinit = InstrSupport.CLINIT_NAME.equals(name);
63 		this.arrayStrategy = arrayStrategy;
64 		int pos = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
65 		for (final Type t : Type.getArgumentTypes(desc)) {
66 			pos += t.getSize();
67 		}
68 		variable = pos;
69 	}
70 
insertProbe(final int id)71 	public void insertProbe(final int id) {
72 
73 		// BEGIN android-change
74 		// For a probe we call setProbe on the IExecutionData object.
75 
76 		mv.visitVarInsn(Opcodes.ALOAD, variable);
77 
78 		// Stack[0]: Lorg/jacoco/core/data/IExecutionData;
79 
80 		InstrSupport.push(mv, id);
81 
82 		// Stack[1]: I
83 		// Stack[0]: Lorg/jacoco/core/data/IExecutionData;
84 
85 		mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "org/jacoco/core/data/IExecutionData", "setProbe", "(I)V", true);
86 		// END android-change
87 	}
88 
89 	@Override
visitCode()90 	public void visitCode() {
91 		accessorStackSize = arrayStrategy.storeInstance(mv, clinit, variable);
92 		mv.visitCode();
93 	}
94 
95 	@Override
visitVarInsn(final int opcode, final int var)96 	public final void visitVarInsn(final int opcode, final int var) {
97 		mv.visitVarInsn(opcode, map(var));
98 	}
99 
100 	@Override
visitIincInsn(final int var, final int increment)101 	public final void visitIincInsn(final int var, final int increment) {
102 		mv.visitIincInsn(map(var), increment);
103 	}
104 
105 	@Override
visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index)106 	public final void visitLocalVariable(final String name, final String desc,
107 			final String signature, final Label start, final Label end,
108 			final int index) {
109 		mv.visitLocalVariable(name, desc, signature, start, end, map(index));
110 	}
111 
112 	@Override
visitLocalVariableAnnotation(final int typeRef, final TypePath typePath, final Label[] start, final Label[] end, final int[] index, final String descriptor, final boolean visible)113 	public AnnotationVisitor visitLocalVariableAnnotation(final int typeRef,
114 			final TypePath typePath, final Label[] start, final Label[] end,
115 			final int[] index, final String descriptor, final boolean visible) {
116 		final int[] newIndex = new int[index.length];
117 		for (int i = 0; i < newIndex.length; i++) {
118 			newIndex[i] = map(index[i]);
119 		}
120 		return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end,
121 				newIndex, descriptor, visible);
122 	}
123 
124 	@Override
visitMaxs(final int maxStack, final int maxLocals)125 	public void visitMaxs(final int maxStack, final int maxLocals) {
126 		// Max stack size of the probe code is 3 which can add to the
127 		// original stack size depending on the probe locations. The accessor
128 		// stack size is an absolute maximum, as the accessor code is inserted
129 		// at the very beginning of each method when the stack size is empty.
130 		final int increasedStack = Math.max(maxStack + 3, accessorStackSize);
131 		mv.visitMaxs(increasedStack, maxLocals + 1);
132 	}
133 
map(final int var)134 	private int map(final int var) {
135 		if (var < variable) {
136 			return var;
137 		} else {
138 			return var + 1;
139 		}
140 	}
141 
142 	@Override
visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack)143 	public final void visitFrame(final int type, final int nLocal,
144 			final Object[] local, final int nStack, final Object[] stack) {
145 
146 		if (type != Opcodes.F_NEW) { // uncompressed frame
147 			throw new IllegalArgumentException(
148 					"ClassReader.accept() should be called with EXPAND_FRAMES flag");
149 		}
150 
151 		final Object[] newLocal = new Object[Math.max(nLocal, variable) + 1];
152 		int idx = 0; // Arrays index for existing locals
153 		int newIdx = 0; // Array index for new locals
154 		int pos = 0; // Current variable position
155 		while (idx < nLocal || pos <= variable) {
156 			if (pos == variable) {
157 				newLocal[newIdx++] = InstrSupport.DATAFIELD_DESC;
158 				pos++;
159 			} else {
160 				if (idx < nLocal) {
161 					final Object t = local[idx++];
162 					newLocal[newIdx++] = t;
163 					pos++;
164 					if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {
165 						pos++;
166 					}
167 				} else {
168 					// Fill unused slots with TOP
169 					newLocal[newIdx++] = Opcodes.TOP;
170 					pos++;
171 				}
172 			}
173 		}
174 		mv.visitFrame(type, newIdx, newLocal, nStack, stack);
175 	}
176 
177 }
178