• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2019 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  *******************************************************************************/
12 package org.jacoco.core.internal.instr;
13 
14 import static java.lang.String.format;
15 
16 import org.objectweb.asm.ClassReader;
17 import org.objectweb.asm.MethodVisitor;
18 import org.objectweb.asm.Opcodes;
19 
20 /**
21  * Constants and utilities for byte code instrumentation.
22  */
23 public final class InstrSupport {
24 
InstrSupport()25 	private InstrSupport() {
26 	}
27 
28 	/** ASM API version */
29 	public static final int ASM_API_VERSION = Opcodes.ASM7;
30 
31 	// === Data Field ===
32 
33 	/**
34 	 * Name of the field that stores coverage information of a class.
35 	 */
36 	public static final String DATAFIELD_NAME = "$jacocoData";
37 
38 	/**
39 	 * Access modifiers of the field that stores coverage information of a
40 	 * class.
41 	 *
42 	 * According to Java Virtual Machine Specification <a href=
43 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.putstatic">
44 	 * §6.5.putstatic</a> this field must not be final:
45 	 *
46 	 * <blockquote>
47 	 * <p>
48 	 * if the field is final, it must be declared in the current class, and the
49 	 * instruction must occur in the {@code <clinit>} method of the current
50 	 * class.
51 	 * </p>
52 	 * </blockquote>
53 	 */
54 	public static final int DATAFIELD_ACC = Opcodes.ACC_SYNTHETIC
55 			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT;
56 
57 	/**
58 	 * Access modifiers of the field that stores coverage information of a Java
59 	 * 8 interface.
60 	 *
61 	 * According to Java Virtual Machine Specification <a href=
62 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5-200-A.3">
63 	 * §4.5</a>:
64 	 *
65 	 * <blockquote>
66 	 * <p>
67 	 * Fields of interfaces must have their ACC_PUBLIC, ACC_STATIC, and
68 	 * ACC_FINAL flags set; they may have their ACC_SYNTHETIC flag set and must
69 	 * not have any of the other flags.
70 	 * </p>
71 	 * </blockquote>
72 	 */
73 	public static final int DATAFIELD_INTF_ACC = Opcodes.ACC_SYNTHETIC
74 			| Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
75 
76 	/**
77 	 * Data type of the field that stores coverage information for a class (
78 	 * <code>boolean[]</code>).
79 	 */
80 	// BEGIN android-change
81 	public static final String DATAFIELD_DESC = "Lorg/jacoco/core/data/IExecutionData;";
82 	// END android-change
83 
84 	// === Init Method ===
85 
86 	/**
87 	 * Name of the initialization method.
88 	 */
89 	public static final String INITMETHOD_NAME = "$jacocoInit";
90 
91 	/**
92 	 * Descriptor of the initialization method.
93 	 */
94 	// BEGIN android-change
95 	public static final String INITMETHOD_DESC = "()Lorg/jacoco/core/data/IExecutionData;";
96 	// END android-change
97 
98 	/**
99 	 * Access modifiers of the initialization method.
100 	 */
101 	public static final int INITMETHOD_ACC = Opcodes.ACC_SYNTHETIC
102 			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
103 
104 	/**
105 	 * Name of the interface initialization method.
106 	 *
107 	 * According to Java Virtual Machine Specification <a href=
108 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9-200">
109 	 * §2.9</a>:
110 	 *
111 	 * <blockquote>
112 	 * <p>
113 	 * A class or interface has at most one class or interface initialization
114 	 * method and is initialized by invoking that method. The initialization
115 	 * method of a class or interface has the special name {@code <clinit>},
116 	 * takes no arguments, and is void.
117 	 * </p>
118 	 * <p>
119 	 * Other methods named {@code <clinit>} in a class file are of no
120 	 * consequence. They are not class or interface initialization methods. They
121 	 * cannot be invoked by any Java Virtual Machine instruction and are never
122 	 * invoked by the Java Virtual Machine itself.
123 	 * </p>
124 	 * <p>
125 	 * In a class file whose version number is 51.0 or above, the method must
126 	 * additionally have its ACC_STATIC flag set in order to be the class or
127 	 * interface initialization method.
128 	 * </p>
129 	 * <p>
130 	 * This requirement was introduced in Java SE 7. In a class file whose
131 	 * version number is 50.0 or below, a method named {@code <clinit>} that is
132 	 * void and takes no arguments is considered the class or interface
133 	 * initialization method regardless of the setting of its ACC_STATIC flag.
134 	 * </p>
135 	 * </blockquote>
136 	 *
137 	 * And <a href=
138 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6-200-A.6">
139 	 * §4.6</a>:
140 	 *
141 	 * <blockquote>
142 	 * <p>
143 	 * Class and interface initialization methods are called implicitly by the
144 	 * Java Virtual Machine. The value of their access_flags item is ignored
145 	 * except for the setting of the ACC_STRICT flag.
146 	 * </p>
147 	 * </blockquote>
148 	 */
149 	static final String CLINIT_NAME = "<clinit>";
150 
151 	/**
152 	 * Descriptor of the interface initialization method.
153 	 *
154 	 * @see #CLINIT_NAME
155 	 */
156 	static final String CLINIT_DESC = "()V";
157 
158 	/**
159 	 * Access flags of the interface initialization method generated by JaCoCo.
160 	 *
161 	 * @see #CLINIT_NAME
162 	 */
163 	static final int CLINIT_ACC = Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC;
164 
165 	/**
166 	 * Gets major version number from given bytes of class (unsigned two bytes
167 	 * at offset 6).
168 	 *
169 	 * @param b
170 	 *            bytes of class
171 	 * @return major version of bytecode
172 	 * @see <a href=
173 	 *      "https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.1">Java
174 	 *      Virtual Machine Specification §4 The class File Format</a>
175 	 * @see #setMajorVersion(int, byte[])
176 	 * @see #getMajorVersion(ClassReader)
177 	 */
getMajorVersion(final byte[] b)178 	public static int getMajorVersion(final byte[] b) {
179 		return ((b[6] & 0xFF) << 8) | (b[7] & 0xFF);
180 	}
181 
182 	/**
183 	 * Sets major version number in given bytes of class (unsigned two bytes at
184 	 * offset 6).
185 	 *
186 	 * @param majorVersion
187 	 *            major version of bytecode to set
188 	 * @param b
189 	 *            bytes of class
190 	 * @see #getMajorVersion(byte[])
191 	 */
setMajorVersion(final int majorVersion, final byte[] b)192 	public static void setMajorVersion(final int majorVersion, final byte[] b) {
193 		b[6] = (byte) (majorVersion >>> 8);
194 		b[7] = (byte) majorVersion;
195 	}
196 
197 	/**
198 	 * Gets major version number from given {@link ClassReader}.
199 	 *
200 	 * @param reader
201 	 *            reader to get information about the class
202 	 * @return major version of bytecode
203 	 * @see ClassReader#ClassReader(byte[], int, int)
204 	 * @see #getMajorVersion(byte[])
205 	 */
getMajorVersion(final ClassReader reader)206 	public static int getMajorVersion(final ClassReader reader) {
207 		// relative to the beginning of constant pool because ASM provides API
208 		// to construct ClassReader which reads from the middle of array
209 		final int firstConstantPoolEntryOffset = reader.getItem(1) - 1;
210 		return reader.readUnsignedShort(firstConstantPoolEntryOffset - 4);
211 	}
212 
213 	/**
214 	 * Determines whether the given class file version requires stackmap frames.
215 	 *
216 	 * @param version
217 	 *            class file version
218 	 * @return <code>true</code> if frames are required
219 	 */
needsFrames(final int version)220 	public static boolean needsFrames(final int version) {
221 		// consider major version only (due to 1.1 anomaly)
222 		return (version & 0xFFFF) >= Opcodes.V1_6;
223 	}
224 
225 	/**
226 	 * Ensures that the given member does not correspond to a internal member
227 	 * created by the instrumentation process. This would mean that the class is
228 	 * already instrumented.
229 	 *
230 	 * @param member
231 	 *            name of the member to check
232 	 * @param owner
233 	 *            name of the class owning the member
234 	 * @throws IllegalStateException
235 	 *             thrown if the member has the same name than the
236 	 *             instrumentation member
237 	 */
assertNotInstrumented(final String member, final String owner)238 	public static void assertNotInstrumented(final String member,
239 			final String owner) throws IllegalStateException {
240 		if (member.equals(DATAFIELD_NAME) || member.equals(INITMETHOD_NAME)) {
241 			throw new IllegalStateException(format(
242 					"Cannot process instrumented class %s. Please supply original non-instrumented classes.",
243 					owner));
244 		}
245 	}
246 
247 	/**
248 	 * Generates the instruction to push the given int value on the stack.
249 	 * Implementation taken from
250 	 * {@link org.objectweb.asm.commons.GeneratorAdapter#push(int)}.
251 	 *
252 	 * @param mv
253 	 *            visitor to emit the instruction
254 	 * @param value
255 	 *            the value to be pushed on the stack.
256 	 */
push(final MethodVisitor mv, final int value)257 	public static void push(final MethodVisitor mv, final int value) {
258 		if (value >= -1 && value <= 5) {
259 			mv.visitInsn(Opcodes.ICONST_0 + value);
260 		} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
261 			mv.visitIntInsn(Opcodes.BIPUSH, value);
262 		} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
263 			mv.visitIntInsn(Opcodes.SIPUSH, value);
264 		} else {
265 			mv.visitLdcInsn(Integer.valueOf(value));
266 		}
267 	}
268 
269 	/**
270 	 * Creates a {@link ClassReader} instance for given bytes of class even if
271 	 * its version not yet supported by ASM.
272 	 *
273 	 * @param b
274 	 *            bytes of class
275 	 * @return {@link ClassReader}
276 	 */
classReaderFor(final byte[] b)277 	public static ClassReader classReaderFor(final byte[] b) {
278 		final int originalVersion = getMajorVersion(b);
279 		if (originalVersion == Opcodes.V12 + 1) {
280 			// temporarily downgrade version to bypass check in ASM
281 			setMajorVersion(Opcodes.V12, b);
282 		}
283 		final ClassReader classReader = new ClassReader(b);
284 		setMajorVersion(originalVersion, b);
285 		return classReader;
286 	}
287 
288 }
289