SF #3161106: Allow any number of probes in static interface initializers

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 321026a..b2c6c00 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
@@ -53,7 +53,7 @@
 	}

 

 	@Test

-	public void testVisitMethod() {

+	public void testVisitClassMethods() {

 		final MockVisitor mv = new MockVisitor() {

 			@Override

 			public IMethodProbesVisitor visitMethod(int access, String name,

@@ -82,14 +82,56 @@
 			}

 		};

 		final ClassProbesAdapter adapter = new ClassProbesAdapter(mv);

+		adapter.visit(Opcodes.V1_5, 0, "Foo", null, "java/lang/Object", null);

 		writeMethod(adapter);

 		writeMethod(adapter);

 		writeMethod(adapter);

+

+		assertEquals(0, mv.count);

 		adapter.visitEnd();

 		assertEquals(3, mv.count);

 	}

 

 	@Test

+	public void testVisitInterfaceMethod() {

+		final MockVisitor mv = new MockVisitor() {

+			@Override

+			public IMethodProbesVisitor visitMethod(int access, String name,

+					String desc, String signature, String[] exceptions) {

+				class MockMethodVisitor extends EmptyVisitor implements

+						IMethodProbesVisitor {

+					public void visitProbe(int probeId) {

+					}

+

+					public void visitJumpInsnWithProbe(int opcode, Label label,

+							int probeId) {

+					}

+

+					public void visitInsnWithProbe(int opcode, int probeId) {

+					}

+

+					public void visitTableSwitchInsnWithProbes(int min,

+							int max, Label dflt, Label[] labels) {

+					}

+

+					public void visitLookupSwitchInsnWithProbes(Label dflt,

+							int[] keys, Label[] labels) {

+					}

+				}

+				return new MockMethodVisitor();

+			}

+		};

+		final ClassProbesAdapter adapter = new ClassProbesAdapter(mv);

+		adapter.visit(Opcodes.V1_5, Opcodes.ACC_INTERFACE, "Foo", null,

+				"java/lang/Object", null);

+		writeMethod(adapter);

+

+		assertEquals(1, mv.count);

+		adapter.visitEnd();

+		assertEquals(1, mv.count);

+	}

+

+	@Test

 	public void testVisitMethodNullMethodVisitor() {

 		final MockVisitor mv = new MockVisitor();

 		final ClassProbesAdapter adapter = new ClassProbesAdapter(mv);

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 f8ac8b1..eda8f6d 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
@@ -40,13 +40,14 @@
 		actual = new MethodRecorder();

 		expected = new MethodRecorder();

 		probeArrayStrategy = new IProbeArrayStrategy() {

+

 			public int pushInstance(MethodVisitor mv) {

 				mv.visitMethodInsn(Opcodes.INVOKESTATIC, "Target",

 						"$jacocoInit", "()[Z");

 				return 1;

 			}

 

-			public void addMembers(ClassVisitor delegate, int probeCount) {

+			public void addMembers(ClassVisitor delegate) {

 			}

 		};

 		instrumenter = new MethodInstrumenter(actual, 0, "()V",

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 60e4cb3..fe09dfb 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
@@ -15,6 +15,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.EmptyVisitor;

 import org.objectweb.asm.commons.JSRInlinerAdapter;

 

@@ -50,10 +51,20 @@
 		EMPTY_BLOCK_METHOD_VISITOR = new Impl();

 	}

 

+	private static class ProbeCounter implements IProbeIdGenerator {

+		int count = 0;

+

+		public int nextId() {

+			return count++;

+		}

+	}

+

 	private final IClassProbesVisitor cv;

 

 	private int counter = 0;

 

+	private boolean interfaceType;

+

 	/**

 	 * Creates a new adapter that delegates to the given visitor.

 	 * 

@@ -66,6 +77,14 @@
 	}

 

 	@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;

+		super.visit(version, access, name, signature, superName, interfaces);

+	}

+

+	@Override

 	public final MethodVisitor visitMethod(final int access, final String name,

 			final String desc, final String signature, final String[] exceptions) {

 		final IMethodProbesVisitor methodProbes;

@@ -84,6 +103,12 @@
 			public void visitEnd() {

 				super.visitEnd();

 				this.accept(new LabelFlowAnalyzer());

+				if (interfaceType) {

+					final ProbeCounter counter = new ProbeCounter();

+					this.accept(new MethodProbesAdapter(

+							EMPTY_BLOCK_METHOD_VISITOR, counter));

+					cv.visitTotalProbeCount(counter.count);

+				}

 				this.accept(new MethodProbesAdapter(methodProbes,

 						ClassProbesAdapter.this));

 			}

@@ -92,7 +117,9 @@
 

 	@Override

 	public void visitEnd() {

-		cv.visitTotalProbeCount(counter);

+		if (!interfaceType) {

+			cv.visitTotalProbeCount(counter);

+		}

 		super.visitEnd();

 	}

 

diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/IClassProbesVisitor.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/IClassProbesVisitor.java
index 67fcb7c..afb4a55 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/IClassProbesVisitor.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/IClassProbesVisitor.java
@@ -23,8 +23,10 @@
 			String desc, String signature, String[] exceptions);

 

 	/**

-	 * Reports the total number of encountered probes. This method is called

-	 * just before {@link ClassVisitor#visitEnd()}.

+	 * Reports the total number of encountered probes. For classes this method

+	 * is called just before {@link ClassVisitor#visitEnd()}. For interfaces

+	 * this method is called before the first method (the static initializer) is

+	 * emitted.

 	 * 

 	 * @param count

 	 *            total number of probes

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 1c5a126..15e7e01 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
@@ -39,6 +39,8 @@
 

 	private String className;

 

+	private int probeCount;

+

 	/**

 	 * Emits a instrumented version of this class to the given class visitor.

 	 * 

@@ -95,7 +97,13 @@
 	}

 

 	public void visitTotalProbeCount(final int count) {

-		probeArrayStrategy.addMembers(cv, count);

+		probeCount = count;

+	}

+

+	@Override

+	public void visitEnd() {

+		probeArrayStrategy.addMembers(cv);

+		super.visitEnd();

 	}

 

 	/**

@@ -129,7 +137,7 @@
 			return 1;

 		}

 

-		public void addMembers(final ClassVisitor delegate, final int probeCount) {

+		public void addMembers(final ClassVisitor delegate) {

 			createDataField();

 			createInitMethod(probeCount);

 		}

@@ -207,17 +215,11 @@
 	private class InterfaceTypeStrategy implements IProbeArrayStrategy {

 

 		public int pushInstance(final MethodVisitor mv) {

-			// TODO As we don't know the actual probe count at this point in

-			// time use a hard coded value of 64. This is typically be way too

-			// big and will break static initializers in interfaces with more

-			// than 64 blocks. So this has to be replaced with the actual probe

-			// count.

-			final int size = accessorGenerator.generateDataAccessor(id,

-					className, 64, mv);

-			return size;

+			return accessorGenerator.generateDataAccessor(id, className,

+					probeCount, mv);

 		}

 

-		public void addMembers(final ClassVisitor delegate, final int probeCount) {

+		public void addMembers(final ClassVisitor delegate) {

 		}

 

 	}

diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java
index f24fafb..1a9110a 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java
@@ -35,9 +35,7 @@
 	 * 

 	 * @param delegate

 	 *            visitor to create fields and classes

-	 * @param probeCount

-	 *            total number of probes inserted into this class

 	 */

-	void addMembers(ClassVisitor delegate, int probeCount);

+	void addMembers(ClassVisitor delegate);

 

 }

diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index f7bd4fe..c9ad3b7 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -33,6 +33,7 @@
   <li>Try to avoid interference with Hibernate (SF #3134190).</li>

   <li>Provide proper error message in case of duplicate class names in the same

       group (SF #3110219).</li>

+  <li>Allow any number of probes in static interface initializers (SF #3161106).</li>

 </ul>

 

 <h3>API Changes</h3>