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