diff options
author | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-12-30 20:49:49 +0100 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-12-30 20:49:49 +0100 |
commit | 8f81097f70a2b9c570e225dec4dcf3a28b988efc (patch) | |
tree | 71d4f760edff3e8cf481728c049bd80af73431f5 | |
parent | 39deeac4b0f6b5daf2ecdac971fe02ddc945ad67 (diff) | |
download | jacoco-8f81097f70a2b9c570e225dec4dcf3a28b988efc.tar.gz |
Replace our FrameTracker with existing ASM API AnalyzerAdapter.
17 files changed, 556 insertions, 2369 deletions
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodAnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodAnalyzerTest.java index 76ad6d03..3cf3b415 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodAnalyzerTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/MethodAnalyzerTest.java @@ -556,7 +556,9 @@ public class MethodAnalyzerTest implements IProbeIdGenerator { LabelFlowAnalyzer.markLabels(method); final MethodAnalyzer analyzer = new MethodAnalyzer("doit", "()V", null, probes); - method.accept(new MethodProbesAdapter(analyzer, this)); + final MethodProbesAdapter probesAdapter = new MethodProbesAdapter( + analyzer, this); + method.accept(probesAdapter); result = analyzer.getCoverage(); } diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/ClassProbesAdapterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/ClassProbesAdapterTest.java index 2d5bad55..fde6ecb9 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/ClassProbesAdapterTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/ClassProbesAdapterTest.java @@ -12,6 +12,8 @@ package org.jacoco.core.internal.flow; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import org.junit.Test; import org.objectweb.asm.ClassVisitor; @@ -24,7 +26,44 @@ import org.objectweb.asm.Opcodes; */ public class ClassProbesAdapterTest { - private static class MockVisitor extends ClassProbesVisitor { + private static class MockMethodVisitor extends MethodProbesVisitor { + + boolean frame = false; + + @Override + public void visitProbe(int probeId) { + } + + @Override + public void visitJumpInsnWithProbe(int opcode, Label label, + int probeId, IFrame frame) { + frame.accept(this); + } + + @Override + public void visitInsnWithProbe(int opcode, int probeId) { + } + + @Override + public void visitTableSwitchInsnWithProbes(int min, int max, + Label dflt, Label[] labels, IFrame frame) { + frame.accept(this); + } + + @Override + public void visitLookupSwitchInsnWithProbes(Label dflt, int[] keys, + Label[] labels, IFrame frame) { + frame.accept(this); + } + + @Override + public void visitFrame(int type, int nLocal, Object[] local, + int nStack, Object[] stack) { + frame = true; + } + } + + private static class MockClassVisitor extends ClassProbesVisitor { int count; @@ -42,118 +81,147 @@ public class ClassProbesAdapterTest { @Test public void testProbeCounter() { - final MockVisitor mv = new MockVisitor(); - final ClassProbesAdapter adapter = new ClassProbesAdapter(mv); + final MockClassVisitor cv = new MockClassVisitor(); + final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false); assertEquals(0, adapter.nextId()); assertEquals(1, adapter.nextId()); assertEquals(2, adapter.nextId()); adapter.visitEnd(); - assertEquals(3, mv.count); + assertEquals(3, cv.count); } @Test public void testVisitClassMethods() { - final MockVisitor mv = new MockVisitor() { + final MockClassVisitor cv = new MockClassVisitor() { @Override public MethodProbesVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - class MockMethodVisitor extends MethodProbesVisitor { - @Override - public void visitProbe(int probeId) { - } - - @Override - public void visitJumpInsnWithProbe(int opcode, Label label, - int probeId) { - } - - @Override - public void visitInsnWithProbe(int opcode, int probeId) { - } - - @Override - public void visitTableSwitchInsnWithProbes(int min, - int max, Label dflt, Label[] labels) { - } - - @Override - public void visitLookupSwitchInsnWithProbes(Label dflt, - int[] keys, Label[] labels) { - } - } return new MockMethodVisitor(); } }; - final ClassProbesAdapter adapter = new ClassProbesAdapter(mv); + final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false); adapter.visit(Opcodes.V1_5, 0, "Foo", null, "java/lang/Object", null); writeMethod(adapter); writeMethod(adapter); writeMethod(adapter); - assertEquals(0, mv.count); + assertEquals(0, cv.count); adapter.visitEnd(); - assertEquals(3, mv.count); + assertEquals(3, cv.count); } @Test public void testVisitInterfaceMethod() { - final MockVisitor mv = new MockVisitor() { + final MockClassVisitor cv = new MockClassVisitor() { @Override public MethodProbesVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - class MockMethodVisitor extends MethodProbesVisitor { - @Override - public void visitProbe(int probeId) { - } - - @Override - public void visitJumpInsnWithProbe(int opcode, Label label, - int probeId) { - } - - @Override - public void visitInsnWithProbe(int opcode, int probeId) { - } - - @Override - public void visitTableSwitchInsnWithProbes(int min, - int max, Label dflt, Label[] labels) { - } - - @Override - public void visitLookupSwitchInsnWithProbes(Label dflt, - int[] keys, Label[] labels) { - } - } return new MockMethodVisitor(); } }; - final ClassProbesAdapter adapter = new ClassProbesAdapter(mv); + final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false); adapter.visit(Opcodes.V1_5, Opcodes.ACC_INTERFACE, "Foo", null, "java/lang/Object", null); writeMethod(adapter); - assertEquals(1, mv.count); + assertEquals(1, cv.count); adapter.visitEnd(); - assertEquals(1, mv.count); + assertEquals(1, cv.count); } @Test public void testVisitMethodNullMethodVisitor() { - final MockVisitor mv = new MockVisitor(); - final ClassProbesAdapter adapter = new ClassProbesAdapter(mv); - writeMethod(adapter); - writeMethod(adapter); - writeMethod(adapter); + final MockClassVisitor cv = new MockClassVisitor(); + final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false); + writeMethod(adapter); // 1 probe + writeMethodWithBranch(adapter); // 3 probes + writeMethodWithTableSwitch(adapter); // 3 probes + writeMethodWithLookupSwitch(adapter); // 3 probes + adapter.visitEnd(); + assertEquals(10, cv.count); + } + + @Test + public void testVisitWithFrames() { + final MockMethodVisitor mv = new MockMethodVisitor(); + final MockClassVisitor cv = new MockClassVisitor() { + @Override + public MethodProbesVisitor visitMethod(int access, String name, + String desc, String signature, String[] exceptions) { + return mv; + } + }; + final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, true); + writeMethodWithBranch(adapter); adapter.visitEnd(); - assertEquals(3, mv.count); + assertTrue(mv.frame); + } + + @Test + public void testVisitWithoutFrames() { + final MockMethodVisitor mv = new MockMethodVisitor(); + final MockClassVisitor cv = new MockClassVisitor() { + @Override + public MethodProbesVisitor visitMethod(int access, String name, + String desc, String signature, String[] exceptions) { + return mv; + } + }; + final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false); + writeMethodWithBranch(adapter); + adapter.visitEnd(); + assertFalse(mv.frame); } private void writeMethod(final ClassVisitor cv) { - MethodVisitor mv = cv.visitMethod(0, "foo", "V()", null, null); + MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null); mv.visitCode(); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(0, 1); mv.visitEnd(); } + + private void writeMethodWithBranch(final ClassVisitor cv) { + MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null); + mv.visitCode(); + mv.visitInsn(Opcodes.ICONST_0); + Label l = new Label(); + mv.visitJumpInsn(Opcodes.IFEQ, l); + mv.visitInsn(Opcodes.NOP); + mv.visitLabel(l); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + private void writeMethodWithTableSwitch(final ClassVisitor cv) { + MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null); + mv.visitCode(); + mv.visitInsn(Opcodes.ICONST_0); + Label l1 = new Label(); + Label l2 = new Label(); + mv.visitTableSwitchInsn(0, 0, l1, new Label[] { l2 }); + mv.visitLabel(l1); + mv.visitInsn(Opcodes.NOP); + mv.visitLabel(l2); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + private void writeMethodWithLookupSwitch(final ClassVisitor cv) { + MethodVisitor mv = cv.visitMethod(0, "foo", "()V", null, null); + mv.visitCode(); + mv.visitInsn(Opcodes.ICONST_0); + Label l1 = new Label(); + Label l2 = new Label(); + mv.visitLookupSwitchInsn(l1, new int[] { 0 }, new Label[] { l2 }); + mv.visitLabel(l1); + mv.visitInsn(Opcodes.NOP); + mv.visitLabel(l2); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + } diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/FrameSnapshotTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/FrameSnapshotTest.java new file mode 100644 index 00000000..210c0d95 --- /dev/null +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/FrameSnapshotTest.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.core.internal.flow; + +import static org.junit.Assert.assertEquals; + +import org.jacoco.core.instr.MethodRecorder; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.AnalyzerAdapter; + +/** + * Unit tests for {@link FrameSnapshot}. + */ +public class FrameSnapshotTest { + + private AnalyzerAdapter analyzer; + private IFrame frame; + + private MethodRecorder expected; + + private MethodVisitor expectedVisitor; + + @Before + public void setup() { + analyzer = new AnalyzerAdapter("Foo", 0, "doit", "()V", null); + expected = new MethodRecorder(); + expectedVisitor = expected.getVisitor(); + } + + @After + public void teardown() { + MethodRecorder actual = new MethodRecorder(); + frame.accept(actual.getVisitor()); + assertEquals(expected, actual); + } + + @Test + public void testNullAnalyzer() { + frame = FrameSnapshot.create(null, 0); + } + + @Test + public void testNoFrame() { + analyzer.visitJumpInsn(Opcodes.GOTO, new Label()); + frame = FrameSnapshot.create(analyzer, 0); + } + + @Test + public void testFrame() { + analyzer.visitInsn(Opcodes.ICONST_0); + frame = FrameSnapshot.create(analyzer, 0); + + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, arr("Foo"), 1, + arr(Opcodes.INTEGER)); + } + + @Test + public void testReduce() { + analyzer.visitInsn(Opcodes.ICONST_0); + analyzer.visitInsn(Opcodes.LCONST_0); + analyzer.visitInsn(Opcodes.ICONST_0); + analyzer.visitInsn(Opcodes.DCONST_0); + frame = FrameSnapshot.create(analyzer, 0); + + final Object[] stack = arr(Opcodes.INTEGER, Opcodes.LONG, + Opcodes.INTEGER, Opcodes.DOUBLE); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, arr("Foo"), 4, stack); + } + + @Test + public void testPop() { + analyzer.visitInsn(Opcodes.ICONST_0); + analyzer.visitInsn(Opcodes.LCONST_0); + analyzer.visitInsn(Opcodes.ICONST_0); + analyzer.visitInsn(Opcodes.ICONST_0); + frame = FrameSnapshot.create(analyzer, 2); + + final Object[] stack = arr(Opcodes.INTEGER, Opcodes.LONG); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, arr("Foo"), 2, stack); + } + + private Object[] arr(Object... elements) { + return elements; + } +} diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java index acd6485c..880ebf20 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/MethodProbesAdapterTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.AnalyzerAdapter; import org.objectweb.asm.util.Printer; /** @@ -40,6 +41,8 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { private MethodVisitor adapter; + private IFrame frame; + private static class TraceAdapter extends MethodProbesVisitor { private final Printer printer; @@ -61,22 +64,26 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { } @Override - public void visitJumpInsnWithProbe(int opcode, Label label, int probeId) { + public void visitJumpInsnWithProbe(int opcode, Label label, + int probeId, IFrame frame) { rec("visitJumpInsnWithProbe", Integer.valueOf(opcode), label, Integer.valueOf(probeId)); + frame.accept(this); } @Override public void visitTableSwitchInsnWithProbes(int min, int max, - Label dflt, Label[] labels) { + Label dflt, Label[] labels, IFrame frame) { rec("visitTableSwitchInsnWithProbes", Integer.valueOf(min), Integer.valueOf(max), dflt, labels); + frame.accept(this); } @Override public void visitLookupSwitchInsnWithProbes(Label dflt, int[] keys, - Label[] labels) { + Label[] labels, IFrame frame) { rec("visitLookupSwitchInsnWithProbes", dflt, keys, labels); + frame.accept(this); } private void rec(String name, Object... args) { @@ -95,7 +102,17 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { expectedVisitor = new TraceAdapter(expected); actual = new MethodRecorder(); MethodProbesVisitor actualVisitor = new TraceAdapter(actual); - adapter = new MethodProbesAdapter(actualVisitor, this); + MethodProbesAdapter probesAdapter = new MethodProbesAdapter( + actualVisitor, this); + final AnalyzerAdapter analyzer = new AnalyzerAdapter("Foo", 0, "doit", + "()V", probesAdapter); + probesAdapter.setAnalyzer(analyzer); + adapter = analyzer; + frame = new IFrame() { + + public void accept(MethodVisitor mv) { + } + }; } @After @@ -116,6 +133,16 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { @Test public void testVisitProbe2() { + LabelInfo.setTarget(label); + LabelInfo.setTarget(label); + + adapter.visitLabel(label); + + expectedVisitor.visitLabel(label); + } + + @Test + public void testVisitProbe3() { adapter.visitLabel(label); expectedVisitor.visitLabel(label); @@ -130,8 +157,12 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { @Test public void testVisitInsn2() { + adapter.visitInsn(Opcodes.ICONST_0); + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitInsn(Opcodes.IADD); + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor.visitInsn(Opcodes.ICONST_0); expectedVisitor.visitInsn(Opcodes.IADD); } @@ -140,28 +171,70 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { LabelInfo.setTarget(label); LabelInfo.setTarget(label); - adapter.visitJumpInsn(Opcodes.IFLT, label); + adapter.visitJumpInsn(Opcodes.GOTO, label); - expectedVisitor.visitJumpInsnWithProbe(Opcodes.IFLT, label, 1000); + expectedVisitor + .visitJumpInsnWithProbe(Opcodes.GOTO, label, 1000, frame); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, + 0, null); } @Test public void testVisitJumpInsn2() { + LabelInfo.setTarget(label); + LabelInfo.setTarget(label); + + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitJumpInsn(Opcodes.IFLT, label); + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor + .visitJumpInsnWithProbe(Opcodes.IFLT, label, 1000, frame); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, + 0, null); + } + + @Test + public void testVisitJumpInsn3() { + adapter.visitInsn(Opcodes.ICONST_0); + adapter.visitJumpInsn(Opcodes.IFLT, label); + + expectedVisitor.visitInsn(Opcodes.ICONST_0); expectedVisitor.visitJumpInsn(Opcodes.IFLT, label); } @Test + public void testVisitJumpInsn4() { + LabelInfo.setTarget(label); + LabelInfo.setTarget(label); + + adapter.visitInsn(Opcodes.ICONST_0); + adapter.visitInsn(Opcodes.ICONST_0); + adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, label); + + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor.visitJumpInsnWithProbe(Opcodes.IF_ICMPEQ, label, 1000, + frame); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, + 0, null); + } + + @Test public void testVisitLookupSwitchInsn1() { LabelInfo.setTarget(label); LabelInfo.setTarget(label); final int[] keys = new int[] { 0, 1 }; final Label[] labels = new Label[] { label, label }; + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitLookupSwitchInsn(label, keys, labels); - expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels); + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels, + frame); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, + 0, null); assertEquals(1000, LabelInfo.getProbeId(label)); } @@ -173,9 +246,14 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { final int[] keys = new int[] { 0, 1 }; final Label[] labels = new Label[] { label2, label }; + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitLookupSwitchInsn(label, keys, labels); - expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels); + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor.visitLookupSwitchInsnWithProbes(label, keys, labels, + frame); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, + 0, null); assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label)); assertEquals(1000, LabelInfo.getProbeId(label2)); } @@ -184,8 +262,10 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { public void testVisitLookupSwitchInsn3() { final int[] keys = new int[] { 0, 1 }; final Label[] labels = new Label[] { label, label }; + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitLookupSwitchInsn(label, keys, labels); + expectedVisitor.visitInsn(Opcodes.ICONST_0); expectedVisitor.visitLookupSwitchInsn(label, keys, labels); } @@ -195,9 +275,14 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { LabelInfo.setTarget(label); final Label[] labels = new Label[] { label, label }; + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitTableSwitchInsn(0, 1, label, labels); - expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels); + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels, + frame); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, + 0, null); assertEquals(1000, LabelInfo.getProbeId(label)); } @@ -208,9 +293,14 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { LabelInfo.setTarget(label2); final Label[] labels = new Label[] { label2, label }; + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitTableSwitchInsn(0, 1, label, labels); - expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels); + expectedVisitor.visitInsn(Opcodes.ICONST_0); + expectedVisitor.visitTableSwitchInsnWithProbes(0, 1, label, labels, + frame); + expectedVisitor.visitFrame(Opcodes.F_FULL, 1, new Object[] { "Foo" }, + 0, null); assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label)); assertEquals(1000, LabelInfo.getProbeId(label2)); } @@ -218,8 +308,10 @@ public class MethodProbesAdapterTest implements IProbeIdGenerator { @Test public void testVisitTableSwitchInsn3() { final Label[] labels = new Label[] { label, label }; + adapter.visitInsn(Opcodes.ICONST_0); adapter.visitTableSwitchInsn(0, 1, label, labels); + expectedVisitor.visitInsn(Opcodes.ICONST_0); expectedVisitor.visitTableSwitchInsn(0, 1, label, labels); } diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/FrameTrackerTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/FrameTrackerTest.java deleted file mode 100644 index c3a89cd6..00000000 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/FrameTrackerTest.java +++ /dev/null @@ -1,1477 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Marc R. Hoffmann - initial API and implementation - * - *******************************************************************************/ -package org.jacoco.core.internal.instr; - -import static org.junit.Assert.assertEquals; -import static org.objectweb.asm.Opcodes.*; - -import org.jacoco.core.JaCoCo; -import org.jacoco.core.instr.MethodRecorder; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.objectweb.asm.Handle; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.MethodNode; - -/** - * Unit tests for {@link ClassInstrumenter}. - */ -public class FrameTrackerTest { - - private static class FrameBuilder { - - private Object[] stack = new Object[0]; - private Object[] locals = new Object[0]; - - FrameBuilder stack(Object... stack) { - this.stack = stack; - return this; - } - - FrameBuilder locals(Object... locals) { - this.locals = locals; - return this; - } - - void accept(MethodVisitor mv) { - mv.visitFrame(F_NEW, locals.length, locals, stack.length, stack); - } - - } - - private FrameBuilder before, after; - - private MethodNode mv; - - private Label label; - - @Before - public void setup() { - before = new FrameBuilder(); - after = new FrameBuilder(); - mv = new MethodNode(0, "test", "()V", null, null); - label = new Label(); - } - - @After - public void verify() { - MethodRecorder actual = new MethodRecorder(); - MethodVisitor noLabels = new MethodVisitor(JaCoCo.ASM_API_VERSION, - actual.getVisitor()) { - @Override - public void visitLabel(Label label) { - // Ignore labels inserted by the tracker - } - }; - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", noLabels); - before.accept(tracker); - mv.instructions.accept(tracker); - tracker.insertFrame(); - - MethodRecorder expected = new MethodRecorder(); - before.accept(expected.getVisitor()); - mv.instructions.accept(expected.getVisitor()); - after.accept(expected.getVisitor()); - - assertEquals(expected, actual); - } - - @Test(expected = IllegalArgumentException.class) - public void testVisitFrameIllegalFrameType() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitFrame(F_APPEND, 0, null, 0, null); - } - - @Test(expected = IllegalArgumentException.class) - public void testVisitInsnIllegalOpcode() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitInsn(GOTO); - } - - @Test(expected = IllegalArgumentException.class) - public void testVisitIntInsnIllegalOpcode() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitIntInsn(NOP, 0); - } - - @Test(expected = IllegalArgumentException.class) - public void testVisitVarInsnIllegalOpcode() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitVarInsn(NOP, 0); - } - - @Test(expected = IllegalArgumentException.class) - public void testVisitTypeInsnIllegalOpcode() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitTypeInsn(NOP, "A"); - } - - @Test(expected = IllegalArgumentException.class) - public void testVisitFieldInsnIllegalOpcode() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitFieldInsn(NOP, "A", "x", "I"); - } - - @Test(expected = IllegalArgumentException.class) - public void testVisitJumpInsnIllegalOpcode() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitJumpInsn(NOP, new Label()); - } - - @Test(expected = IllegalStateException.class) - public void testInvalidFrame_StackUnderflow() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitInsn(POP); - } - - @Test(expected = IllegalStateException.class) - public void testInvalidFrame_UndefinedLocal() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitVarInsn(ALOAD, 1); - } - - @Test - public void testArgumentsConstructor() { - FrameBuilder expectedFrame = new FrameBuilder(); - expectedFrame.locals(UNINITIALIZED_THIS); - testArguments(0, "<init>", "()V", expectedFrame); - } - - @Test - public void testArgumentsStatic() { - FrameBuilder expectedFrame = new FrameBuilder(); - testArguments(Opcodes.ACC_STATIC, "test", "()V", expectedFrame); - } - - @Test - public void testArgumentsStaticIJZ() { - FrameBuilder expectedFrame = new FrameBuilder(); - expectedFrame.locals(INTEGER, LONG, INTEGER); - testArguments(Opcodes.ACC_STATIC, "test", "(IJZ)V", expectedFrame); - } - - @Test - public void testArgumentsStaticLArr() { - FrameBuilder expectedFrame = new FrameBuilder(); - expectedFrame.locals("Foo", "[[S"); - testArguments(Opcodes.ACC_STATIC, "test", "(LFoo;[[S)V", expectedFrame); - } - - @Test - public void testArgumentsFD() { - FrameBuilder expectedFrame = new FrameBuilder(); - expectedFrame.locals("Test", FLOAT, DOUBLE); - testArguments(0, "test", "(FD)V", expectedFrame); - } - - private void testArguments(int access, String name, String desc, - FrameBuilder expectedFrame) { - MethodRecorder actual = new MethodRecorder(); - FrameTracker tracker = new FrameTracker("Test", access, name, desc, - actual.getVisitor()); - tracker.insertFrame(); - - MethodRecorder expected = new MethodRecorder(); - expectedFrame.accept(expected.getVisitor()); - - assertEquals(expected, actual); - } - - @Test - public void testFrameGaps() { - before.locals().stack(INTEGER); - mv.visitVarInsn(ISTORE, 3); - after.locals(TOP, TOP, TOP, INTEGER).stack(); - } - - @Test - public void testLargeFrame() { - before.locals("A", "B", "C", "D", "E").stack("AA", "BB", "CC", "DD", - "EE"); - mv.visitInsn(NOP); - after.locals("A", "B", "C", "D", "E").stack("AA", "BB", "CC", "DD", - "EE"); - } - - @Test - public void AALOAD_multidim_obj() { - before.locals().stack("[[Ljava/lang/String;", INTEGER); - mv.visitInsn(AALOAD); - after.locals().stack("[Ljava/lang/String;"); - } - - @Test - public void AALOAD_multidim_prim() { - before.locals().stack("[[I", INTEGER); - mv.visitInsn(AALOAD); - after.locals().stack("[I"); - } - - @Test - public void AASTORE() { - before.locals().stack("[Ljava/lang/String;", INTEGER, - "[Ljava/lang/String;"); - mv.visitInsn(AASTORE); - after.locals().stack(); - } - - @Test - public void ACONST_NULL() { - before.locals().stack(); - mv.visitInsn(ACONST_NULL); - after.locals().stack(NULL); - } - - @Test - public void ALOAD() { - before.locals(LONG, "X", INTEGER).stack(); - mv.visitVarInsn(ALOAD, 2); - after.locals(LONG, "X", INTEGER).stack("X"); - } - - @Test - public void ANEWARRAY() { - before.locals().stack(INTEGER); - mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); - after.locals().stack("[Ljava/lang/String;"); - } - - @Test - public void ANEWARRAY_multidim_obj() { - before.locals().stack(INTEGER); - mv.visitTypeInsn(ANEWARRAY, "[Ljava/lang/String;"); - after.locals().stack("[[Ljava/lang/String;"); - } - - @Test - public void ANEWARRAY_multidim_prim() { - before.locals().stack(INTEGER); - mv.visitTypeInsn(ANEWARRAY, "[I"); - after.locals().stack("[[I"); - } - - @Test - public void ARETURN() { - before.locals().stack("java/lang/Object"); - mv.visitInsn(ARETURN); - after.locals().stack(); - } - - @Test - public void ARRAYLENGTH() { - before.locals().stack("[Z"); - mv.visitInsn(ARRAYLENGTH); - after.locals().stack(INTEGER); - } - - @Test - public void ASTORE() { - before.locals(LONG, "X", INTEGER).stack("Y"); - mv.visitVarInsn(ASTORE, 3); - after.locals(LONG, "X", "Y").stack(); - } - - @Test - public void ATHROW() { - before.locals().stack("java/lang/Exception"); - mv.visitInsn(ATHROW); - after.locals().stack(); - } - - @Test - public void BALOAD() { - before.locals().stack("[B", INTEGER); - mv.visitInsn(BALOAD); - after.locals().stack(INTEGER); - } - - @Test - public void BASTORE() { - before.locals().stack("[B", INTEGER, INTEGER); - mv.visitInsn(BASTORE); - after.locals().stack(); - } - - @Test - public void BIPUSH() { - before.locals().stack(); - mv.visitIntInsn(BIPUSH, 123); - after.locals().stack(INTEGER); - } - - @Test - public void CALOAD() { - before.locals().stack("[C", INTEGER); - mv.visitInsn(CALOAD); - after.locals().stack(INTEGER); - } - - @Test - public void CASTORE() { - before.locals().stack("[C", INTEGER, INTEGER); - mv.visitInsn(CASTORE); - after.locals().stack(); - } - - @Test - public void CHECKCAST() { - before.locals().stack("java/lang/Object"); - mv.visitTypeInsn(CHECKCAST, "java/lang/String"); - after.locals().stack("java/lang/String"); - } - - @Test - public void D2F() { - before.locals().stack(DOUBLE); - mv.visitInsn(D2F); - after.locals().stack(FLOAT); - } - - @Test - public void D2I() { - before.locals().stack(DOUBLE); - mv.visitInsn(D2I); - after.locals().stack(INTEGER); - } - - @Test - public void D2L() { - before.locals().stack(DOUBLE); - mv.visitInsn(D2L); - after.locals().stack(LONG); - } - - @Test - public void DADD() { - before.locals().stack(DOUBLE, DOUBLE); - mv.visitInsn(DADD); - after.locals().stack(DOUBLE); - } - - @Test - public void DALOAD() { - before.locals().stack("[D", INTEGER); - mv.visitInsn(DALOAD); - after.locals().stack(DOUBLE); - } - - @Test - public void DASTORE() { - before.locals().stack("[D", INTEGER, DOUBLE); - mv.visitInsn(DASTORE); - after.locals().stack(); - } - - @Test - public void DCMPG() { - before.locals().stack(DOUBLE, DOUBLE); - mv.visitInsn(DCMPG); - after.locals().stack(INTEGER); - } - - @Test - public void DCMPL() { - before.locals().stack(DOUBLE, DOUBLE); - mv.visitInsn(DCMPL); - after.locals().stack(INTEGER); - } - - @Test - public void DCONST_0() { - before.locals().stack(); - mv.visitInsn(DCONST_0); - after.locals().stack(DOUBLE); - } - - @Test - public void DCONST_1() { - before.locals().stack(); - mv.visitInsn(DCONST_1); - after.locals().stack(DOUBLE); - } - - @Test - public void DDIV() { - before.locals().stack(DOUBLE, DOUBLE); - mv.visitInsn(DDIV); - after.locals().stack(DOUBLE); - } - - @Test - public void DLOAD() { - before.locals(DOUBLE).stack(); - mv.visitVarInsn(DLOAD, 0); - after.locals(DOUBLE).stack(DOUBLE); - } - - @Test - public void DMUL() { - before.locals().stack(DOUBLE, DOUBLE); - mv.visitInsn(DMUL); - after.locals().stack(DOUBLE); - } - - @Test - public void DNEG() { - before.locals().stack(DOUBLE); - mv.visitInsn(DNEG); - after.locals().stack(DOUBLE); - } - - @Test - public void DREM() { - before.locals().stack(DOUBLE, DOUBLE); - mv.visitInsn(DREM); - after.locals().stack(DOUBLE); - } - - @Test - public void DRETURN() { - before.locals().stack(DOUBLE); - mv.visitInsn(DRETURN); - after.locals().stack(); - } - - @Test - public void DSTORE() { - before.locals().stack(DOUBLE); - mv.visitVarInsn(DSTORE, 0); - after.locals(DOUBLE).stack(); - } - - @Test - public void DSUB() { - before.locals().stack(DOUBLE, DOUBLE); - mv.visitInsn(DSUB); - after.locals().stack(DOUBLE); - } - - @Test - public void DUP() { - before.locals().stack("A"); - mv.visitInsn(DUP); - after.locals().stack("A", "A"); - } - - @Test - public void DUP2_one_two_word_item() { - before.locals().stack(LONG); - mv.visitInsn(DUP2); - after.locals().stack(LONG, LONG); - } - - @Test - public void DUP2_two_one_word_items() { - before.locals().stack("A", "B"); - mv.visitInsn(DUP2); - after.locals().stack("A", "B", "A", "B"); - } - - @Test - public void DUP_X1() { - before.locals().stack("A", "B"); - mv.visitInsn(DUP_X1); - after.locals().stack("B", "A", "B"); - } - - @Test - public void DUP2_X1_one_two_word_item() { - before.locals().stack("A", LONG); - mv.visitInsn(DUP2_X1); - after.locals().stack(LONG, "A", LONG); - } - - @Test - public void DUP2_X1_two_one_word_items() { - before.locals().stack("A", "B", "C"); - mv.visitInsn(DUP2_X1); - after.locals().stack("B", "C", "A", "B", "C"); - } - - @Test - public void DUP_X2() { - before.locals().stack("A", "B", "C"); - mv.visitInsn(DUP_X2); - after.locals().stack("C", "A", "B", "C"); - } - - @Test - public void DUP2_X2_one_two_word_item() { - before.locals().stack("A", "B", LONG); - mv.visitInsn(DUP2_X2); - after.locals().stack(LONG, "A", "B", LONG); - } - - @Test - public void DUP2_X2_two_one_word_items() { - before.locals().stack("A", "B", "C", "D"); - mv.visitInsn(DUP2_X2); - after.locals().stack("C", "D", "A", "B", "C", "D"); - } - - @Test - public void F2D() { - before.locals().stack(FLOAT); - mv.visitInsn(F2D); - after.locals().stack(DOUBLE); - } - - @Test - public void F2I() { - before.locals().stack(FLOAT); - mv.visitInsn(F2I); - after.locals().stack(INTEGER); - } - - @Test - public void F2L() { - before.locals().stack(FLOAT); - mv.visitInsn(F2L); - after.locals().stack(LONG); - } - - @Test - public void FADD() { - before.locals().stack(FLOAT, FLOAT); - mv.visitInsn(FADD); - after.locals().stack(FLOAT); - } - - @Test - public void FALOAD() { - before.locals().stack("[F", INTEGER); - mv.visitInsn(FALOAD); - after.locals().stack(FLOAT); - } - - @Test - public void FASTORE() { - before.locals().stack("[F", INTEGER, FLOAT); - mv.visitInsn(FASTORE); - after.locals().stack(); - } - - @Test - public void FCMPG() { - before.locals().stack(FLOAT, FLOAT); - mv.visitInsn(FCMPG); - after.locals().stack(INTEGER); - } - - @Test - public void FCMPL() { - before.locals().stack(FLOAT, FLOAT); - mv.visitInsn(FCMPL); - after.locals().stack(INTEGER); - } - - @Test - public void FCONST_0() { - before.locals().stack(); - mv.visitInsn(FCONST_0); - after.locals().stack(FLOAT); - } - - @Test - public void FCONST_1() { - before.locals().stack(); - mv.visitInsn(FCONST_1); - after.locals().stack(FLOAT); - } - - @Test - public void FCONST_2() { - before.locals().stack(); - mv.visitInsn(FCONST_2); - after.locals().stack(FLOAT); - } - - @Test - public void FDIV() { - before.locals().stack(FLOAT, FLOAT); - mv.visitInsn(FDIV); - after.locals().stack(FLOAT); - } - - @Test - public void FLOAD() { - before.locals(FLOAT).stack(); - mv.visitVarInsn(FLOAD, 0); - after.locals(FLOAT).stack(FLOAT); - } - - @Test - public void FMUL() { - before.locals().stack(FLOAT, FLOAT); - mv.visitInsn(FMUL); - after.locals().stack(FLOAT); - } - - @Test - public void FNEG() { - before.locals().stack(FLOAT); - mv.visitInsn(FNEG); - after.locals().stack(FLOAT); - } - - @Test - public void FREM() { - before.locals().stack(FLOAT, FLOAT); - mv.visitInsn(FREM); - after.locals().stack(FLOAT); - } - - @Test - public void FRETURN() { - before.locals().stack(FLOAT); - mv.visitInsn(FRETURN); - after.locals().stack(); - } - - @Test - public void FSTORE() { - before.locals().stack(FLOAT); - mv.visitVarInsn(FSTORE, 0); - after.locals(FLOAT).stack(); - } - - @Test - public void FSUB() { - before.locals().stack(FLOAT, FLOAT); - mv.visitInsn(FSUB); - after.locals().stack(FLOAT); - } - - @Test - public void GETFIELD() { - before.locals().stack("Test"); - mv.visitFieldInsn(GETFIELD, "Test", "f", "I"); - after.locals().stack(INTEGER); - } - - @Test - public void GETSTATIC() { - before.locals().stack(); - mv.visitFieldInsn(GETSTATIC, "Test", "f", "Z"); - after.locals().stack(INTEGER); - } - - @Test - public void GETSTATIC_float() { - before.locals().stack(); - mv.visitFieldInsn(GETSTATIC, "Test", "f", "F"); - after.locals().stack(FLOAT); - } - - @Test - public void GETSTATIC_double() { - before.locals().stack(); - mv.visitFieldInsn(GETSTATIC, "Test", "f", "D"); - after.locals().stack(DOUBLE); - } - - @Test - public void GOTO() { - before.locals().stack(); - mv.visitJumpInsn(GOTO, label); - after.locals().stack(); - } - - @Test - public void I2B() { - before.locals().stack(INTEGER); - mv.visitInsn(I2B); - after.locals().stack(INTEGER); - } - - @Test - public void I2C() { - before.locals().stack(INTEGER); - mv.visitInsn(I2C); - after.locals().stack(INTEGER); - } - - @Test - public void I2D() { - before.locals().stack(INTEGER); - mv.visitInsn(I2D); - after.locals().stack(DOUBLE); - } - - @Test - public void I2F() { - before.locals().stack(INTEGER); - mv.visitInsn(I2F); - after.locals().stack(FLOAT); - } - - @Test - public void I2L() { - before.locals().stack(INTEGER); - mv.visitInsn(I2L); - after.locals().stack(LONG); - } - - @Test - public void I2S() { - before.locals().stack(INTEGER); - mv.visitInsn(I2S); - after.locals().stack(INTEGER); - } - - @Test - public void IADD() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IADD); - after.locals().stack(INTEGER); - } - - @Test - public void IALOAD() { - before.locals().stack("[I", INTEGER); - mv.visitInsn(IALOAD); - after.locals().stack(INTEGER); - } - - @Test - public void IAND() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IAND); - after.locals().stack(INTEGER); - } - - @Test - public void IASTORE() { - before.locals().stack("[I", INTEGER, INTEGER); - mv.visitInsn(IASTORE); - after.locals().stack(); - } - - @Test - public void ICONST_M1() { - before.locals().stack(); - mv.visitInsn(ICONST_M1); - after.locals().stack(INTEGER); - } - - @Test - public void ICONST_0() { - before.locals().stack(); - mv.visitInsn(ICONST_0); - after.locals().stack(INTEGER); - } - - @Test - public void ICONST_1() { - before.locals().stack(); - mv.visitInsn(ICONST_1); - after.locals().stack(INTEGER); - } - - @Test - public void ICONST_2() { - before.locals().stack(); - mv.visitInsn(ICONST_2); - after.locals().stack(INTEGER); - } - - @Test - public void ICONST_3() { - before.locals().stack(); - mv.visitInsn(ICONST_3); - after.locals().stack(INTEGER); - } - - @Test - public void ICONST_4() { - before.locals().stack(); - mv.visitInsn(ICONST_4); - after.locals().stack(INTEGER); - } - - @Test - public void ICONST_5() { - before.locals().stack(); - mv.visitInsn(ICONST_5); - after.locals().stack(INTEGER); - } - - @Test - public void IDIV() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IDIV); - after.locals().stack(INTEGER); - } - - @Test - public void IF_ACMPEQ() { - before.locals().stack("A", "A"); - mv.visitJumpInsn(IF_ACMPEQ, label); - after.locals().stack(); - } - - @Test - public void IF_ACMPNE() { - before.locals().stack("A", "A"); - mv.visitJumpInsn(IF_ACMPNE, label); - after.locals().stack(); - } - - @Test - public void IF_ICMPEQ() { - before.locals().stack(INTEGER, INTEGER); - mv.visitJumpInsn(IF_ICMPEQ, label); - after.locals().stack(); - } - - @Test - public void IF_ICMPGE() { - before.locals().stack(INTEGER, INTEGER); - mv.visitJumpInsn(IF_ICMPGE, label); - after.locals().stack(); - } - - @Test - public void IF_ICMPGT() { - before.locals().stack(INTEGER, INTEGER); - mv.visitJumpInsn(IF_ICMPGT, label); - after.locals().stack(); - } - - @Test - public void IF_ICMPLE() { - before.locals().stack(INTEGER, INTEGER); - mv.visitJumpInsn(IF_ICMPLE, label); - after.locals().stack(); - } - - @Test - public void IF_ICMPLT() { - before.locals().stack(INTEGER, INTEGER); - mv.visitJumpInsn(IF_ICMPLT, label); - after.locals().stack(); - } - - @Test - public void IF_ICMPNE() { - before.locals().stack(INTEGER, INTEGER); - mv.visitJumpInsn(IF_ICMPNE, label); - after.locals().stack(); - } - - @Test - public void IFEQ() { - before.locals().stack(INTEGER); - mv.visitJumpInsn(IFEQ, label); - after.locals().stack(); - } - - @Test - public void IFGE() { - before.locals().stack(INTEGER); - mv.visitJumpInsn(IFGE, label); - after.locals().stack(); - } - - @Test - public void IFGT() { - before.locals().stack(INTEGER); - mv.visitJumpInsn(IFGT, label); - after.locals().stack(); - } - - @Test - public void IFLE() { - before.locals().stack(INTEGER); - mv.visitJumpInsn(IFLE, label); - after.locals().stack(); - } - - @Test - public void IFLT() { - before.locals().stack(INTEGER); - mv.visitJumpInsn(IFLT, label); - after.locals().stack(); - } - - @Test - public void IFNE() { - before.locals().stack(INTEGER); - mv.visitJumpInsn(IFNE, label); - after.locals().stack(); - } - - @Test - public void IFNONNULL() { - before.locals().stack("A"); - mv.visitJumpInsn(IFNONNULL, label); - after.locals().stack(); - } - - @Test - public void IFNULL() { - before.locals().stack("A"); - mv.visitJumpInsn(IFNULL, label); - after.locals().stack(); - } - - @Test - public void IINC() { - before.locals(INTEGER).stack(); - mv.visitIincInsn(0, 1); - after.locals(INTEGER).stack(); - } - - @Test - public void ILOAD() { - before.locals(INTEGER).stack(); - mv.visitVarInsn(ILOAD, 0); - after.locals(INTEGER).stack(INTEGER); - } - - @Test - public void IMUL() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IMUL); - after.locals().stack(INTEGER); - } - - @Test - public void INEG() { - before.locals().stack(INTEGER); - mv.visitInsn(INEG); - after.locals().stack(INTEGER); - } - - @Test - public void INSTANCEOF() { - before.locals().stack("java/lang/String"); - mv.visitTypeInsn(INSTANCEOF, "java/lang/String"); - after.locals().stack(INTEGER); - } - - @Test - public void INVOKEDYNAMIC() { - before.locals().stack("java/lang/String"); - mv.visitInvokeDynamicInsn("foo", "(Ljava/lang/String;)I", new Handle(0, - null, null, null)); - after.locals().stack(INTEGER); - } - - @Test - public void INVOKEINTERFACE() { - before.locals().stack("Test"); - mv.visitMethodInsn(INVOKEVIRTUAL, "Test", "getSize", "()I"); - after.locals().stack(INTEGER); - } - - @Test - public void INVOKESPECIAL() { - before.locals().stack("Test", LONG, LONG); - mv.visitMethodInsn(INVOKEVIRTUAL, "Test", "add", "(JJ)J"); - after.locals().stack(LONG); - } - - @Test - public void INVOKESPECIAL_initsuper() { - before.locals(UNINITIALIZED_THIS).stack(UNINITIALIZED_THIS); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "<init>", "()V"); - after.locals("Test").stack(); - } - - @Test - public void INVOKESTATIC() { - before.locals().stack(LONG, LONG); - mv.visitMethodInsn(INVOKESTATIC, "Test", "add", "(JJ)J"); - after.locals().stack(LONG); - } - - @Test - public void INVOKEVIRTUAL() { - before.locals().stack("Test", INTEGER, DOUBLE); - mv.visitMethodInsn(INVOKEVIRTUAL, "Test", "run", "(ID)V"); - after.locals().stack(); - } - - @Test - public void IOR() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IOR); - after.locals().stack(INTEGER); - } - - @Test - public void IREM() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IREM); - after.locals().stack(INTEGER); - } - - @Test - public void IRETURN() { - before.locals().stack(INTEGER); - mv.visitInsn(IRETURN); - after.locals().stack(); - } - - @Test - public void ISHL() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(ISHL); - after.locals().stack(INTEGER); - } - - @Test - public void ISHR() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(ISHR); - after.locals().stack(INTEGER); - } - - @Test - public void ISSTORE() { - before.locals().stack(INTEGER); - mv.visitVarInsn(ISTORE, 0); - after.locals(INTEGER).stack(); - } - - @Test - public void ISUB() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(ISUB); - after.locals().stack(INTEGER); - } - - @Test - public void IUSHR() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IUSHR); - after.locals().stack(INTEGER); - } - - @Test - public void IXOR() { - before.locals().stack(INTEGER, INTEGER); - mv.visitInsn(IXOR); - after.locals().stack(INTEGER); - } - - @Test - public void L2D() { - before.locals().stack(LONG); - mv.visitInsn(L2D); - after.locals().stack(DOUBLE); - } - - @Test - public void L2F() { - before.locals().stack(LONG); - mv.visitInsn(L2F); - after.locals().stack(FLOAT); - } - - @Test - public void L2I() { - before.locals().stack(LONG); - mv.visitInsn(L2I); - after.locals().stack(INTEGER); - } - - @Test - public void LADD() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LADD); - after.locals().stack(LONG); - } - - @Test - public void LALOAD() { - before.locals().stack("L[", INTEGER); - mv.visitInsn(LALOAD); - after.locals().stack(LONG); - } - - @Test - public void LAND() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LAND); - after.locals().stack(LONG); - } - - @Test - public void LASTORE() { - before.locals().stack("L[", INTEGER, LONG); - mv.visitInsn(LASTORE); - after.locals().stack(); - } - - @Test - public void LCMP() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LCMP); - after.locals().stack(INTEGER); - } - - @Test - public void LCONST_0() { - before.locals().stack(); - mv.visitInsn(LCONST_0); - after.locals().stack(LONG); - } - - @Test - public void LCONST_1() { - before.locals().stack(); - mv.visitInsn(LCONST_1); - after.locals().stack(LONG); - } - - @Test - public void LDC_int() { - before.locals().stack(); - mv.visitLdcInsn(Integer.valueOf(123)); - after.locals().stack(INTEGER); - } - - @Test - public void LDC_float() { - before.locals().stack(); - mv.visitLdcInsn(Float.valueOf(123)); - after.locals().stack(FLOAT); - } - - @Test - public void LDC_long() { - before.locals().stack(); - mv.visitLdcInsn(Long.valueOf(123)); - after.locals().stack(LONG); - } - - @Test - public void LDC_double() { - before.locals().stack(); - mv.visitLdcInsn(Double.valueOf(123)); - after.locals().stack(DOUBLE); - } - - @Test - public void LDC_String() { - before.locals().stack(); - mv.visitLdcInsn("Hello VM!"); - after.locals().stack("java/lang/String"); - } - - @Test - public void LDC_Class() { - before.locals().stack(); - mv.visitLdcInsn(Type.getType("[java/lang/Runnable;")); - after.locals().stack("java/lang/Class"); - } - - @Test(expected = IllegalArgumentException.class) - public void LDC_invalidType() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", null); - tracker.visitLdcInsn(Byte.valueOf((byte) 123)); - } - - @Test - public void LDIV() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LDIV); - after.locals().stack(LONG); - } - - @Test - public void LLOAD() { - before.locals(LONG).stack(); - mv.visitVarInsn(LLOAD, 0); - after.locals(LONG).stack(LONG); - } - - @Test - public void LMUL() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LMUL); - after.locals().stack(LONG); - } - - @Test - public void LNEG() { - before.locals().stack(LONG); - mv.visitInsn(LNEG); - after.locals().stack(LONG); - } - - @Test - public void LOOKUPSWITCH() { - before.locals().stack(INTEGER); - mv.visitLookupSwitchInsn(new Label(), new int[0], new Label[0]); - after.locals().stack(); - } - - @Test - public void LOR() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LOR); - after.locals().stack(LONG); - } - - @Test - public void LREM() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LREM); - after.locals().stack(LONG); - } - - @Test - public void LRETURN() { - before.locals().stack(LONG); - mv.visitInsn(LRETURN); - after.locals().stack(); - } - - @Test - public void LSHL() { - before.locals().stack(LONG, INTEGER); - mv.visitInsn(LSHL); - after.locals().stack(LONG); - } - - @Test - public void LSHR() { - before.locals().stack(LONG, INTEGER); - mv.visitInsn(LSHR); - after.locals().stack(LONG); - } - - @Test - public void LSTORE() { - before.locals().stack(LONG); - mv.visitVarInsn(LSTORE, 0); - after.locals(LONG).stack(); - } - - @Test - public void LSUB() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LSUB); - after.locals().stack(LONG); - } - - @Test - public void LUSHR() { - before.locals().stack(LONG, INTEGER); - mv.visitInsn(LUSHR); - after.locals().stack(LONG); - } - - @Test - public void LXOR() { - before.locals().stack(LONG, LONG); - mv.visitInsn(LXOR); - after.locals().stack(LONG); - } - - @Test - public void MONITORENTER() { - before.locals().stack("java/lang/Object"); - mv.visitInsn(MONITORENTER); - after.locals().stack(); - } - - @Test - public void MONITOREXIT() { - before.locals().stack("java/lang/Object"); - mv.visitInsn(MONITOREXIT); - after.locals().stack(); - } - - @Test - public void MULTIANEWARRAY() { - before.locals().stack(INTEGER, INTEGER, INTEGER); - mv.visitMultiANewArrayInsn("[[[Ljava/lang/String;", 3); - after.locals().stack("[[[Ljava/lang/String;"); - } - - @Test - public void NEW() { - before.locals(LONG).stack(LONG); - mv.visitTypeInsn(NEW, "Test"); - mv.visitInsn(DUP); - mv.visitMethodInsn(INVOKESPECIAL, "Test", "<init>", "()V"); - after.locals(LONG).stack(LONG, "Test"); - } - - @Test - public void NEWARRAY_boolean() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_BOOLEAN); - after.locals().stack("[Z"); - } - - @Test - public void NEWARRAY_char() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_CHAR); - after.locals().stack("[C"); - } - - @Test - public void NEWARRAY_float() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_FLOAT); - after.locals().stack("[F"); - } - - @Test - public void NEWARRAY_double() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_DOUBLE); - after.locals().stack("[D"); - } - - @Test - public void NEWARRAY_byte() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_BYTE); - after.locals().stack("[B"); - } - - @Test - public void NEWARRAY_short() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_SHORT); - after.locals().stack("[S"); - } - - @Test - public void NEWARRAY_int() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_INT); - after.locals().stack("[I"); - } - - @Test - public void NEWARRAY_long() { - before.locals().stack(INTEGER); - mv.visitIntInsn(NEWARRAY, T_LONG); - after.locals().stack("[J"); - } - - @Test(expected = IllegalArgumentException.class) - public void NEWARRAY_invalidOperand() { - FrameTracker tracker = new FrameTracker("Test", ACC_STATIC, "test", - "()V", new MethodNode()); - tracker.visitFrame(F_NEW, 0, new Object[0], 1, new Object[] { INTEGER }); - tracker.visitIntInsn(NEWARRAY, -1); - } - - @Test - public void NOP() { - before.locals().stack(); - mv.visitInsn(NOP); - after.locals().stack(); - } - - @Test - public void POP() { - before.locals().stack(INTEGER); - mv.visitInsn(POP); - after.locals().stack(); - } - - @Test - public void POP2_one_two_word_item() { - before.locals().stack(DOUBLE); - mv.visitInsn(POP2); - after.locals().stack(); - } - - @Test - public void POP2_two_one_word_items() { - before.locals().stack("A", INTEGER); - mv.visitInsn(POP2); - after.locals().stack(); - } - - @Test - public void PUTFIELD() { - before.locals().stack("Test", INTEGER); - mv.visitFieldInsn(PUTFIELD, "Test", "field", "I"); - after.locals().stack(); - } - - @Test - public void PUTSTATIC() { - before.locals().stack(INTEGER); - mv.visitFieldInsn(PUTSTATIC, "Test", "field", "I"); - after.locals().stack(); - } - - @Test - public void RETURN() { - before.locals().stack(); - mv.visitInsn(RETURN); - after.locals().stack(); - } - - @Test - public void SALOAD() { - before.locals().stack("[S", INTEGER); - mv.visitInsn(SALOAD); - after.locals().stack(INTEGER); - } - - @Test - public void SASTORE() { - before.locals().stack("[S", INTEGER, INTEGER); - mv.visitInsn(SASTORE); - after.locals().stack(); - } - - @Test - public void SIPUSH() { - before.locals().stack(); - mv.visitIntInsn(SIPUSH, 123); - after.locals().stack(INTEGER); - } - - @Test - public void SWAP() { - before.locals().stack("A", "B"); - mv.visitInsn(SWAP); - after.locals().stack("B", "A"); - } - - @Test - public void TABLESWITCH() { - before.locals().stack(INTEGER); - mv.visitTableSwitchInsn(0, 1, new Label(), new Label[0]); - after.locals().stack(); - } - -} diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java index 317fbd61..67ffdbfc 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/MethodInstrumenterTest.java @@ -14,6 +14,7 @@ package org.jacoco.core.internal.instr; import static org.junit.Assert.assertEquals; import org.jacoco.core.instr.MethodRecorder; +import org.jacoco.core.internal.flow.IFrame; import org.jacoco.core.internal.flow.LabelInfo; import org.junit.Before; import org.junit.Test; @@ -32,6 +33,8 @@ public class MethodInstrumenterTest { private MethodVisitor expectedVisitor; + private IFrame frame; + @Before public void setup() { actual = new MethodRecorder(); @@ -43,14 +46,13 @@ public class MethodInstrumenterTest { actual.getVisitor().visitLdcInsn("Probe " + id); } }; - final IFrameInserter frameInserter = new IFrameInserter() { - - public void insertFrame() { - actual.getVisitor().visitLdcInsn("Frame"); + instrumenter = new MethodInstrumenter(actual.getVisitor(), + probeInserter); + frame = new IFrame() { + public void accept(MethodVisitor mv) { + mv.visitFrame(Opcodes.F_FULL, 0, null, 0, null); } }; - instrumenter = new MethodInstrumenter(actual.getVisitor(), - probeInserter, frameInserter); } void sampleReturn() { @@ -79,7 +81,7 @@ public class MethodInstrumenterTest { @Test public void testVisitJumpInsnWithProbe_GOTO() { final Label label = new Label(); - instrumenter.visitJumpInsnWithProbe(Opcodes.GOTO, label, 3); + instrumenter.visitJumpInsnWithProbe(Opcodes.GOTO, label, 3, frame); expectedVisitor.visitLdcInsn("Probe 3"); expectedVisitor.visitJumpInsn(Opcodes.GOTO, label); @@ -174,14 +176,14 @@ public class MethodInstrumenterTest { private void testVisitJumpInsnWithProbe(int opcodeOrig, int opcodeInstr) { final Label label = new Label(); - instrumenter.visitJumpInsnWithProbe(opcodeOrig, label, 3); + instrumenter.visitJumpInsnWithProbe(opcodeOrig, label, 3, frame); final Label l2 = new Label(); expectedVisitor.visitJumpInsn(opcodeInstr, l2); expectedVisitor.visitLdcInsn("Probe 3"); expectedVisitor.visitJumpInsn(Opcodes.GOTO, label); expectedVisitor.visitLabel(l2); - expectedVisitor.visitLdcInsn("Frame"); + expectedVisitor.visitFrame(Opcodes.F_FULL, 0, null, 0, null); assertEquals(expected, actual); } @@ -194,16 +196,16 @@ public class MethodInstrumenterTest { LabelInfo.setProbeId(L0, 0); LabelInfo.setProbeId(L1, 1); instrumenter.visitTableSwitchInsnWithProbes(3, 5, L0, new Label[] { L1, - L1, L2 }); + L1, L2 }, frame); expectedVisitor.visitTableSwitchInsn(3, 4, L0, new Label[] { L1, L1, L2 }); expectedVisitor.visitLabel(L0); - expectedVisitor.visitLdcInsn("Frame"); + expectedVisitor.visitFrame(Opcodes.F_FULL, 0, null, 0, null); expectedVisitor.visitLdcInsn("Probe 0"); expectedVisitor.visitJumpInsn(Opcodes.GOTO, new Label()); expectedVisitor.visitLabel(L1); - expectedVisitor.visitLdcInsn("Frame"); + expectedVisitor.visitFrame(Opcodes.F_FULL, 0, null, 0, null); expectedVisitor.visitLdcInsn("Probe 1"); expectedVisitor.visitJumpInsn(Opcodes.GOTO, new Label()); @@ -218,16 +220,16 @@ public class MethodInstrumenterTest { LabelInfo.setProbeId(L0, 0); LabelInfo.setProbeId(L1, 1); instrumenter.visitLookupSwitchInsnWithProbes(L0, - new int[] { 10, 20, 30 }, new Label[] { L1, L1, L2 }); + new int[] { 10, 20, 30 }, new Label[] { L1, L1, L2 }, frame); expectedVisitor.visitLookupSwitchInsn(L0, new int[] { 10, 20, 30 }, new Label[] { L1, L1, L2 }); expectedVisitor.visitLabel(L0); - expectedVisitor.visitLdcInsn("Frame"); + expectedVisitor.visitFrame(Opcodes.F_FULL, 0, null, 0, null); expectedVisitor.visitLdcInsn("Probe 0"); expectedVisitor.visitJumpInsn(Opcodes.GOTO, new Label()); expectedVisitor.visitLabel(L1); - expectedVisitor.visitLdcInsn("Frame"); + expectedVisitor.visitFrame(Opcodes.F_FULL, 0, null, 0, null); expectedVisitor.visitLdcInsn("Probe 1"); expectedVisitor.visitJumpInsn(Opcodes.GOTO, new Label()); diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java index 0a55213d..fd5265e0 100644 --- a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java +++ b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java @@ -81,7 +81,7 @@ public class Analyzer { coverageVisitor.visitCoverage(getCoverage()); } }; - return new ClassProbesAdapter(analyzer); + return new ClassProbesAdapter(analyzer, false); } /** diff --git a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java index c4d52669..9a5279cf 100644 --- a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java +++ b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java @@ -60,7 +60,7 @@ public class Instrumenter { private ClassVisitor createInstrumentingVisitor(final long classid, final ClassVisitor cv) { return new ClassProbesAdapter(new ClassInstrumenter(classid, - accessGenerator, cv)); + accessGenerator, cv), true); } /** diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java index 97ccaa3d..03e0c6d5 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java @@ -17,6 +17,7 @@ import java.util.List; import org.jacoco.core.analysis.ICounter; import org.jacoco.core.analysis.IMethodCoverage; import org.jacoco.core.analysis.ISourceNode; +import org.jacoco.core.internal.flow.IFrame; import org.jacoco.core.internal.flow.Instruction; import org.jacoco.core.internal.flow.LabelInfo; import org.jacoco.core.internal.flow.MethodProbesVisitor; @@ -212,7 +213,7 @@ public class MethodAnalyzer extends MethodProbesVisitor { @Override public void visitJumpInsnWithProbe(final int opcode, final Label label, - final int probeId) { + final int probeId, final IFrame frame) { visitInsn(); addProbe(probeId); } @@ -225,13 +226,13 @@ public class MethodAnalyzer extends MethodProbesVisitor { @Override public void visitTableSwitchInsnWithProbes(final int min, final int max, - final Label dflt, final Label[] labels) { + final Label dflt, final Label[] labels, final IFrame frame) { visitSwitchInsnWithProbes(dflt, labels); } @Override public void visitLookupSwitchInsnWithProbes(final Label dflt, - final int[] keys, final Label[] labels) { + final int[] keys, final Label[] labels, final IFrame frame) { visitSwitchInsnWithProbes(dflt, labels); } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java index 2fe3e3d7..5089b24c 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java @@ -16,6 +16,7 @@ import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.AnalyzerAdapter; /** * A {@link org.objectweb.asm.ClassVisitor} that calculates probes for every @@ -36,7 +37,7 @@ public class ClassProbesAdapter extends ClassVisitor implements @Override public void visitJumpInsnWithProbe(final int opcode, - final Label label, final int probeId) { + final Label label, final int probeId, final IFrame frame) { // nothing to do } @@ -47,13 +48,14 @@ public class ClassProbesAdapter extends ClassVisitor implements @Override public void visitTableSwitchInsnWithProbes(final int min, - final int max, final Label dflt, final Label[] labels) { + final int max, final Label dflt, final Label[] labels, + final IFrame frame) { // nothing to do } @Override public void visitLookupSwitchInsnWithProbes(final Label dflt, - final int[] keys, final Label[] labels) { + final int[] keys, final Label[] labels, final IFrame frame) { // nothing to do } } @@ -70,8 +72,12 @@ public class ClassProbesAdapter extends ClassVisitor implements private final ClassProbesVisitor cv; + private final boolean trackFrames; + private int counter = 0; + private String name; + private boolean interfaceType; /** @@ -79,17 +85,22 @@ public class ClassProbesAdapter extends ClassVisitor implements * * @param cv * instance to delegate to + * @param trackFrames + * if <code>true</code> stackmap frames are tracked and provided */ - public ClassProbesAdapter(final ClassProbesVisitor cv) { + public ClassProbesAdapter(final ClassProbesVisitor cv, + final boolean trackFrames) { super(JaCoCo.ASM_API_VERSION, cv); this.cv = cv; + this.trackFrames = trackFrames; } @Override public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { - interfaceType = (access & Opcodes.ACC_INTERFACE) != 0; + this.name = name; + this.interfaceType = (access & Opcodes.ACC_INTERFACE) != 0; super.visit(version, access, name, signature, superName, interfaces); } @@ -122,8 +133,17 @@ public class ClassProbesAdapter extends ClassVisitor implements instructions.accept(adapter); cv.visitTotalProbeCount(probeCounter.count); } - this.accept(new MethodProbesAdapter(methodProbes, - ClassProbesAdapter.this)); + final MethodProbesAdapter probesAdapter = new MethodProbesAdapter( + methodProbes, ClassProbesAdapter.this); + if (trackFrames) { + final AnalyzerAdapter analyzer = new AnalyzerAdapter( + ClassProbesAdapter.this.name, access, name, desc, + probesAdapter); + probesAdapter.setAnalyzer(analyzer); + this.accept(analyzer); + } else { + this.accept(probesAdapter); + } } }; } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/FrameSnapshot.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/FrameSnapshot.java new file mode 100644 index 00000000..c3acf5d8 --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/FrameSnapshot.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ + +package org.jacoco.core.internal.flow; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.AnalyzerAdapter; + +/** + * IFrame implementation which creates snapshots from an {@link AnalyzerAdapter} + */ +class FrameSnapshot implements IFrame { + + private static final FrameSnapshot NOP = new FrameSnapshot(null, null); + + private final Object[] locals; + private final Object[] stack; + + private FrameSnapshot(final Object[] locals, final Object[] stack) { + this.locals = locals; + this.stack = stack; + } + + /** + * Create a IFrame instance based on the given analyzer. + * + * @param analyzer + * analyzer instance or <code>null</code> + * @param popCount + * number of items to remove from the operand stack + * @return IFrame instance. In case the analyzer is <code>null</code> or + * does not contain stackmap information a "NOP" IFrame is returned. + */ + static IFrame create(final AnalyzerAdapter analyzer, final int popCount) { + if (analyzer == null || analyzer.locals == null) { + return NOP; + } + @SuppressWarnings("unchecked") + final List<Object> locals = analyzer.locals, stack = analyzer.stack; + return new FrameSnapshot(reduce(locals, 0), reduce(stack, popCount)); + } + + /** + * Reduce double word types into a single slot as required + * {@link MethodVisitor#visitFrame(int, int, Object[], int, Object[])} + * method. + */ + private static Object[] reduce(final List<Object> source, final int popCount) { + final List<Object> copy = new ArrayList<Object>(source); + final int size = source.size() - popCount; + copy.subList(size, source.size()).clear(); + for (int i = size; --i >= 0;) { + final Object type = source.get(i); + if (type == Opcodes.LONG || type == Opcodes.DOUBLE) { + copy.remove(i + 1); + } + } + return copy.toArray(); + } + + // === IFrame implementation === + + public void accept(final MethodVisitor mv) { + if (locals != null) { + mv.visitFrame(Opcodes.F_NEW, locals.length, locals, stack.length, + stack); + } + } + +} diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/IFrameInserter.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/IFrame.java index 5bd0b4a8..31042678 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/instr/IFrameInserter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/IFrame.java @@ -9,26 +9,21 @@ * Marc R. Hoffmann - initial API and implementation * *******************************************************************************/ -package org.jacoco.core.internal.instr; +package org.jacoco.core.internal.flow; + +import org.objectweb.asm.MethodVisitor; /** - * Internal interface for insertion of additional stackmap frame in the - * instruction sequence of a method. + * Representation of the current stackmap frame content. */ -interface IFrameInserter { - - /** - * Empty implementation. - */ - static final IFrameInserter NOP = new IFrameInserter() { - public void insertFrame() { - } - }; +public interface IFrame { /** - * Inserts an additional frame reflecting the current locals and stack - * types. + * Emits a frame event with the current content to the given visitor. + * + * @param mv + * method visitor to emit frame event to */ - void insertFrame(); + void accept(final MethodVisitor mv); } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java index 5d681445..dde89c07 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java @@ -15,6 +15,7 @@ import org.jacoco.core.JaCoCo; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.AnalyzerAdapter; /** * Adapter that creates additional visitor events for probes to be inserted into @@ -26,6 +27,8 @@ public final class MethodProbesAdapter extends MethodVisitor { private final IProbeIdGenerator idGenerator; + private AnalyzerAdapter analyzer; + /** * Create a new adapter instance. * @@ -41,6 +44,17 @@ public final class MethodProbesAdapter extends MethodVisitor { this.idGenerator = idGenerator; } + /** + * If an analyzer is set {@link IFrame} handles are calculated and emitted + * to the probes methods. + * + * @param analyzer + * optional analyzer to set + */ + public void setAnalyzer(final AnalyzerAdapter analyzer) { + this.analyzer = analyzer; + } + @Override public void visitLabel(final Label label) { if (LabelInfo.isMultiTarget(label) && LabelInfo.isSuccessor(label)) { @@ -71,17 +85,36 @@ public final class MethodProbesAdapter extends MethodVisitor { public void visitJumpInsn(final int opcode, final Label label) { if (LabelInfo.isMultiTarget(label)) { probesVisitor.visitJumpInsnWithProbe(opcode, label, - idGenerator.nextId()); + idGenerator.nextId(), frame(jumpPopCount(opcode))); } else { probesVisitor.visitJumpInsn(opcode, label); } } + private int jumpPopCount(final int opcode) { + switch (opcode) { + case Opcodes.GOTO: + return 0; + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: + return 1; + default: // IF_CMPxx and IF_ACMPxx + return 2; + } + } + @Override public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { if (markLabels(dflt, labels)) { - probesVisitor.visitLookupSwitchInsnWithProbes(dflt, keys, labels); + probesVisitor.visitLookupSwitchInsnWithProbes(dflt, keys, labels, + frame(1)); } else { probesVisitor.visitLookupSwitchInsn(dflt, keys, labels); } @@ -91,8 +124,8 @@ public final class MethodProbesAdapter extends MethodVisitor { public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { if (markLabels(dflt, labels)) { - probesVisitor - .visitTableSwitchInsnWithProbes(min, max, dflt, labels); + probesVisitor.visitTableSwitchInsnWithProbes(min, max, dflt, + labels, frame(1)); } else { probesVisitor.visitTableSwitchInsn(min, max, dflt, labels); } @@ -107,7 +140,7 @@ public final class MethodProbesAdapter extends MethodVisitor { } LabelInfo.setDone(dflt); for (final Label l : labels) { - if (!LabelInfo.isDone(l) && LabelInfo.isMultiTarget(l)) { + if (LabelInfo.isMultiTarget(l) && !LabelInfo.isDone(l)) { LabelInfo.setProbeId(l, idGenerator.nextId()); probe = true; } @@ -116,4 +149,8 @@ public final class MethodProbesAdapter extends MethodVisitor { return probe; } + private IFrame frame(final int popCount) { + return FrameSnapshot.create(analyzer, popCount); + } + } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java index fd4b281a..609279ba 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesVisitor.java @@ -63,10 +63,14 @@ public abstract class MethodProbesVisitor extends MethodVisitor { * instruction may jump. * @param probeId * id of the probe + * @param frame + * stackmap frame status after the execution of the jump + * instruction. The instance is only valid with the call of this + * method. * @see MethodVisitor#visitJumpInsn(int, Label) */ public abstract void visitJumpInsnWithProbe(int opcode, Label label, - int probeId); + int probeId, IFrame frame); /** * Visits a zero operand instruction with a probe. This event is used only @@ -100,10 +104,14 @@ public abstract class MethodProbesVisitor extends MethodVisitor { * beginnings of the handler blocks. <code>labels[i]</code> is * the beginning of the handler block for the * <code>min + i</code> key. + * @param frame + * stackmap frame status after the execution of the switch + * instruction. The instance is only valid with the call of this + * method. * @see MethodVisitor#visitTableSwitchInsn(int, int, Label, Label[]) */ public abstract void visitTableSwitchInsnWithProbes(int min, int max, - Label dflt, Label[] labels); + Label dflt, Label[] labels, IFrame frame); /** * Visits a LOOKUPSWITCH instruction with optional probes for each target @@ -120,9 +128,13 @@ public abstract class MethodProbesVisitor extends MethodVisitor { * beginnings of the handler blocks. <code>labels[i]</code> is * the beginning of the handler block for the * <code>keys[i]</code> key. + * @param frame + * stackmap frame status after the execution of the switch + * instruction. The instance is only valid with the call of this + * method. * @see MethodVisitor#visitLookupSwitchInsn(Label, int[], Label[]) */ public abstract void visitLookupSwitchInsnWithProbes(Label dflt, - int[] keys, Label[] labels); + int[] keys, Label[] labels, IFrame frame); } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java index d9fb76ea..8ea1926f 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java @@ -95,15 +95,8 @@ public class ClassInstrumenter extends ClassProbesVisitor { final MethodVisitor frameEliminator = new DuplicateFrameEliminator(mv); final ProbeInserter probeVariableInserter = new ProbeInserter(access, desc, frameEliminator, probeArrayStrategy); - if (withFrames) { - final FrameTracker frameTracker = new FrameTracker(className, - access, name, desc, probeVariableInserter); - return new MethodInstrumenter(frameTracker, probeVariableInserter, - frameTracker); - } else { - return new MethodInstrumenter(probeVariableInserter, - probeVariableInserter, IFrameInserter.NOP); - } + return new MethodInstrumenter(probeVariableInserter, + probeVariableInserter); } @Override diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/FrameTracker.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/FrameTracker.java deleted file mode 100644 index ced09d26..00000000 --- a/org.jacoco.core/src/org/jacoco/core/internal/instr/FrameTracker.java +++ /dev/null @@ -1,737 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Marc R. Hoffmann - initial API and implementation - * - *******************************************************************************/ -package org.jacoco.core.internal.instr; - -import org.jacoco.core.JaCoCo; -import org.objectweb.asm.Handle; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -/** - * This method adapter tracks the state of the local variable and stack types. - * With insertFrame() additional frames can then be added. The adapter is only - * intended to be used with class file versions >= {@link Opcodes#V1_6}. - */ -class FrameTracker extends MethodVisitor implements IFrameInserter { - - private final String owner; - - private Object[] local; - private int localSize; - private Object[] stack; - private int stackSize; - - public FrameTracker(final String owner, final int access, - final String name, final String desc, final MethodVisitor mv) { - super(JaCoCo.ASM_API_VERSION, mv); - this.owner = owner; - local = new Object[8]; - localSize = 0; - stack = new Object[8]; - stackSize = 0; - - if ((access & Opcodes.ACC_STATIC) == 0) { - if ("<init>".equals(name)) { - set(localSize, Opcodes.UNINITIALIZED_THIS); - } else { - set(localSize, owner); - } - } - for (final Type t : Type.getArgumentTypes(desc)) { - set(localSize, t); - } - - } - - public void insertFrame() { - // Reduced types do not need more space than expanded types: - final Object[] local = new Object[this.localSize]; - final Object[] stack = new Object[this.stackSize]; - final int localSize = reduce(this.local, this.localSize, local); - final int stackSize = reduce(this.stack, this.stackSize, stack); - mv.visitFrame(Opcodes.F_NEW, localSize, local, stackSize, stack); - } - - @Override - public void visitFrame(final int type, final int nLocal, - final Object[] local, final int nStack, final Object[] stack) { - - if (type != Opcodes.F_NEW) { - throw new IllegalArgumentException( - "ClassReader.accept() should be called with EXPAND_FRAMES flag"); - } - - // expanded types need at most twice the size - this.local = ensureSize(this.local, nLocal * 2); - this.stack = ensureSize(this.stack, nStack * 2); - this.localSize = expand(local, nLocal, this.local); - this.stackSize = expand(stack, nStack, this.stack); - - mv.visitFrame(type, nLocal, local, nStack, stack); - } - - @Override - public void visitInsn(final int opcode) { - final Object t1, t2, t3, t4; - switch (opcode) { - case Opcodes.NOP: - case Opcodes.RETURN: - break; - case Opcodes.ARETURN: - case Opcodes.ATHROW: - case Opcodes.FRETURN: - case Opcodes.IRETURN: - case Opcodes.MONITORENTER: - case Opcodes.MONITOREXIT: - case Opcodes.POP: - pop(1); - break; - case Opcodes.DRETURN: - case Opcodes.LRETURN: - case Opcodes.POP2: - pop(2); - break; - case Opcodes.AASTORE: - case Opcodes.BASTORE: - case Opcodes.CASTORE: - case Opcodes.FASTORE: - case Opcodes.IASTORE: - case Opcodes.SASTORE: - pop(3); - break; - case Opcodes.LASTORE: - case Opcodes.DASTORE: - pop(4); - break; - case Opcodes.ICONST_M1: - case Opcodes.ICONST_0: - case Opcodes.ICONST_1: - case Opcodes.ICONST_2: - case Opcodes.ICONST_3: - case Opcodes.ICONST_4: - case Opcodes.ICONST_5: - push(Opcodes.INTEGER); - break; - case Opcodes.ARRAYLENGTH: - case Opcodes.F2I: - case Opcodes.I2B: - case Opcodes.I2C: - case Opcodes.I2S: - case Opcodes.INEG: - pop(1); - push(Opcodes.INTEGER); - break; - case Opcodes.BALOAD: - case Opcodes.CALOAD: - case Opcodes.D2I: - case Opcodes.FCMPG: - case Opcodes.FCMPL: - case Opcodes.IADD: - case Opcodes.IALOAD: - case Opcodes.IAND: - case Opcodes.IDIV: - case Opcodes.IMUL: - case Opcodes.IOR: - case Opcodes.IREM: - case Opcodes.ISHL: - case Opcodes.ISHR: - case Opcodes.ISUB: - case Opcodes.IUSHR: - case Opcodes.IXOR: - case Opcodes.L2I: - case Opcodes.SALOAD: - pop(2); - push(Opcodes.INTEGER); - break; - case Opcodes.DCMPG: - case Opcodes.DCMPL: - case Opcodes.LCMP: - pop(4); - push(Opcodes.INTEGER); - break; - case Opcodes.FCONST_0: - case Opcodes.FCONST_1: - case Opcodes.FCONST_2: - push(Opcodes.FLOAT); - break; - case Opcodes.FNEG: - case Opcodes.I2F: - pop(1); - push(Opcodes.FLOAT); - break; - case Opcodes.D2F: - case Opcodes.FADD: - case Opcodes.FALOAD: - case Opcodes.FDIV: - case Opcodes.FMUL: - case Opcodes.FREM: - case Opcodes.FSUB: - case Opcodes.L2F: - pop(2); - push(Opcodes.FLOAT); - break; - case Opcodes.LCONST_0: - case Opcodes.LCONST_1: - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.F2L: - case Opcodes.I2L: - pop(1); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.D2L: - case Opcodes.LALOAD: - case Opcodes.LNEG: - pop(2); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.LSHL: - case Opcodes.LSHR: - case Opcodes.LUSHR: - pop(3); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.LADD: - case Opcodes.LAND: - case Opcodes.LDIV: - case Opcodes.LMUL: - case Opcodes.LOR: - case Opcodes.LREM: - case Opcodes.LSUB: - case Opcodes.LXOR: - pop(4); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.DCONST_0: - case Opcodes.DCONST_1: - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.F2D: - case Opcodes.I2D: - pop(1); - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.DALOAD: - case Opcodes.DNEG: - case Opcodes.L2D: - pop(2); - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.DADD: - case Opcodes.DDIV: - case Opcodes.DMUL: - case Opcodes.DREM: - case Opcodes.DSUB: - pop(4); - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.ACONST_NULL: - push(Opcodes.NULL); - break; - case Opcodes.AALOAD: - pop(1); - t1 = pop(); - push(Type.getType(((String) t1).substring(1))); - break; - case Opcodes.DUP: - t1 = pop(); - push(t1); - push(t1); - break; - case Opcodes.DUP_X1: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2: - t1 = pop(); - t2 = pop(); - push(t2); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X1: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t2); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - t4 = pop(); - push(t2); - push(t1); - push(t4); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.SWAP: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - break; - default: - throw new IllegalArgumentException(); - } - mv.visitInsn(opcode); - } - - @Override - public void visitIntInsn(final int opcode, final int operand) { - switch (opcode) { - case Opcodes.BIPUSH: - case Opcodes.SIPUSH: - push(Opcodes.INTEGER); - break; - case Opcodes.NEWARRAY: - pop(1); - switch (operand) { - case Opcodes.T_BOOLEAN: - push("[Z"); - break; - case Opcodes.T_CHAR: - push("[C"); - break; - case Opcodes.T_FLOAT: - push("[F"); - break; - case Opcodes.T_DOUBLE: - push("[D"); - break; - case Opcodes.T_BYTE: - push("[B"); - break; - case Opcodes.T_SHORT: - push("[S"); - break; - case Opcodes.T_INT: - push("[I"); - break; - case Opcodes.T_LONG: - push("[J"); - break; - default: - throw new IllegalArgumentException(); - } - break; - default: - throw new IllegalArgumentException(); - } - mv.visitIntInsn(opcode, operand); - } - - @Override - public void visitVarInsn(final int opcode, final int var) { - final Object t; - switch (opcode) { - case Opcodes.ALOAD: - push(get(var)); - break; - case Opcodes.ILOAD: - push(Opcodes.INTEGER); - break; - case Opcodes.FLOAD: - push(Opcodes.FLOAT); - break; - case Opcodes.LLOAD: - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.DLOAD: - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.ASTORE: - case Opcodes.ISTORE: - case Opcodes.FSTORE: - t = pop(); - set(var, t); - break; - case Opcodes.LSTORE: - case Opcodes.DSTORE: - pop(1); - t = pop(); - set(var, t); - set(var + 1, Opcodes.TOP); - break; - default: - throw new IllegalArgumentException(); - } - mv.visitVarInsn(opcode, var); - } - - @Override - public void visitTypeInsn(final int opcode, final String type) { - switch (opcode) { - case Opcodes.NEW: - final Label label = new Label(); - mv.visitLabel(label); - push(label); - break; - case Opcodes.ANEWARRAY: - pop(1); - push('[' + Type.getObjectType(type).getDescriptor()); - break; - case Opcodes.CHECKCAST: - pop(1); - push(type); - break; - case Opcodes.INSTANCEOF: - pop(1); - push(Opcodes.INTEGER); - break; - default: - throw new IllegalArgumentException(); - } - mv.visitTypeInsn(opcode, type); - } - - @Override - public void visitFieldInsn(final int opcode, final String owner, - final String name, final String desc) { - final Type t = Type.getType(desc); - switch (opcode) { - case Opcodes.PUTSTATIC: - pop(t); - break; - case Opcodes.PUTFIELD: - pop(t); - pop(1); - break; - case Opcodes.GETSTATIC: - push(t); - break; - case Opcodes.GETFIELD: - pop(1); - push(t); - break; - default: - throw new IllegalArgumentException(); - } - mv.visitFieldInsn(opcode, owner, name, desc); - } - - @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { - for (final Type t : Type.getArgumentTypes(desc)) { - pop(t); - } - if (opcode != Opcodes.INVOKESTATIC) { - final Object target = pop(); - if (target == Opcodes.UNINITIALIZED_THIS) { - replace(Opcodes.UNINITIALIZED_THIS, this.owner); - } else if (target instanceof Label) { - replace(target, owner); - } - } - push(Type.getReturnType(desc)); - - mv.visitMethodInsn(opcode, owner, name, desc); - } - - @Override - public void visitInvokeDynamicInsn(final String name, final String desc, - final Handle bsm, final Object... bsmArgs) { - for (final Type t : Type.getArgumentTypes(desc)) { - pop(t); - } - push(Type.getReturnType(desc)); - - mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); - } - - @Override - public void visitLdcInsn(final Object cst) { - if (cst instanceof Integer) { - push(Opcodes.INTEGER); - } else if (cst instanceof Float) { - push(Opcodes.FLOAT); - } else if (cst instanceof Long) { - push(Opcodes.LONG); - push(Opcodes.TOP); - } else if (cst instanceof Double) { - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - } else if (cst instanceof String) { - push("java/lang/String"); - } else if (cst instanceof Type) { - push("java/lang/Class"); - } else { - throw new IllegalArgumentException(); - } - mv.visitLdcInsn(cst); - } - - @Override - public void visitJumpInsn(final int opcode, final Label label) { - switch (opcode) { - case Opcodes.GOTO: - break; - case Opcodes.IFEQ: - case Opcodes.IFNE: - case Opcodes.IFLT: - case Opcodes.IFGE: - case Opcodes.IFGT: - case Opcodes.IFLE: - case Opcodes.IFNULL: - case Opcodes.IFNONNULL: - pop(1); - break; - case Opcodes.IF_ICMPEQ: - case Opcodes.IF_ICMPNE: - case Opcodes.IF_ICMPLT: - case Opcodes.IF_ICMPGE: - case Opcodes.IF_ICMPGT: - case Opcodes.IF_ICMPLE: - case Opcodes.IF_ACMPEQ: - case Opcodes.IF_ACMPNE: - pop(2); - break; - default: - throw new IllegalArgumentException(); - } - mv.visitJumpInsn(opcode, label); - } - - @Override - public void visitIincInsn(final int var, final int increment) { - set(var, Opcodes.INTEGER); - mv.visitIincInsn(var, increment); - } - - @Override - public void visitTableSwitchInsn(final int min, final int max, - final Label dflt, final Label... labels) { - pop(1); - mv.visitTableSwitchInsn(min, max, dflt, labels); - } - - @Override - public void visitLookupSwitchInsn(final Label dflt, final int[] keys, - final Label[] labels) { - pop(1); - mv.visitLookupSwitchInsn(dflt, keys, labels); - } - - @Override - public void visitMultiANewArrayInsn(final String desc, final int dims) { - pop(dims); - push(desc); - mv.visitMultiANewArrayInsn(desc, dims); - } - - private void push(final Object type) { - stack = ensureSize(stack, stackSize + 1); - stack[stackSize] = type; - stackSize++; - } - - private void push(final Type type) { - switch (type.getSort()) { - case Type.VOID: - break; - case Type.BOOLEAN: - case Type.BYTE: - case Type.CHAR: - case Type.INT: - case Type.SHORT: - push(Opcodes.INTEGER); - break; - case Type.FLOAT: - push(Opcodes.FLOAT); - break; - case Type.LONG: - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Type.DOUBLE: - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Type.ARRAY: - case Type.OBJECT: - push(type.getInternalName()); - break; - default: - throw new AssertionError(type); - } - } - - private Object pop() { - stackSize--; - assertValidFrames(stackSize); - return stack[stackSize]; - } - - private void pop(final int count) { - stackSize -= count; - assertValidFrames(stackSize); - } - - private void assertValidFrames(final int stackSize) { - if (stackSize < 0) { - throw new IllegalStateException( - "Missing or invalid stackmap frames."); - } - } - - private void pop(final Type type) { - pop(type.getSize()); - } - - private void set(final int pos, final Object type) { - local = ensureSize(local, pos + 1); - // fill gaps: - for (int i = localSize; i < pos; i++) { - local[i] = Opcodes.TOP; - } - localSize = Math.max(localSize, pos + 1); - local[pos] = type; - } - - private void set(final int pos, final Type type) { - switch (type.getSort()) { - case Type.BOOLEAN: - case Type.BYTE: - case Type.CHAR: - case Type.INT: - case Type.SHORT: - set(pos, Opcodes.INTEGER); - break; - case Type.FLOAT: - set(pos, Opcodes.FLOAT); - break; - case Type.LONG: - set(pos, Opcodes.LONG); - set(pos + 1, Opcodes.TOP); - break; - case Type.DOUBLE: - set(pos, Opcodes.DOUBLE); - set(pos + 1, Opcodes.TOP); - break; - case Type.ARRAY: - case Type.OBJECT: - set(pos, type.getInternalName()); - break; - default: - throw new AssertionError(type); - } - } - - private Object get(final int pos) { - if (localSize <= pos) { - throw new IllegalStateException( - "Missing or invalid stackmap frames."); - } - return local[pos]; - } - - private Object[] ensureSize(final Object[] array, final int size) { - if (array.length >= size) { - return array; - } - int newLength = array.length; - while (newLength < size) { - newLength *= 2; - } - final Object[] newArray = new Object[newLength]; - System.arraycopy(array, 0, newArray, 0, array.length); - return newArray; - } - - /** - * Expand double word types into two slots. - */ - private int expand(final Object[] source, final int size, - final Object[] target) { - int targetIdx = 0; - for (int sourceIdx = 0; sourceIdx < size; sourceIdx++) { - final Object type = source[sourceIdx]; - target[targetIdx++] = type; - if (type == Opcodes.LONG || type == Opcodes.DOUBLE) { - target[targetIdx++] = Opcodes.TOP; - } - } - return targetIdx; - } - - /** - * Reduce double word types into a single slot. - */ - private int reduce(final Object[] source, final int size, - final Object[] target) { - int targetIdx = 0; - for (int sourceIdx = 0; sourceIdx < size; sourceIdx++) { - final Object type = source[sourceIdx]; - target[targetIdx++] = type; - if (type == Opcodes.LONG || type == Opcodes.DOUBLE) { - sourceIdx++; - } - } - return targetIdx; - } - - /** - * Replaces a type in the locals and on the stack. This is used for - * uninitialized objects. - * - * @param oldtype - * type to replace - * @param newtype - * replacement type - */ - private void replace(final Object oldtype, final Object newtype) { - for (int i = 0; i < localSize; i++) { - if (oldtype.equals(local[i])) { - local[i] = newtype; - } - } - for (int i = 0; i < stackSize; i++) { - if (oldtype.equals(stack[i])) { - stack[i] = newtype; - } - } - } - -} diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java index 808362d1..15b88140 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/MethodInstrumenter.java @@ -11,8 +11,9 @@ *******************************************************************************/ package org.jacoco.core.internal.instr; -import org.jacoco.core.internal.flow.MethodProbesVisitor; +import org.jacoco.core.internal.flow.IFrame; import org.jacoco.core.internal.flow.LabelInfo; +import org.jacoco.core.internal.flow.MethodProbesVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -24,7 +25,6 @@ import org.objectweb.asm.Opcodes; class MethodInstrumenter extends MethodProbesVisitor { private final IProbeInserter probeInserter; - private final IFrameInserter frameInserter; /** * Create a new instrumenter instance for the given method. @@ -33,15 +33,11 @@ class MethodInstrumenter extends MethodProbesVisitor { * next method visitor in the chain * @param probeInserter * call-back to insert probes where required - * @param frameInserter - * call-back to insert additional frames where required */ public MethodInstrumenter(final MethodVisitor mv, - final IProbeInserter probeInserter, - final IFrameInserter frameInserter) { + final IProbeInserter probeInserter) { super(mv); this.probeInserter = probeInserter; - this.frameInserter = frameInserter; } // === IMethodProbesVisitor === @@ -59,7 +55,7 @@ class MethodInstrumenter extends MethodProbesVisitor { @Override public void visitJumpInsnWithProbe(final int opcode, final Label label, - final int probeId) { + final int probeId, final IFrame frame) { if (opcode == Opcodes.GOTO) { probeInserter.insertProbe(probeId); mv.visitJumpInsn(Opcodes.GOTO, label); @@ -69,7 +65,7 @@ class MethodInstrumenter extends MethodProbesVisitor { probeInserter.insertProbe(probeId); mv.visitJumpInsn(Opcodes.GOTO, label); mv.visitLabel(intermediate); - frameInserter.insertFrame(); + frame.accept(mv); } } @@ -113,7 +109,7 @@ class MethodInstrumenter extends MethodProbesVisitor { @Override public void visitTableSwitchInsnWithProbes(final int min, final int max, - final Label dflt, final Label[] labels) { + final Label dflt, final Label[] labels, final IFrame frame) { // 1. Calculate intermediate labels: LabelInfo.resetDone(dflt); LabelInfo.resetDone(labels); @@ -122,12 +118,12 @@ class MethodInstrumenter extends MethodProbesVisitor { mv.visitTableSwitchInsn(min, max, newDflt, newLabels); // 2. Insert probes: - insertIntermediateProbes(dflt, labels); + insertIntermediateProbes(dflt, labels, frame); } @Override public void visitLookupSwitchInsnWithProbes(final Label dflt, - final int[] keys, final Label[] labels) { + final int[] keys, final Label[] labels, final IFrame frame) { // 1. Calculate intermediate labels: LabelInfo.resetDone(dflt); LabelInfo.resetDone(labels); @@ -136,7 +132,7 @@ class MethodInstrumenter extends MethodProbesVisitor { mv.visitLookupSwitchInsn(newDflt, keys, newLabels); // 2. Insert probes: - insertIntermediateProbes(dflt, labels); + insertIntermediateProbes(dflt, labels, frame); } private Label[] createIntermediates(final Label[] labels) { @@ -163,23 +159,24 @@ class MethodInstrumenter extends MethodProbesVisitor { return intermediate; } - private void insertIntermediateProbe(final Label label) { + private void insertIntermediateProbe(final Label label, final IFrame frame) { final int probeId = LabelInfo.getProbeId(label); if (probeId != LabelInfo.NO_PROBE && !LabelInfo.isDone(label)) { mv.visitLabel(LabelInfo.getIntermediateLabel(label)); - frameInserter.insertFrame(); + frame.accept(mv); probeInserter.insertProbe(probeId); mv.visitJumpInsn(Opcodes.GOTO, label); LabelInfo.setDone(label); } } - private void insertIntermediateProbes(final Label dflt, final Label[] labels) { + private void insertIntermediateProbes(final Label dflt, + final Label[] labels, final IFrame frame) { LabelInfo.resetDone(dflt); LabelInfo.resetDone(labels); - insertIntermediateProbe(dflt); + insertIntermediateProbe(dflt, frame); for (final Label l : labels) { - insertIntermediateProbe(l); + insertIntermediateProbe(l, frame); } } |