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.flow;
14 
15 import static org.junit.Assert.assertEquals;
16 
17 import java.util.Arrays;
18 
19 import org.jacoco.core.instr.MethodRecorder;
20 import org.junit.After;
21 import org.junit.Before;
22 import org.junit.Test;
23 import org.objectweb.asm.Label;
24 import org.objectweb.asm.MethodVisitor;
25 import org.objectweb.asm.Opcodes;
26 import org.objectweb.asm.commons.AnalyzerAdapter;
27 import org.objectweb.asm.util.Printer;
28 
29 /**
30  * Unit tests for {@link MethodProbesAdapter}.
31  */
32 public class MethodProbesAdapterTest implements IProbeIdGenerator {
33 
34 	private Label label;
35 
36 	private int id;
37 
38 	private MethodRecorder expected, actual;
39 
40 	private MethodProbesVisitor expectedVisitor;
41 
42 	private MethodVisitor adapter;
43 
44 	private IFrame frame;
45 
46 	private static class TraceAdapter extends MethodProbesVisitor {
47 
48 		private final Printer printer;
49 
TraceAdapter(MethodRecorder recorder)50 		TraceAdapter(MethodRecorder recorder) {
51 			super(recorder.getVisitor());
52 			printer = recorder.getPrinter();
53 		}
54 
55 		@Override
visitProbe(int probeId)56 		public void visitProbe(int probeId) {
57 			rec("visitProbe", Integer.valueOf(probeId));
58 		}
59 
60 		@Override
visitInsnWithProbe(int opcode, int probeId)61 		public void visitInsnWithProbe(int opcode, int probeId) {
62 			rec("visitInsnWithProbe", Integer.valueOf(opcode),
63 					Integer.valueOf(probeId));
64 		}
65 
66 		@Override
visitJumpInsnWithProbe(int opcode, Label label, int probeId, IFrame frame)67 		public void visitJumpInsnWithProbe(int opcode, Label label, int probeId,
68 				IFrame frame) {
69 			rec("visitJumpInsnWithProbe", Integer.valueOf(opcode), label,
70 					Integer.valueOf(probeId));
71 			frame.accept(this);
72 		}
73 
74 		@Override
visitTableSwitchInsnWithProbes(int min, int max, Label dflt, Label[] labels, IFrame frame)75 		public void visitTableSwitchInsnWithProbes(int min, int max, Label dflt,
76 				Label[] labels, IFrame frame) {
77 			rec("visitTableSwitchInsnWithProbes", Integer.valueOf(min),
78 					Integer.valueOf(max), dflt, labels);
79 			frame.accept(this);
80 		}
81 
82 		@Override
visitLookupSwitchInsnWithProbes(Label dflt, int[] keys, Label[] labels, IFrame frame)83 		public void visitLookupSwitchInsnWithProbes(Label dflt, int[] keys,
84 				Label[] labels, IFrame frame) {
85 			rec("visitLookupSwitchInsnWithProbes", dflt, keys, labels);
86 			frame.accept(this);
87 		}
88 
rec(String name, Object... args)89 		private void rec(String name, Object... args) {
90 			printer.text.add(name + Arrays.asList(args));
91 		}
92 
93 	}
94 
95 	@Before
setup()96 	public void setup() {
97 		label = new Label();
98 		id = 1000;
99 		expected = new MethodRecorder();
100 		expectedVisitor = new TraceAdapter(expected);
101 		actual = new MethodRecorder();
102 		MethodProbesVisitor actualVisitor = new TraceAdapter(actual);
103 		MethodProbesAdapter probesAdapter = new MethodProbesAdapter(
104 				actualVisitor, this);
105 		final AnalyzerAdapter analyzer = new AnalyzerAdapter("Foo", 0, "doit",
106 				"()V", probesAdapter);
107 		probesAdapter.setAnalyzer(analyzer);
108 		adapter = analyzer;
109 		frame = new IFrame() {
110 
111 			public void accept(MethodVisitor mv) {
112 			}
113 		};
114 	}
115 
116 	@After
verify()117 	public void verify() {
118 		assertEquals(expected, actual);
119 	}
120 
121 	@Test
testVisitProbe1()122 	public void testVisitProbe1() {
123 		LabelInfo.setTarget(label);
124 		LabelInfo.setSuccessor(label);
125 
126 		adapter.visitLabel(label);
127 
128 		expectedVisitor.visitProbe(1000);
129 		expectedVisitor.visitLabel(label);
130 	}
131 
132 	@Test
testVisitProbe2()133 	public void testVisitProbe2() {
134 		LabelInfo.setTarget(label);
135 		LabelInfo.setTarget(label);
136 
137 		adapter.visitLabel(label);
138 
139 		expectedVisitor.visitLabel(label);
140 	}
141 
142 	@Test
testVisitProbe3()143 	public void testVisitProbe3() {
144 		adapter.visitLabel(label);
145 
146 		expectedVisitor.visitLabel(label);
147 	}
148 
149 	@Test
testVisitInsn1()150 	public void testVisitInsn1() {
151 		adapter.visitInsn(Opcodes.RETURN);
152 
153 		expectedVisitor.visitInsnWithProbe(Opcodes.RETURN, 1000);
154 	}
155 
156 	@Test
testVisitInsn2()157 	public void testVisitInsn2() {
158 		adapter.visitInsn(Opcodes.ICONST_0);
159 		adapter.visitInsn(Opcodes.ICONST_0);
160 		adapter.visitInsn(Opcodes.IADD);
161 
162 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
163 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
164 		expectedVisitor.visitInsn(Opcodes.IADD);
165 	}
166 
167 	@Test
testVisitJumpInsn1()168 	public void testVisitJumpInsn1() {
169 		LabelInfo.setTarget(label);
170 		LabelInfo.setTarget(label);
171 
172 		adapter.visitJumpInsn(Opcodes.GOTO, label);
173 
174 		expectedVisitor.visitJumpInsnWithProbe(Opcodes.GOTO, label, 1000,
175 				frame);
176 		expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 0,
177 				null);
178 	}
179 
180 	@Test
testVisitJumpInsn2()181 	public void testVisitJumpInsn2() {
182 		LabelInfo.setTarget(label);
183 		LabelInfo.setTarget(label);
184 
185 		adapter.visitInsn(Opcodes.ICONST_0);
186 		adapter.visitJumpInsn(Opcodes.IFLT, label);
187 
188 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
189 		expectedVisitor.visitJumpInsnWithProbe(Opcodes.IFLT, label, 1000,
190 				frame);
191 		expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 0,
192 				null);
193 	}
194 
195 	@Test
testVisitJumpInsn3()196 	public void testVisitJumpInsn3() {
197 		adapter.visitInsn(Opcodes.ICONST_0);
198 		adapter.visitJumpInsn(Opcodes.IFLT, label);
199 
200 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
201 		expectedVisitor.visitJumpInsn(Opcodes.IFLT, label);
202 	}
203 
204 	@Test
testVisitJumpInsn4()205 	public void testVisitJumpInsn4() {
206 		LabelInfo.setTarget(label);
207 		LabelInfo.setTarget(label);
208 
209 		adapter.visitInsn(Opcodes.ICONST_0);
210 		adapter.visitInsn(Opcodes.ICONST_0);
211 		adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, label);
212 
213 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
214 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
215 		expectedVisitor.visitJumpInsnWithProbe(Opcodes.IF_ICMPEQ, label, 1000,
216 				frame);
217 		expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 0,
218 				null);
219 	}
220 
221 	@Test
testVisitLookupSwitchInsn1()222 	public void testVisitLookupSwitchInsn1() {
223 		LabelInfo.setTarget(label);
224 		LabelInfo.setTarget(label);
225 
226 		final int[] keys = new int[] { 0, 1 };
227 		final Label[] labels = new Label[] { label, label };
228 		adapter.visitInsn(Opcodes.ICONST_0);
229 		adapter.visitLookupSwitchInsn(label, keys, labels);
230 
231 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
232 		expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels,
233 				frame);
234 		expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 0,
235 				null);
236 		assertEquals(1000, LabelInfo.getProbeId(label));
237 	}
238 
239 	@Test
testVisitLookupSwitchInsn2()240 	public void testVisitLookupSwitchInsn2() {
241 		Label label2 = new Label();
242 		LabelInfo.setTarget(label2);
243 		LabelInfo.setTarget(label2);
244 
245 		final int[] keys = new int[] { 0, 1 };
246 		final Label[] labels = new Label[] { label2, label };
247 		adapter.visitInsn(Opcodes.ICONST_0);
248 		adapter.visitLookupSwitchInsn(label, keys, labels);
249 
250 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
251 		expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels,
252 				frame);
253 		expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 0,
254 				null);
255 		assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label));
256 		assertEquals(1000, LabelInfo.getProbeId(label2));
257 	}
258 
259 	@Test
testVisitLookupSwitchInsn3()260 	public void testVisitLookupSwitchInsn3() {
261 		final int[] keys = new int[] { 0, 1 };
262 		final Label[] labels = new Label[] { label, label };
263 		adapter.visitInsn(Opcodes.ICONST_0);
264 		adapter.visitLookupSwitchInsn(label, keys, labels);
265 
266 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
267 		expectedVisitor.visitLookupSwitchInsn(label, keys, labels);
268 	}
269 
270 	@Test
testVisitTableSwitchInsn1()271 	public void testVisitTableSwitchInsn1() {
272 		LabelInfo.setTarget(label);
273 		LabelInfo.setTarget(label);
274 
275 		final Label[] labels = new Label[] { label, label };
276 		adapter.visitInsn(Opcodes.ICONST_0);
277 		adapter.visitTableSwitchInsn(0, 1, label, labels);
278 
279 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
280 		expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels,
281 				frame);
282 		expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 0,
283 				null);
284 		assertEquals(1000, LabelInfo.getProbeId(label));
285 	}
286 
287 	@Test
testVisitTableSwitchInsn2()288 	public void testVisitTableSwitchInsn2() {
289 		Label label2 = new Label();
290 		LabelInfo.setTarget(label2);
291 		LabelInfo.setTarget(label2);
292 
293 		final Label[] labels = new Label[] { label2, label };
294 		adapter.visitInsn(Opcodes.ICONST_0);
295 		adapter.visitTableSwitchInsn(0, 1, label, labels);
296 
297 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
298 		expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels,
299 				frame);
300 		expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, 0,
301 				null);
302 		assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label));
303 		assertEquals(1000, LabelInfo.getProbeId(label2));
304 	}
305 
306 	@Test
testVisitTableSwitchInsn3()307 	public void testVisitTableSwitchInsn3() {
308 		final Label[] labels = new Label[] { label, label };
309 		adapter.visitInsn(Opcodes.ICONST_0);
310 		adapter.visitTableSwitchInsn(0, 1, label, labels);
311 
312 		expectedVisitor.visitInsn(Opcodes.ICONST_0);
313 		expectedVisitor.visitTableSwitchInsn(0, 1, label, labels);
314 	}
315 
316 	@Test
testVisitTryCatchBlockNoProbe()317 	public void testVisitTryCatchBlockNoProbe() {
318 		Label start = new Label();
319 		Label end = new Label();
320 		Label handler = new Label();
321 
322 		adapter.visitTryCatchBlock(start, end, handler, "java/lang/Exception");
323 		adapter.visitLabel(start);
324 		adapter.visitInsn(Opcodes.NOP);
325 		adapter.visitLabel(end);
326 
327 		expectedVisitor.visitTryCatchBlock(start, end, handler,
328 				"java/lang/Exception");
329 		expectedVisitor.visitLabel(start);
330 		expectedVisitor.visitInsn(Opcodes.NOP);
331 		expectedVisitor.visitLabel(end);
332 	}
333 
334 	@Test
testVisitTryCatchBlockWithProbeBeforeStart()335 	public void testVisitTryCatchBlockWithProbeBeforeStart() {
336 		Label start = new Label();
337 		LabelInfo.setSuccessor(start);
338 		LabelInfo.setTarget(start);
339 		Label end = new Label();
340 		Label handler1 = new Label();
341 		Label handler2 = new Label();
342 
343 		adapter.visitTryCatchBlock(start, end, handler1, "java/lang/Exception");
344 		adapter.visitTryCatchBlock(start, end, handler2, "java/lang/Throwable");
345 		adapter.visitLabel(start);
346 		adapter.visitInsn(Opcodes.NOP);
347 		adapter.visitLabel(end);
348 
349 		Label probe = new Label();
350 		expectedVisitor.visitTryCatchBlock(probe, end, handler1,
351 				"java/lang/Exception");
352 		expectedVisitor.visitTryCatchBlock(probe, end, handler2,
353 				"java/lang/Throwable");
354 		expectedVisitor.visitLabel(probe);
355 		expectedVisitor.visitProbe(1000);
356 		expectedVisitor.visitLabel(start);
357 		expectedVisitor.visitInsn(Opcodes.NOP);
358 		expectedVisitor.visitLabel(end);
359 	}
360 
361 	@Test
testVisitTryCatchBlockWithProbeBeforeEnd()362 	public void testVisitTryCatchBlockWithProbeBeforeEnd() {
363 		Label start = new Label();
364 		Label end = new Label();
365 		LabelInfo.setSuccessor(end);
366 		LabelInfo.setTarget(end);
367 		Label handler1 = new Label();
368 		Label handler2 = new Label();
369 
370 		adapter.visitTryCatchBlock(start, end, handler1, "java/lang/Exception");
371 		adapter.visitTryCatchBlock(start, end, handler2, "java/lang/Throwable");
372 		adapter.visitLabel(start);
373 		adapter.visitInsn(Opcodes.NOP);
374 		adapter.visitLabel(end);
375 
376 		Label probe = new Label();
377 		expectedVisitor.visitTryCatchBlock(start, probe, handler1,
378 				"java/lang/Exception");
379 		expectedVisitor.visitTryCatchBlock(start, probe, handler2,
380 				"java/lang/Throwable");
381 		expectedVisitor.visitLabel(start);
382 		expectedVisitor.visitInsn(Opcodes.NOP);
383 		expectedVisitor.visitLabel(probe);
384 		expectedVisitor.visitProbe(1000);
385 		expectedVisitor.visitLabel(end);
386 	}
387 
388 	/**
389 	 * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-2.html#jvms-2.11.10
390 	 */
391 	@Test
testStructuredLocking()392 	public void testStructuredLocking() {
393 		Label start = new Label();
394 		LabelInfo.setSuccessor(start);
395 		LabelInfo.setTarget(start);
396 		Label end = new Label();
397 		LabelInfo.setSuccessor(end);
398 		LabelInfo.setTarget(end);
399 		Label handlerStart = new Label();
400 		Label handlerEnd = new Label();
401 		Label after = new Label();
402 
403 		adapter.visitTryCatchBlock(start, end, handlerStart, null);
404 		adapter.visitTryCatchBlock(handlerStart, handlerEnd, handlerStart,
405 				null);
406 		adapter.visitVarInsn(Opcodes.ALOAD, 1);
407 		adapter.visitInsn(Opcodes.MONITORENTER);
408 		adapter.visitLabel(start);
409 		adapter.visitInsn(Opcodes.NOP);
410 		adapter.visitVarInsn(Opcodes.ALOAD, 1);
411 		adapter.visitInsn(Opcodes.MONITOREXIT);
412 		adapter.visitLabel(end);
413 		adapter.visitJumpInsn(Opcodes.GOTO, after);
414 		adapter.visitLabel(handlerStart);
415 		adapter.visitVarInsn(Opcodes.ALOAD, 1);
416 		adapter.visitInsn(Opcodes.MONITOREXIT);
417 		adapter.visitLabel(handlerEnd);
418 		adapter.visitInsn(Opcodes.ATHROW);
419 		adapter.visitLabel(after);
420 
421 		Label probe1 = new Label();
422 		Label probe2 = new Label();
423 		expectedVisitor.visitTryCatchBlock(probe1, probe2, handlerStart, null);
424 		expectedVisitor.visitTryCatchBlock(handlerStart, handlerEnd,
425 				handlerStart, null);
426 		expectedVisitor.visitVarInsn(Opcodes.ALOAD, 1);
427 		expectedVisitor.visitInsn(Opcodes.MONITORENTER);
428 		// next probe must be INSIDE range of instructions covered by handler,
429 		// otherwise monitorexit won't be executed
430 		// in case if probe causes exception
431 		expectedVisitor.visitLabel(probe1);
432 		expectedVisitor.visitProbe(1000);
433 		expectedVisitor.visitLabel(start);
434 		expectedVisitor.visitInsn(Opcodes.NOP);
435 		expectedVisitor.visitVarInsn(Opcodes.ALOAD, 1);
436 		expectedVisitor.visitInsn(Opcodes.MONITOREXIT);
437 		// next probe must be OUTSIDE range of instructions covered by handler,
438 		// otherwise monitorexit will be executed second time
439 		// in case if probe causes exception
440 		expectedVisitor.visitLabel(probe2);
441 		expectedVisitor.visitProbe(1001);
442 		expectedVisitor.visitLabel(end);
443 		expectedVisitor.visitJumpInsn(Opcodes.GOTO, after);
444 		expectedVisitor.visitLabel(handlerStart);
445 		expectedVisitor.visitVarInsn(Opcodes.ALOAD, 1);
446 		expectedVisitor.visitInsn(Opcodes.MONITOREXIT);
447 		expectedVisitor.visitLabel(handlerEnd);
448 		expectedVisitor.visitInsnWithProbe(Opcodes.ATHROW, 1002);
449 		expectedVisitor.visitLabel(after);
450 	}
451 
452 	// === IProbeIdGenerator ===
453 
nextId()454 	public int nextId() {
455 		return id++;
456 	}
457 
458 }
459