• 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.instr;
14 
15 import static org.junit.Assert.assertEquals;
16 import static org.junit.Assert.assertFalse;
17 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
18 import static org.objectweb.asm.Opcodes.ACC_SUPER;
19 import static org.objectweb.asm.Opcodes.ALOAD;
20 import static org.objectweb.asm.Opcodes.F_NEW;
21 import static org.objectweb.asm.Opcodes.IFEQ;
22 import static org.objectweb.asm.Opcodes.ILOAD;
23 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
24 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
25 import static org.objectweb.asm.Opcodes.POP;
26 import static org.objectweb.asm.Opcodes.RETURN;
27 import static org.objectweb.asm.Opcodes.V10;
28 import static org.objectweb.asm.Opcodes.V11;
29 import static org.objectweb.asm.Opcodes.V12;
30 import static org.objectweb.asm.Opcodes.V13;
31 import static org.objectweb.asm.Opcodes.V14;
32 import static org.objectweb.asm.Opcodes.V15;
33 import static org.objectweb.asm.Opcodes.V16;
34 import static org.objectweb.asm.Opcodes.V1_1;
35 import static org.objectweb.asm.Opcodes.V1_2;
36 import static org.objectweb.asm.Opcodes.V1_3;
37 import static org.objectweb.asm.Opcodes.V1_4;
38 import static org.objectweb.asm.Opcodes.V1_5;
39 import static org.objectweb.asm.Opcodes.V1_6;
40 import static org.objectweb.asm.Opcodes.V1_7;
41 import static org.objectweb.asm.Opcodes.V1_8;
42 import static org.objectweb.asm.Opcodes.V9;
43 
44 import java.io.IOException;
45 
46 import org.jacoco.core.internal.instr.CondyProbeArrayStrategy;
47 import org.jacoco.core.internal.instr.InstrSupport;
48 import org.jacoco.core.runtime.IRuntime;
49 import org.jacoco.core.runtime.SystemPropertiesRuntime;
50 import org.junit.Test;
51 import org.objectweb.asm.ClassVisitor;
52 import org.objectweb.asm.ClassWriter;
53 import org.objectweb.asm.Label;
54 import org.objectweb.asm.MethodVisitor;
55 import org.objectweb.asm.Opcodes;
56 
57 /**
58  * Test class inserted stackmap frames for different class file versions.
59  */
60 public class ClassFileVersionsTest {
61 
62 	@Test
test_1_1()63 	public void test_1_1() throws IOException {
64 		testVersion(V1_1, false);
65 	}
66 
67 	@Test
test_1_2()68 	public void test_1_2() throws IOException {
69 		testVersion(V1_2, false);
70 	}
71 
72 	@Test
test_1_3()73 	public void test_1_3() throws IOException {
74 		testVersion(V1_3, false);
75 	}
76 
77 	@Test
test_1_4()78 	public void test_1_4() throws IOException {
79 		testVersion(V1_4, false);
80 	}
81 
82 	@Test
test_1_5()83 	public void test_1_5() throws IOException {
84 		testVersion(V1_5, false);
85 	}
86 
87 	@Test
test_1_6()88 	public void test_1_6() throws IOException {
89 		testVersion(V1_6, true);
90 	}
91 
92 	@Test
test_1_7()93 	public void test_1_7() throws IOException {
94 		testVersion(V1_7, true);
95 	}
96 
97 	@Test
test_1_8()98 	public void test_1_8() throws IOException {
99 		testVersion(V1_8, true);
100 	}
101 
102 	@Test
test_9()103 	public void test_9() throws IOException {
104 		testVersion(V9, true);
105 	}
106 
107 	@Test
test_10()108 	public void test_10() throws IOException {
109 		testVersion(V10, true);
110 	}
111 
112 	@Test
test_11()113 	public void test_11() throws IOException {
114 		testVersion(V11, true);
115 	}
116 
117 	@Test
test_12()118 	public void test_12() throws IOException {
119 		testVersion(V12, true);
120 	}
121 
122 	@Test
test_13()123 	public void test_13() throws IOException {
124 		testVersion(V13, true);
125 	}
126 
127 	@Test
test_14()128 	public void test_14() throws IOException {
129 		testVersion(V14, true);
130 	}
131 
132 	@Test
test_15()133 	public void test_15() throws IOException {
134 		testVersion(V15, true);
135 	}
136 
137 	@Test
test_16()138 	public void test_16() throws IOException {
139 		testVersion(V16, true);
140 	}
141 
testVersion(int version, boolean frames)142 	private void testVersion(int version, boolean frames) throws IOException {
143 		final byte[] original = createClass(version, frames);
144 
145 		IRuntime runtime = new SystemPropertiesRuntime();
146 		Instrumenter instrumenter = new Instrumenter(runtime);
147 		byte[] instrumented = instrumenter.instrument(original, "TestTarget");
148 
149 		assertFrames(instrumented, frames);
150 	}
151 
assertFrames(byte[] source, final boolean expected)152 	private void assertFrames(byte[] source, final boolean expected) {
153 		InstrSupport.classReaderFor(source)
154 				.accept(new ClassVisitor(InstrSupport.ASM_API_VERSION) {
155 
156 					@Override
157 					public MethodVisitor visitMethod(int access, String name,
158 							final String desc, String signature,
159 							String[] exceptions) {
160 						return new MethodVisitor(InstrSupport.ASM_API_VERSION) {
161 							boolean frames = false;
162 
163 							@Override
164 							public void visitFrame(int type, int nLocal,
165 									Object[] local, int nStack,
166 									Object[] stack) {
167 								frames = true;
168 							}
169 
170 							@Override
171 							public void visitEnd() {
172 								if (CondyProbeArrayStrategy.B_DESC
173 										.equals(desc)) {
174 									assertFalse(
175 											"CondyProbeArrayStrategy does not need frames",
176 											frames);
177 								} else {
178 									assertEquals(Boolean.valueOf(expected),
179 											Boolean.valueOf(frames));
180 								}
181 							}
182 						};
183 					}
184 				}, 0);
185 	}
186 
187 	/**
188 	 * Creates a class that requires a frame before the return statement. Also
189 	 * for this class instrumentation should insert another frame.
190 	 *
191 	 * <code><pre>
192 	 * public class Sample {
193 	 *   public Sample(boolean b){
194 	 *     if(b){
195 	 *       toString();
196 	 *     }
197 	 *     return;
198 	 *   }
199 	 * }
200 	 * </pre></code>
201 	 */
createClass(int version, boolean frames)202 	private byte[] createClass(int version, boolean frames) {
203 
204 		ClassWriter cw = new ClassWriter(0);
205 		MethodVisitor mv;
206 
207 		cw.visit(version, ACC_PUBLIC + ACC_SUPER, "org/jacoco/test/Sample",
208 				null, "java/lang/Object", null);
209 
210 		mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Z)V", null, null);
211 		mv.visitCode();
212 		mv.visitVarInsn(ALOAD, 0);
213 		mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V",
214 				false);
215 		mv.visitVarInsn(ILOAD, 1);
216 		Label l1 = new Label();
217 		mv.visitJumpInsn(IFEQ, l1);
218 		mv.visitVarInsn(ALOAD, 0);
219 		mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString",
220 				"()Ljava/lang/String;", false);
221 		mv.visitInsn(POP);
222 		mv.visitLabel(l1);
223 		if (frames) {
224 			mv.visitFrame(F_NEW, 2,
225 					new Object[] { "org/jacoco/test/Sample", Opcodes.INTEGER },
226 					0, new Object[] {});
227 		}
228 		mv.visitInsn(RETURN);
229 		mv.visitMaxs(1, 2);
230 		mv.visitEnd();
231 
232 		cw.visitEnd();
233 
234 		return cw.toByteArray();
235 	}
236 
237 }
238