• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2017 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.MethodVisitor;
17 import org.objectweb.asm.Opcodes;
18 
19 /**
20  * Constants and utilities for byte code instrumentation.
21  */
22 public final class InstrSupport {
23 
InstrSupport()24 	private InstrSupport() {
25 	}
26 
27 	/** ASM API version */
28 	public static final int ASM_API_VERSION = Opcodes.ASM5;
29 
30 	// === Data Field ===
31 
32 	/**
33 	 * Name of the field that stores coverage information of a class.
34 	 */
35 	public static final String DATAFIELD_NAME = "$jacocoData";
36 
37 	/**
38 	 * Access modifiers of the field that stores coverage information of a
39 	 * class.
40 	 *
41 	 * According to Java Virtual Machine Specification <a href=
42 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.putstatic">
43 	 * §6.5.putstatic</a> this field must not be final:
44 	 *
45 	 * <blockquote>
46 	 * <p>
47 	 * if the field is final, it must be declared in the current class, and the
48 	 * instruction must occur in the {@code <clinit>} method of the current
49 	 * class.
50 	 * </p>
51 	 * </blockquote>
52 	 */
53 	public static final int DATAFIELD_ACC = Opcodes.ACC_SYNTHETIC
54 			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT;
55 
56 	/**
57 	 * Access modifiers of the field that stores coverage information of a Java
58 	 * 8 interface.
59 	 *
60 	 * According to Java Virtual Machine Specification <a href=
61 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5-200-A.3">
62 	 * §4.5</a>:
63 	 *
64 	 * <blockquote>
65 	 * <p>
66 	 * Fields of interfaces must have their ACC_PUBLIC, ACC_STATIC, and
67 	 * ACC_FINAL flags set; they may have their ACC_SYNTHETIC flag set and must
68 	 * not have any of the other flags.
69 	 * </p>
70 	 * </blockquote>
71 	 */
72 	public static final int DATAFIELD_INTF_ACC = Opcodes.ACC_SYNTHETIC
73 			| Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
74 
75 	/**
76 	 * Data type of the field that stores coverage information for a class (
77 	 * <code>boolean[]</code>).
78 	 */
79 	public static final String DATAFIELD_DESC = "[Z";
80 
81 	// === Init Method ===
82 
83 	/**
84 	 * Name of the initialization method.
85 	 */
86 	public static final String INITMETHOD_NAME = "$jacocoInit";
87 
88 	/**
89 	 * Descriptor of the initialization method.
90 	 */
91 	public static final String INITMETHOD_DESC = "()[Z";
92 
93 	/**
94 	 * Access modifiers of the initialization method.
95 	 */
96 	public static final int INITMETHOD_ACC = Opcodes.ACC_SYNTHETIC
97 			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
98 
99 	/**
100 	 * Name of the interface initialization method.
101 	 *
102 	 * According to Java Virtual Machine Specification <a href=
103 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9-200">
104 	 * §2.9</a>:
105 	 *
106 	 * <blockquote>
107 	 * <p>
108 	 * A class or interface has at most one class or interface initialization
109 	 * method and is initialized by invoking that method. The initialization
110 	 * method of a class or interface has the special name {@code <clinit>},
111 	 * takes no arguments, and is void.
112 	 * </p>
113 	 * <p>
114 	 * Other methods named {@code <clinit>} in a class file are of no
115 	 * consequence. They are not class or interface initialization methods. They
116 	 * cannot be invoked by any Java Virtual Machine instruction and are never
117 	 * invoked by the Java Virtual Machine itself.
118 	 * </p>
119 	 * <p>
120 	 * In a class file whose version number is 51.0 or above, the method must
121 	 * additionally have its ACC_STATIC flag set in order to be the class or
122 	 * interface initialization method.
123 	 * </p>
124 	 * <p>
125 	 * This requirement was introduced in Java SE 7. In a class file whose
126 	 * version number is 50.0 or below, a method named {@code <clinit>} that is
127 	 * void and takes no arguments is considered the class or interface
128 	 * initialization method regardless of the setting of its ACC_STATIC flag.
129 	 * </p>
130 	 * </blockquote>
131 	 *
132 	 * And <a href=
133 	 * "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6-200-A.6">
134 	 * §4.6</a>:
135 	 *
136 	 * <blockquote>
137 	 * <p>
138 	 * Class and interface initialization methods are called implicitly by the
139 	 * Java Virtual Machine. The value of their access_flags item is ignored
140 	 * except for the setting of the ACC_STRICT flag.
141 	 * </p>
142 	 * </blockquote>
143 	 */
144 	static final String CLINIT_NAME = "<clinit>";
145 
146 	/**
147 	 * Descriptor of the interface initialization method.
148 	 *
149 	 * @see #CLINIT_NAME
150 	 */
151 	static final String CLINIT_DESC = "()V";
152 
153 	/**
154 	 * Access flags of the interface initialization method generated by JaCoCo.
155 	 *
156 	 * @see #CLINIT_NAME
157 	 */
158 	static final int CLINIT_ACC = Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC;
159 
160 	/**
161 	 * Ensures that the given member does not correspond to a internal member
162 	 * created by the instrumentation process. This would mean that the class is
163 	 * already instrumented.
164 	 *
165 	 * @param member
166 	 *            name of the member to check
167 	 * @param owner
168 	 *            name of the class owning the member
169 	 * @throws IllegalStateException
170 	 *             thrown if the member has the same name than the
171 	 *             instrumentation member
172 	 */
assertNotInstrumented(final String member, final String owner)173 	public static void assertNotInstrumented(final String member,
174 			final String owner) throws IllegalStateException {
175 		if (member.equals(DATAFIELD_NAME) || member.equals(INITMETHOD_NAME)) {
176 			throw new IllegalStateException(format(
177 					"Class %s is already instrumented.", owner));
178 		}
179 	}
180 
181 	/**
182 	 * Generates the instruction to push the given int value on the stack.
183 	 * Implementation taken from
184 	 * {@link org.objectweb.asm.commons.GeneratorAdapter#push(int)}.
185 	 *
186 	 * @param mv
187 	 *            visitor to emit the instruction
188 	 * @param value
189 	 *            the value to be pushed on the stack.
190 	 */
push(final MethodVisitor mv, final int value)191 	public static void push(final MethodVisitor mv, final int value) {
192 		if (value >= -1 && value <= 5) {
193 			mv.visitInsn(Opcodes.ICONST_0 + value);
194 		} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
195 			mv.visitIntInsn(Opcodes.BIPUSH, value);
196 		} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
197 			mv.visitIntInsn(Opcodes.SIPUSH, value);
198 		} else {
199 			mv.visitLdcInsn(Integer.valueOf(value));
200 		}
201 	}
202 
203 }
204