aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc R. Hoffmann <hoffmann@mountainminds.com>2014-05-01 06:15:21 +0200
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2014-05-01 06:15:21 +0200
commit72793f84314a393b86c5dc344499888b7113d20b (patch)
treefa2fe561a4e12ab1e225fb14569952f1e94851fe
parent9d79259f33e7117a4bf4ec1db2848e9429455325 (diff)
downloadplatform_external_jacoco-72793f84314a393b86c5dc344499888b7113d20b.tar.gz
platform_external_jacoco-72793f84314a393b86c5dc344499888b7113d20b.tar.bz2
platform_external_jacoco-72793f84314a393b86c5dc344499888b7113d20b.zip
GitHub #201: Fixed failure with default methods in in Java 8 interfaces
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/flow/ClassProbesAdapterTest.java19
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java20
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java229
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeCounterTest.java58
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java2
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java6
-rw-r--r--org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java28
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/flow/ClassProbesAdapter.java25
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java146
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/FieldProbeArrayStrategy.java137
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/IProbeArrayStrategy.java11
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java12
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java52
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java31
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java80
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java56
-rw-r--r--org.jacoco.doc/docroot/doc/changes.html2
17 files changed, 696 insertions, 218 deletions
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 7b1993c5..b4c94f1c 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
@@ -111,25 +111,6 @@ public class ClassProbesAdapterTest {
}
@Test
- public void testVisitInterfaceMethod() {
- final MockClassVisitor cv = new MockClassVisitor() {
- @Override
- public MethodProbesVisitor visitMethod(int access, String name,
- String desc, String signature, String[] exceptions) {
- return new MockMethodVisitor();
- }
- };
- 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, cv.count);
- adapter.visitEnd();
- assertEquals(1, cv.count);
- }
-
- @Test
public void testVisitMethodNullMethodVisitor() {
final MockClassVisitor cv = new MockClassVisitor();
final ClassProbesAdapter adapter = new ClassProbesAdapter(cv, false);
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
index 374a7d54..87aab3bc 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
@@ -14,8 +14,6 @@ package org.jacoco.core.internal.instr;
import static org.junit.Assert.assertNull;
import org.jacoco.core.JaCoCo;
-import org.jacoco.core.runtime.IRuntime;
-import org.jacoco.core.runtime.LoggerRuntime;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.ClassVisitor;
@@ -24,16 +22,13 @@ import org.objectweb.asm.MethodVisitor;
/**
* Unit tests for {@link ClassInstrumenter}.
*/
-public class ClassInstrumenterTest {
-
- private IRuntime runtime;
+public class ClassInstrumenterTest implements IProbeArrayStrategy {
private ClassInstrumenter instrumenter;
@Before
public void setup() {
- runtime = new LoggerRuntime();
- instrumenter = new ClassInstrumenter(123, runtime, new ClassVisitor(
+ instrumenter = new ClassInstrumenter(this, new ClassVisitor(
JaCoCo.ASM_API_VERSION) {
});
}
@@ -54,7 +49,7 @@ public class ClassInstrumenterTest {
@Test
public void testNoMethodVisitor() {
- instrumenter = new ClassInstrumenter(123, runtime, new ClassVisitor(
+ instrumenter = new ClassInstrumenter(this, new ClassVisitor(
JaCoCo.ASM_API_VERSION) {
@Override
public MethodVisitor visitMethod(int access, String name,
@@ -65,4 +60,13 @@ public class ClassInstrumenterTest {
assertNull(instrumenter.visitMethod(0, "foo", "()V", null, null));
}
+ // === IProbeArrayStrategy ===
+
+ public int storeInstance(MethodVisitor mv, int variable) {
+ return 0;
+ }
+
+ public void addMembers(ClassVisitor cv, int probeCount) {
+ }
+
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
new file mode 100644
index 00000000..a63b34f0
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 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.junit.Assert.assertNull;
+
+import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
+import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Unit tests for {@link ProbeArrayStrategyFactory} and the
+ * {@link IProbeArrayStrategy} implementations. The verifies the behaviour of
+ * the returned {@link IProbeArrayStrategy} instances for different classes.
+ */
+public class ProbeArrayStrategyFactoryTest {
+
+ private IExecutionDataAccessorGenerator generator;
+ private ClassVisitorMock cv;
+
+ @Before
+ public void setup() {
+ generator = new OfflineInstrumentationAccessGenerator();
+ cv = new ClassVisitorMock();
+ }
+
+ @Test
+ public void testClass1() {
+ test(Opcodes.V1_1, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(false);
+ }
+
+ @Test
+ public void testClass2() {
+ test(Opcodes.V1_2, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(false);
+ }
+
+ @Test
+ public void testClass3() {
+ test(Opcodes.V1_3, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(false);
+ }
+
+ @Test
+ public void testClass4() {
+ test(Opcodes.V1_4, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(false);
+ }
+
+ @Test
+ public void testClass5() {
+ test(Opcodes.V1_5, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(false);
+ }
+
+ @Test
+ public void testClass6() {
+ test(Opcodes.V1_6, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(true);
+ }
+
+ @Test
+ public void testClass7() {
+ test(Opcodes.V1_7, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(true);
+ }
+
+ @Test
+ public void testClass8() {
+ test(Opcodes.V1_8, 0, false, true);
+ assertDataField(InstrSupport.DATAFIELD_ACC);
+ assertInitMethod(true);
+ }
+
+ @Test
+ public void testInterface7() {
+ test(Opcodes.V1_7, Opcodes.ACC_INTERFACE, true, false);
+ assertNoDataField();
+ assertNoInitMethod();
+ }
+
+ @Test
+ public void testEmptyInterface7() {
+ test(Opcodes.V1_7, Opcodes.ACC_INTERFACE, false, false);
+ assertNoDataField();
+ assertNoInitMethod();
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testEmptyInterface7StoreInstance() {
+ IProbeArrayStrategy strategy = test(Opcodes.V1_7,
+ Opcodes.ACC_INTERFACE, false, false);
+ strategy.storeInstance(null, 0);
+ }
+
+ @Test
+ public void testInterface8() {
+ test(Opcodes.V1_8, Opcodes.ACC_INTERFACE, false, true);
+ assertDataField(InstrSupport.DATAFIELD_INTF_ACC);
+ assertInitMethod(true);
+ }
+
+ @Test
+ public void testEmptyInterface8() {
+ test(Opcodes.V1_8, Opcodes.ACC_INTERFACE, false, false);
+ assertNoDataField();
+ assertNoInitMethod();
+ }
+
+ @Test
+ public void testClinitInterface8() {
+ test(Opcodes.V1_8, Opcodes.ACC_INTERFACE, true, false);
+ assertNoDataField();
+ assertNoInitMethod();
+ }
+
+ private IProbeArrayStrategy test(int version, int access, boolean clinit,
+ boolean method) {
+ ClassWriter writer = new ClassWriter(0);
+ writer.visit(version, access, "Foo", "java/lang/Object", null, null);
+ if (clinit) {
+ MethodVisitor mv = writer.visitMethod(Opcodes.ACC_PUBLIC
+ | Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
+ mv.visitCode();
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ if (method) {
+ MethodVisitor mv = writer.visitMethod(Opcodes.ACC_PUBLIC
+ | Opcodes.ACC_STATIC, "doit", "()V", null, null);
+ mv.visitCode();
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ writer.visitEnd();
+
+ final IProbeArrayStrategy strategy = ProbeArrayStrategyFactory
+ .createFor(new ClassReader(writer.toByteArray()), generator);
+
+ strategy.addMembers(cv, 123);
+ return strategy;
+ }
+
+ private static class ClassVisitorMock extends ClassVisitor {
+
+ private int fieldAccess;
+ private String fieldName;
+
+ private int methodAccess;
+ private String methodName;
+
+ private boolean frames;
+
+ ClassVisitorMock() {
+ super(Opcodes.ASM5);
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value) {
+ assertNull(fieldName);
+ fieldAccess = access;
+ fieldName = name;
+ return null;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ assertNull(methodName);
+ methodAccess = access;
+ methodName = name;
+ return new MethodVisitor(Opcodes.ASM5) {
+ @Override
+ public void visitFrame(int type, int nLocal, Object[] local,
+ int nStack, Object[] stack) {
+ frames = true;
+ }
+ };
+ }
+ }
+
+ void assertDataField(int access) {
+ assertEquals(InstrSupport.DATAFIELD_NAME, cv.fieldName);
+ assertEquals(access, cv.fieldAccess);
+ }
+
+ void assertNoDataField() {
+ assertNull(cv.fieldName);
+ }
+
+ void assertInitMethod(boolean frames) {
+ assertEquals(InstrSupport.INITMETHOD_NAME, cv.methodName);
+ assertEquals(InstrSupport.INITMETHOD_ACC, cv.methodAccess);
+ assertEquals(Boolean.valueOf(frames), Boolean.valueOf(cv.frames));
+ }
+
+ void assertNoInitMethod() {
+ assertNull(cv.methodName);
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeCounterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeCounterTest.java
new file mode 100644
index 00000000..9b16d3b4
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeCounterTest.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 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.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link ProbeCounter}.
+ */
+public class ProbeCounterTest {
+
+ private ProbeCounter counter;
+
+ @Before
+ public void setup() {
+ counter = new ProbeCounter();
+ }
+
+ @Test
+ public void testInitial() {
+ assertFalse(counter.hasMethods());
+ assertEquals(0, counter.getCount());
+ }
+
+ @Test
+ public void testVisitTotalProbeCount() {
+ counter.visitTotalProbeCount(42);
+ assertEquals(42, counter.getCount());
+ }
+
+ @Test
+ public void testVisitClinitMethod() {
+ assertNull(counter.visitMethod(0, "<clinit>", null, null, null));
+ assertFalse(counter.hasMethods());
+ }
+
+ @Test
+ public void testVisitMethod() {
+ assertNull(counter.visitMethod(0, "foo", null, null, null));
+ assertTrue(counter.hasMethods());
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
index 47cd043c..44cde233 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
@@ -45,7 +45,7 @@ public class ProbeInserterTest {
return 5;
}
- public void addMembers(ClassVisitor delegate) {
+ public void addMembers(ClassVisitor delegate, int probeCount) {
}
};
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java
index aae5c288..71f08baa 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java
@@ -24,6 +24,7 @@ import static org.objectweb.asm.Opcodes.V1_4;
import static org.objectweb.asm.Opcodes.V1_5;
import static org.objectweb.asm.Opcodes.V1_6;
import static org.objectweb.asm.Opcodes.V1_7;
+import static org.objectweb.asm.Opcodes.V1_8;
import java.io.IOException;
@@ -77,6 +78,11 @@ public class ClassFileVersionsTest {
testVersion(V1_7, true);
}
+ @Test
+ public void test_1_8() throws IOException {
+ testVersion(V1_8, true);
+ }
+
private void testVersion(int version, boolean frames) throws IOException {
final byte[] original = createClass(version);
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 6d9c59f9..28c58ac8 100644
--- a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
+++ b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
@@ -23,9 +23,10 @@ import java.util.zip.ZipOutputStream;
import org.jacoco.core.internal.ContentTypeDetector;
import org.jacoco.core.internal.Pack200Streams;
-import org.jacoco.core.internal.data.CRC64;
import org.jacoco.core.internal.flow.ClassProbesAdapter;
import org.jacoco.core.internal.instr.ClassInstrumenter;
+import org.jacoco.core.internal.instr.IProbeArrayStrategy;
+import org.jacoco.core.internal.instr.ProbeArrayStrategyFactory;
import org.jacoco.core.internal.instr.SignatureRemover;
import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
import org.objectweb.asm.ClassReader;
@@ -37,7 +38,7 @@ import org.objectweb.asm.ClassWriter;
*/
public class Instrumenter {
- private final IExecutionDataAccessorGenerator accessGenerator;
+ private final IExecutionDataAccessorGenerator accessorGenerator;
private final SignatureRemover signatureRemover;
@@ -48,7 +49,7 @@ public class Instrumenter {
* runtime used by the instrumented classes
*/
public Instrumenter(final IExecutionDataAccessorGenerator runtime) {
- this.accessGenerator = runtime;
+ this.accessorGenerator = runtime;
this.signatureRemover = new SignatureRemover();
}
@@ -66,21 +67,6 @@ public class Instrumenter {
}
/**
- * Creates a ASM adapter for a class with the given id.
- *
- * @param classid
- * id of the class calculated with {@link CRC64}
- * @param cv
- * next class visitor in the chain
- * @return new visitor to write class definition to
- */
- private ClassVisitor createInstrumentingVisitor(final long classid,
- final ClassVisitor cv) {
- return new ClassProbesAdapter(new ClassInstrumenter(classid,
- accessGenerator, cv), true);
- }
-
- /**
* Creates a instrumented version of the given class if possible.
*
* @param reader
@@ -90,8 +76,10 @@ public class Instrumenter {
*/
public byte[] instrument(final ClassReader reader) {
final ClassWriter writer = new ClassWriter(reader, 0);
- final ClassVisitor visitor = createInstrumentingVisitor(
- CRC64.checksum(reader.b), writer);
+ final IProbeArrayStrategy strategy = ProbeArrayStrategyFactory
+ .createFor(reader, accessorGenerator);
+ final ClassVisitor visitor = new ClassProbesAdapter(
+ new ClassInstrumenter(strategy, writer), true);
reader.accept(visitor, ClassReader.EXPAND_FRAMES);
return writer.toByteArray();
}
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 5eb7f14c..2f658c6a 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,7 +15,6 @@ import org.jacoco.core.JaCoCo;
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;
/**
@@ -62,14 +61,6 @@ public class ClassProbesAdapter extends ClassVisitor implements
EMPTY_METHOD_PROBES_VISITOR = new Impl();
}
- private static class ProbeCounter implements IProbeIdGenerator {
- int count = 0;
-
- public int nextId() {
- return count++;
- }
- }
-
private final ClassProbesVisitor cv;
private final boolean trackFrames;
@@ -78,8 +69,6 @@ public class ClassProbesAdapter extends ClassVisitor implements
private String name;
- private boolean interfaceType;
-
/**
* Creates a new adapter that delegates to the given visitor.
*
@@ -100,7 +89,6 @@ public class ClassProbesAdapter extends ClassVisitor implements
final String signature, final String superName,
final String[] interfaces) {
this.name = name;
- this.interfaceType = (access & Opcodes.ACC_INTERFACE) != 0;
super.visit(version, access, name, signature, superName, interfaces);
}
@@ -124,15 +112,6 @@ public class ClassProbesAdapter extends ClassVisitor implements
public void visitEnd() {
super.visitEnd();
LabelFlowAnalyzer.markLabels(this);
- if (interfaceType) {
- final ProbeCounter probeCounter = new ProbeCounter();
- final MethodProbesAdapter adapter = new MethodProbesAdapter(
- EMPTY_METHOD_PROBES_VISITOR, probeCounter);
- // We do not use the accept() method as ASM resets labels
- // after every call to accept()
- instructions.accept(adapter);
- cv.visitTotalProbeCount(probeCounter.count);
- }
final MethodProbesAdapter probesAdapter = new MethodProbesAdapter(
methodProbes, ClassProbesAdapter.this);
if (trackFrames) {
@@ -150,9 +129,7 @@ public class ClassProbesAdapter extends ClassVisitor implements
@Override
public void visitEnd() {
- if (!interfaceType) {
- cv.visitTotalProbeCount(counter);
- }
+ cv.visitTotalProbeCount(counter);
super.visitEnd();
}
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 dd1bf899..a1aec588 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
@@ -13,50 +13,32 @@ package org.jacoco.core.internal.instr;
import org.jacoco.core.internal.flow.ClassProbesVisitor;
import org.jacoco.core.internal.flow.MethodProbesVisitor;
-import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
/**
* Adapter that instruments a class for coverage tracing.
*/
public class ClassInstrumenter extends ClassProbesVisitor {
- private static final Object[] STACK_ARRZ = new Object[] { InstrSupport.DATAFIELD_DESC };
- private static final Object[] NO_LOCALS = new Object[0];
-
- private final long id;
-
- private final IExecutionDataAccessorGenerator accessorGenerator;
-
- private IProbeArrayStrategy probeArrayStrategy;
+ private final IProbeArrayStrategy probeArrayStrategy;
private String className;
- private boolean withFrames;
-
- private int probeCount;
-
/**
* Emits a instrumented version of this class to the given class visitor.
*
- * @param id
- * unique identifier given to this class
- * @param accessorGenerator
- * this generator will be used for instrumentation
+ * @param probeArrayStrategy
+ * this strategy will be used to access the probe array
* @param cv
* next delegate in the visitor chain will receive the
* instrumented class
*/
- public ClassInstrumenter(final long id,
- final IExecutionDataAccessorGenerator accessorGenerator,
+ public ClassInstrumenter(final IProbeArrayStrategy probeArrayStrategy,
final ClassVisitor cv) {
super(cv);
- this.id = id;
- this.accessorGenerator = accessorGenerator;
+ this.probeArrayStrategy = probeArrayStrategy;
}
@Override
@@ -64,12 +46,6 @@ public class ClassInstrumenter extends ClassProbesVisitor {
final String signature, final String superName,
final String[] interfaces) {
this.className = name;
- withFrames = (version & 0xff) >= Opcodes.V1_6;
- if ((access & Opcodes.ACC_INTERFACE) == 0) {
- this.probeArrayStrategy = new ClassTypeStrategy();
- } else {
- this.probeArrayStrategy = new InterfaceTypeStrategy();
- }
super.visit(version, access, name, signature, superName, interfaces);
}
@@ -101,117 +77,7 @@ public class ClassInstrumenter extends ClassProbesVisitor {
@Override
public void visitTotalProbeCount(final int count) {
- probeCount = count;
- }
-
- @Override
- public void visitEnd() {
- probeArrayStrategy.addMembers(cv);
- super.visitEnd();
- }
-
- // === probe array strategies ===
-
- private class ClassTypeStrategy implements IProbeArrayStrategy {
-
- public int storeInstance(final MethodVisitor mv, final int variable) {
- mv.visitMethodInsn(Opcodes.INVOKESTATIC, className,
- InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
- false);
- mv.visitVarInsn(Opcodes.ASTORE, variable);
- return 1;
- }
-
- public void addMembers(final ClassVisitor delegate) {
- createDataField();
- createInitMethod(probeCount);
- }
-
- private void createDataField() {
- cv.visitField(InstrSupport.DATAFIELD_ACC,
- InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC,
- null, null);
- }
-
- private void createInitMethod(final int probeCount) {
- final MethodVisitor mv = cv.visitMethod(
- InstrSupport.INITMETHOD_ACC, InstrSupport.INITMETHOD_NAME,
- InstrSupport.INITMETHOD_DESC, null, null);
- mv.visitCode();
-
- // Load the value of the static data field:
- mv.visitFieldInsn(Opcodes.GETSTATIC, className,
- InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC);
- mv.visitInsn(Opcodes.DUP);
-
- // Stack[1]: [Z
- // Stack[0]: [Z
-
- // Skip initialization when we already have a data array:
- final Label alreadyInitialized = new Label();
- mv.visitJumpInsn(Opcodes.IFNONNULL, alreadyInitialized);
-
- // Stack[0]: [Z
-
- mv.visitInsn(Opcodes.POP);
- final int size = genInitializeDataField(mv, probeCount);
-
- // Stack[0]: [Z
-
- // Return the class' probe array:
- if (withFrames) {
- mv.visitFrame(Opcodes.F_NEW, 0, NO_LOCALS, 1, STACK_ARRZ);
- }
- mv.visitLabel(alreadyInitialized);
- mv.visitInsn(Opcodes.ARETURN);
-
- mv.visitMaxs(Math.max(size, 2), 0); // Maximum local stack size is 2
- mv.visitEnd();
- }
-
- /**
- * Generates the byte code to initialize the static coverage data field
- * within this class.
- *
- * The code will push the [Z data array on the operand stack.
- *
- * @param mv
- * generator to emit code to
- */
- private int genInitializeDataField(final MethodVisitor mv,
- final int probeCount) {
- final int size = accessorGenerator.generateDataAccessor(id,
- className, probeCount, mv);
-
- // Stack[0]: [Z
-
- mv.visitInsn(Opcodes.DUP);
-
- // Stack[1]: [Z
- // Stack[0]: [Z
-
- mv.visitFieldInsn(Opcodes.PUTSTATIC, className,
- InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC);
-
- // Stack[0]: [Z
-
- return Math.max(size, 2); // Maximum local stack size is 2
- }
- }
-
- private class InterfaceTypeStrategy implements IProbeArrayStrategy {
-
- public int storeInstance(final MethodVisitor mv, final int variable) {
- final int maxStack = accessorGenerator.generateDataAccessor(id,
- className, probeCount, mv);
- mv.visitVarInsn(Opcodes.ASTORE, variable);
- return maxStack;
- }
-
- public void addMembers(final ClassVisitor delegate) {
- // nothing to do
- }
-
+ probeArrayStrategy.addMembers(cv, count);
}
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/FieldProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/FieldProbeArrayStrategy.java
new file mode 100644
index 00000000..1a53da2b
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/FieldProbeArrayStrategy.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 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.runtime.IExecutionDataAccessorGenerator;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * The strategy for regular classes and Java 8 interfaces which adds a static
+ * field to hold the probe array and a static initialization method requesting
+ * the probe array from the runtime.
+ */
+class FieldProbeArrayStrategy implements IProbeArrayStrategy {
+
+ /**
+ * Frame stack with a single boolean array.
+ */
+ public static final Object[] FRAME_STACK_ARRZ = new Object[] { InstrSupport.DATAFIELD_DESC };
+
+ /**
+ * Empty frame locals.
+ */
+ public static final Object[] FRAME_LOCALS_EMPTY = new Object[0];
+
+ private final String className;
+ private final long classId;
+ private final boolean withFrames;
+ private final int fieldAccess;
+ private final IExecutionDataAccessorGenerator accessorGenerator;
+
+ FieldProbeArrayStrategy(final String className, final long classId,
+ final boolean withFrames, final int fieldAccess,
+ final IExecutionDataAccessorGenerator accessorGenerator) {
+ this.className = className;
+ this.classId = classId;
+ this.withFrames = withFrames;
+ this.fieldAccess = fieldAccess;
+ this.accessorGenerator = accessorGenerator;
+ }
+
+ public int storeInstance(final MethodVisitor mv, final int variable) {
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, className,
+ InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
+ false);
+ mv.visitVarInsn(Opcodes.ASTORE, variable);
+ return 1;
+ }
+
+ public void addMembers(final ClassVisitor cv, final int probeCount) {
+ createDataField(cv);
+ createInitMethod(cv, probeCount);
+ }
+
+ private void createDataField(final ClassVisitor cv) {
+ cv.visitField(fieldAccess, InstrSupport.DATAFIELD_NAME,
+ InstrSupport.DATAFIELD_DESC, null, null);
+ }
+
+ private void createInitMethod(final ClassVisitor cv, final int probeCount) {
+ final MethodVisitor mv = cv.visitMethod(InstrSupport.INITMETHOD_ACC,
+ InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
+ null, null);
+ mv.visitCode();
+
+ // Load the value of the static data field:
+ mv.visitFieldInsn(Opcodes.GETSTATIC, className,
+ InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC);
+ mv.visitInsn(Opcodes.DUP);
+
+ // Stack[1]: [Z
+ // Stack[0]: [Z
+
+ // Skip initialization when we already have a data array:
+ final Label alreadyInitialized = new Label();
+ mv.visitJumpInsn(Opcodes.IFNONNULL, alreadyInitialized);
+
+ // Stack[0]: [Z
+
+ mv.visitInsn(Opcodes.POP);
+ final int size = genInitializeDataField(mv, probeCount);
+
+ // Stack[0]: [Z
+
+ // Return the class' probe array:
+ if (withFrames) {
+ mv.visitFrame(Opcodes.F_NEW, 0, FRAME_LOCALS_EMPTY, 1,
+ FRAME_STACK_ARRZ);
+ }
+ mv.visitLabel(alreadyInitialized);
+ mv.visitInsn(Opcodes.ARETURN);
+
+ mv.visitMaxs(Math.max(size, 2), 0); // Maximum local stack size is 2
+ mv.visitEnd();
+ }
+
+ /**
+ * Generates the byte code to initialize the static coverage data field
+ * within this class.
+ *
+ * The code will push the [Z data array on the operand stack.
+ *
+ * @param mv
+ * generator to emit code to
+ */
+ private int genInitializeDataField(final MethodVisitor mv,
+ final int probeCount) {
+ final int size = accessorGenerator.generateDataAccessor(classId,
+ className, probeCount, mv);
+
+ // Stack[0]: [Z
+
+ mv.visitInsn(Opcodes.DUP);
+
+ // Stack[1]: [Z
+ // Stack[0]: [Z
+
+ mv.visitFieldInsn(Opcodes.PUTSTATIC, className,
+ InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC);
+
+ // Stack[0]: [Z
+
+ return Math.max(size, 2); // Maximum local stack size is 2
+ }
+
+} \ No newline at end of file
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 4858c7b0..274242ad 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
@@ -19,7 +19,7 @@ import org.objectweb.asm.MethodVisitor;
* type. This abstraction is required as we need to follow a different strategy
* depending on whether the instrumented type is a class or interface.
*/
-interface IProbeArrayStrategy {
+public interface IProbeArrayStrategy {
/**
* Creates code that stores the probe array instance in the given variable.
@@ -33,11 +33,14 @@ interface IProbeArrayStrategy {
int storeInstance(MethodVisitor mv, int variable);
/**
- * Adds additional class members required by this strategy.
+ * Adds additional class members required by this strategy. This method is
+ * called after all original members of the class has been processed.
*
- * @param delegate
+ * @param cv
* visitor to create fields and classes
+ * @param probeCount
+ * total number of probes required for this class
*/
- void addMembers(ClassVisitor delegate);
+ void addMembers(ClassVisitor cv, int probeCount);
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
index 01fda031..a00cf4c0 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
@@ -36,7 +36,15 @@ public final class InstrSupport {
* class.
*/
public static final int DATAFIELD_ACC = Opcodes.ACC_SYNTHETIC
- | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT;
+ | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT
+ | Opcodes.ACC_FINAL;
+
+ /**
+ * Access modifiers of the field that stores coverage information of a Java
+ * 8 interface.
+ */
+ public static final int DATAFIELD_INTF_ACC = Opcodes.ACC_SYNTHETIC
+ | Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
/**
* Data type of the field that stores coverage information for a class (
@@ -60,7 +68,7 @@ public final class InstrSupport {
* Access modifiers of the initialization method.
*/
public static final int INITMETHOD_ACC = Opcodes.ACC_SYNTHETIC
- | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
+ | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
/**
* Ensures that the given member does not correspond to a internal member
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java
new file mode 100644
index 00000000..bccf42ec
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/LocalProbeArrayStrategy.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 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.runtime.IExecutionDataAccessorGenerator;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * The strategy for interfaces inlines the runtime access directly into the
+ * methods as this is the only method without keeping reference within this
+ * class. This is very inefficient as the runtime is contacted for every method
+ * invocation and therefore only used for static initializers in interfaces.
+ */
+class LocalProbeArrayStrategy implements IProbeArrayStrategy {
+
+ private final String className;
+ private final long classId;
+ private final int probeCount;
+ private final IExecutionDataAccessorGenerator accessorGenerator;
+
+ LocalProbeArrayStrategy(final String className, final long classId,
+ final int probeCount,
+ final IExecutionDataAccessorGenerator accessorGenerator) {
+ this.className = className;
+ this.classId = classId;
+ this.probeCount = probeCount;
+ this.accessorGenerator = accessorGenerator;
+ }
+
+ public int storeInstance(final MethodVisitor mv, final int variable) {
+ final int maxStack = accessorGenerator.generateDataAccessor(classId,
+ className, probeCount, mv);
+ mv.visitVarInsn(Opcodes.ASTORE, variable);
+ return maxStack;
+ }
+
+ public void addMembers(final ClassVisitor delegate, final int probeCount) {
+ // nothing to do
+ }
+
+} \ No newline at end of file
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java
new file mode 100644
index 00000000..647623cc
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/NoneProbeArrayStrategy.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 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.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * The strategy does not emit any code at all. This is used for interface types
+ * without any code.
+ */
+class NoneProbeArrayStrategy implements IProbeArrayStrategy {
+
+ public int storeInstance(final MethodVisitor mv, final int variable) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addMembers(final ClassVisitor delegate, final int probeCount) {
+ // nothing to do
+ }
+
+} \ No newline at end of file
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java
new file mode 100644
index 00000000..8a52df8d
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 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.internal.data.CRC64;
+import org.jacoco.core.internal.flow.ClassProbesAdapter;
+import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Factory to find a suitable strategy to access the probe array for a given
+ * class.
+ */
+public final class ProbeArrayStrategyFactory {
+
+ private ProbeArrayStrategyFactory() {
+ }
+
+ /**
+ * Creates a suitable strategy instance for the class described by the given
+ * reader.
+ *
+ * @param reader
+ * reader to get information about the class
+ * @param accessorGenerator
+ * accessor to the coverage runtime
+ * @return strategy instance
+ */
+ public static IProbeArrayStrategy createFor(final ClassReader reader,
+ final IExecutionDataAccessorGenerator accessorGenerator) {
+
+ final String className = reader.getClassName();
+ final int version = getVersion(reader);
+ final long classId = CRC64.checksum(reader.b);
+ final boolean withFrames = version >= Opcodes.V1_6;
+
+ if (isInterface(reader)) {
+ final ProbeCounter counter = getProbeCounter(reader);
+ if (counter.getCount() == 0) {
+ return new NoneProbeArrayStrategy();
+ }
+ if (version >= Opcodes.V1_8 && counter.hasMethods()) {
+ return new FieldProbeArrayStrategy(className, classId,
+ withFrames, InstrSupport.DATAFIELD_INTF_ACC,
+ accessorGenerator);
+ } else {
+ return new LocalProbeArrayStrategy(className, classId,
+ counter.getCount(), accessorGenerator);
+ }
+ } else {
+ return new FieldProbeArrayStrategy(className, classId, withFrames,
+ InstrSupport.DATAFIELD_ACC, accessorGenerator);
+ }
+ }
+
+ private static boolean isInterface(final ClassReader reader) {
+ return (reader.getAccess() & Opcodes.ACC_INTERFACE) != 0;
+ }
+
+ private static int getVersion(final ClassReader reader) {
+ return reader.readShort(6);
+ }
+
+ private static ProbeCounter getProbeCounter(final ClassReader reader) {
+ final ProbeCounter counter = new ProbeCounter();
+ reader.accept(new ClassProbesAdapter(counter, false), 0);
+ return counter;
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java
new file mode 100644
index 00000000..a54505fd
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeCounter.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2014 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.internal.flow.ClassProbesVisitor;
+import org.jacoco.core.internal.flow.MethodProbesVisitor;
+
+/**
+ * Internal class to remember the total number of probes required for a class.
+ */
+class ProbeCounter extends ClassProbesVisitor {
+
+ private int count;
+ private boolean methods;
+
+ ProbeCounter() {
+ count = 0;
+ methods = false;
+ }
+
+ @Override
+ public MethodProbesVisitor visitMethod(final int access, final String name,
+ final String desc, final String signature, final String[] exceptions) {
+ if (!"<clinit>".equals(name)) {
+ methods = true;
+ }
+ return null;
+ }
+
+ @Override
+ public void visitTotalProbeCount(final int count) {
+ this.count = count;
+ }
+
+ int getCount() {
+ return count;
+ }
+
+ /**
+ * @return <code>true</code> if the class has other methods than a static
+ * initializer
+ */
+ boolean hasMethods() {
+ return methods;
+ }
+
+}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index afd9b7ff..c160d963 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -22,6 +22,8 @@
<h3>Fixed Bugs</h3>
<ul>
+ <li>Fixed failure with default methods in in Java 8 interfaces.
+ (GitHub <a href="https://github.com/jacoco/jacoco/issues/201">#201</a>).</li>
<li>Better interoperability with JMockit, analysis and fix contributed by Rogério
Liesenfeld (GitHub <a href="https://github.com/jacoco/jacoco/issues/35">#35</a>
and (GitHub <a href="https://github.com/jacoco/jacoco/issues/54">#54</a>).</li>