aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Mandrikov <138671+Godin@users.noreply.github.com>2019-03-05 06:53:49 +0100
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2019-03-05 06:53:49 +0100
commit36b4e9c7103441d556a1667b4485960f5bdfeff8 (patch)
treefdf32b3ba388e592850d6a2c5e7746b8491dcfe2
parentfaf49f9418f7bc8f1a198de3afd941ff953d3abe (diff)
downloadplatform_external_jacoco-36b4e9c7103441d556a1667b4485960f5bdfeff8.tar.gz
platform_external_jacoco-36b4e9c7103441d556a1667b4485960f5bdfeff8.tar.bz2
platform_external_jacoco-36b4e9c7103441d556a1667b4485960f5bdfeff8.zip
Use condy for probes array in Java 11+ class files (#845)
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java12
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java15
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java100
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java76
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategy.java73
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java8
-rw-r--r--org.jacoco.doc/docroot/doc/changes.html3
7 files changed, 268 insertions, 19 deletions
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java
index ff49aa8d..76f31b83 100644
--- a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java
@@ -33,7 +33,7 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
/**
* Unit tests for {@link Instrument}.
@@ -138,16 +138,16 @@ public class InstrumentTest extends CommandTestBase {
final ClassReader reader = InstrSupport
.classReaderFor(InputStreams.readFully(in));
in.close();
- final Set<String> fields = new HashSet<String>();
+ final Set<String> methods = new HashSet<String>();
reader.accept(new ClassVisitor(InstrSupport.ASM_API_VERSION) {
@Override
- public FieldVisitor visitField(int access, String name, String desc,
- String signature, Object value) {
- fields.add(name);
+ public MethodVisitor visitMethod(int access, String name,
+ String descriptor, String signature, String[] exceptions) {
+ methods.add(name);
return null;
}
}, 0);
- assertTrue(fields.contains("$jacocoData"));
+ assertTrue(methods.contains("$jacocoInit"));
}
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java
index f1f8d16c..9a9bd557 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/instr/ClassFileVersionsTest.java
@@ -12,6 +12,7 @@
package org.jacoco.core.instr;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ALOAD;
@@ -37,6 +38,7 @@ import static org.objectweb.asm.Opcodes.V9;
import java.io.IOException;
+import org.jacoco.core.internal.instr.CondyProbeArrayStrategy;
import org.jacoco.core.internal.instr.InstrSupport;
import org.jacoco.core.runtime.IRuntime;
import org.jacoco.core.runtime.SystemPropertiesRuntime;
@@ -133,7 +135,7 @@ public class ClassFileVersionsTest {
@Override
public MethodVisitor visitMethod(int access, String name,
- String desc, String signature,
+ final String desc, String signature,
String[] exceptions) {
return new MethodVisitor(InstrSupport.ASM_API_VERSION) {
boolean frames = false;
@@ -147,8 +149,15 @@ public class ClassFileVersionsTest {
@Override
public void visitEnd() {
- assertEquals(Boolean.valueOf(expected),
- Boolean.valueOf(frames));
+ if (CondyProbeArrayStrategy.B_DESC
+ .equals(desc)) {
+ assertFalse(
+ "CondyProbeArrayStrategy does not need frames",
+ frames);
+ } else {
+ assertEquals(Boolean.valueOf(expected),
+ Boolean.valueOf(frames));
+ }
}
};
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java
new file mode 100644
index 00000000..6397a59b
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategyTest.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2019 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:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.instr;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ConstantDynamic;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+import org.objectweb.asm.tree.VarInsnNode;
+
+public class CondyProbeArrayStrategyTest {
+
+ private CondyProbeArrayStrategy strategy;
+
+ @Before
+ public void setup() {
+ strategy = new CondyProbeArrayStrategy("ClassName", true, 1L,
+ new OfflineInstrumentationAccessGenerator());
+ }
+
+ @Test
+ public void should_store_instance_using_condy_and_checkcast() {
+ final MethodNode m = new MethodNode();
+ final int maxStack = strategy.storeInstance(m, false, 1);
+
+ assertEquals(1, maxStack);
+
+ final ConstantDynamic constantDynamic = (ConstantDynamic) ((LdcInsnNode) m.instructions
+ .get(0)).cst;
+ assertEquals("$jacocoData", constantDynamic.getName());
+ assertEquals("Ljava/lang/Object;", constantDynamic.getDescriptor());
+
+ final Handle bootstrapMethod = constantDynamic.getBootstrapMethod();
+ assertEquals(Opcodes.H_INVOKESTATIC, bootstrapMethod.getTag());
+ assertEquals("ClassName", bootstrapMethod.getOwner());
+ assertEquals("$jacocoInit", bootstrapMethod.getName());
+ assertEquals(
+ "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)[Z",
+ bootstrapMethod.getDesc());
+ assertTrue(bootstrapMethod.isInterface());
+
+ final TypeInsnNode castInstruction = (TypeInsnNode) m.instructions
+ .get(1);
+ assertEquals(Opcodes.CHECKCAST, castInstruction.getOpcode());
+ assertEquals("[Z", castInstruction.desc);
+
+ final VarInsnNode storeInstruction = (VarInsnNode) m.instructions
+ .get(2);
+ assertEquals(Opcodes.ASTORE, storeInstruction.getOpcode());
+ assertEquals(1, storeInstruction.var);
+
+ assertEquals(3, m.instructions.size());
+ }
+
+ @Test
+ public void should_not_add_fields() {
+ final ClassNode c = new ClassNode();
+ strategy.addMembers(c, 1);
+
+ assertEquals(0, c.fields.size());
+ }
+
+ @Test
+ public void should_add_bootstrap_method() {
+ final ClassNode c = new ClassNode();
+ strategy.addMembers(c, 1);
+
+ assertEquals(1, c.methods.size());
+
+ final MethodNode m = c.methods.get(0);
+ assertEquals(Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_STATIC, m.access);
+ assertEquals("$jacocoInit", m.name);
+ assertEquals(
+ "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)[Z",
+ m.desc);
+
+ assertEquals(4, m.maxStack);
+ assertEquals(3, m.maxLocals);
+ }
+
+}
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
index 1354b60f..f1d1282a 100644
--- 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
@@ -212,16 +212,65 @@ public class ProbeArrayStrategyFactoryTest {
}
@Test
- public void testModule() {
+ public void test_java9_module() {
+ final IProbeArrayStrategy strategy = createForModule(Opcodes.V9);
+ assertEquals(NoneProbeArrayStrategy.class, strategy.getClass());
+ }
+
+ @Test
+ public void test_java11_class() {
+ final IProbeArrayStrategy strategy = test(Opcodes.V11, 0, true, true,
+ true);
+
+ assertEquals(CondyProbeArrayStrategy.class, strategy.getClass());
+ assertNoDataField();
+ assertCondyBootstrapMethod();
+ }
+
+ @Test
+ public void test_java11_interface_with_clinit_and_methods() {
+ final IProbeArrayStrategy strategy = test(Opcodes.V11,
+ Opcodes.ACC_INTERFACE, true, true, true);
+
+ assertEquals(CondyProbeArrayStrategy.class, strategy.getClass());
+ assertNoDataField();
+ assertCondyBootstrapMethod();
+ }
+
+ @Test
+ public void test_java11_interface_with_clinit() {
+ final IProbeArrayStrategy strategy = test(Opcodes.V11,
+ Opcodes.ACC_INTERFACE, true, false, true);
+
+ assertEquals(LocalProbeArrayStrategy.class, strategy.getClass());
+ assertNoDataField();
+ assertNoInitMethod();
+ }
+
+ @Test
+ public void test_java11_interface_without_code() {
+ final IProbeArrayStrategy strategy = test(Opcodes.V11,
+ Opcodes.ACC_INTERFACE, false, false, true);
+
+ assertEquals(NoneProbeArrayStrategy.class, strategy.getClass());
+ assertNoDataField();
+ assertNoInitMethod();
+ }
+
+ @Test
+ public void test_java11_module() {
+ final IProbeArrayStrategy strategy = createForModule(Opcodes.V11);
+ assertEquals(NoneProbeArrayStrategy.class, strategy.getClass());
+ }
+
+ private IProbeArrayStrategy createForModule(int version) {
final ClassWriter writer = new ClassWriter(0);
- writer.visit(Opcodes.V9, Opcodes.ACC_MODULE, "module-info", null, null,
+ writer.visit(version, Opcodes.ACC_MODULE, "module-info", null, null,
null);
writer.visitModule("module", 0, null).visitEnd();
writer.visitEnd();
-
- final IProbeArrayStrategy strategy = ProbeArrayStrategyFactory
- .createFor(0, new ClassReader(writer.toByteArray()), generator);
- assertEquals(NoneProbeArrayStrategy.class, strategy.getClass());
+ return ProbeArrayStrategyFactory.createFor(0,
+ new ClassReader(writer.toByteArray()), generator);
}
private IProbeArrayStrategy test(int version, int access, boolean clinit,
@@ -272,9 +321,9 @@ public class ProbeArrayStrategyFactoryTest {
this.desc = desc;
}
- void assertInitMethod(boolean frames) {
+ void assertInitMethod(String expectedDesc, boolean frames) {
assertEquals(InstrSupport.INITMETHOD_NAME, name);
- assertEquals(InstrSupport.INITMETHOD_DESC, desc);
+ assertEquals(expectedDesc, desc);
assertEquals(InstrSupport.INITMETHOD_ACC, access);
assertEquals(Boolean.valueOf(frames), Boolean.valueOf(frames));
}
@@ -373,12 +422,19 @@ public class ProbeArrayStrategyFactoryTest {
void assertInitMethod(boolean frames) {
assertEquals(cv.methods.size(), 1);
- cv.methods.get(0).assertInitMethod(frames);
+ cv.methods.get(0).assertInitMethod(InstrSupport.INITMETHOD_DESC,
+ frames);
+ }
+
+ void assertCondyBootstrapMethod() {
+ assertEquals(cv.methods.size(), 1);
+ cv.methods.get(0).assertInitMethod(CondyProbeArrayStrategy.B_DESC,
+ false);
}
void assertInitAndClinitMethods() {
assertEquals(2, cv.methods.size());
- cv.methods.get(0).assertInitMethod(true);
+ cv.methods.get(0).assertInitMethod(InstrSupport.INITMETHOD_DESC, true);
cv.methods.get(1).assertClinit();
}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategy.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategy.java
new file mode 100644
index 00000000..ca2fb60d
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/CondyProbeArrayStrategy.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2019 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:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.instr;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ConstantDynamic;
+import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * This strategy for Java 11+ class files uses {@link ConstantDynamic} to hold
+ * the probe array and adds bootstrap method requesting the probe array from the
+ * runtime.
+ */
+public class CondyProbeArrayStrategy implements IProbeArrayStrategy {
+
+ /**
+ * Descriptor of the bootstrap method.
+ */
+ public static final String B_DESC = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)[Z";
+
+ private final String className;
+
+ private final boolean isInterface;
+
+ private final long classId;
+
+ private final IExecutionDataAccessorGenerator accessorGenerator;
+
+ CondyProbeArrayStrategy(final String className, final boolean isInterface,
+ final long classId,
+ final IExecutionDataAccessorGenerator accessorGenerator) {
+ this.className = className;
+ this.isInterface = isInterface;
+ this.classId = classId;
+ this.accessorGenerator = accessorGenerator;
+ }
+
+ public int storeInstance(final MethodVisitor mv, final boolean clinit,
+ final int variable) {
+ final Handle bootstrapMethod = new Handle(Opcodes.H_INVOKESTATIC,
+ className, InstrSupport.INITMETHOD_NAME, B_DESC, isInterface);
+ // As a workaround for https://bugs.openjdk.java.net/browse/JDK-8216970
+ // constant should have type Object
+ mv.visitLdcInsn(new ConstantDynamic(InstrSupport.DATAFIELD_NAME,
+ "Ljava/lang/Object;", bootstrapMethod));
+ mv.visitTypeInsn(Opcodes.CHECKCAST, "[Z");
+ mv.visitVarInsn(Opcodes.ASTORE, variable);
+ return 1;
+ }
+
+ public void addMembers(final ClassVisitor cv, final int probeCount) {
+ final MethodVisitor mv = cv.visitMethod(InstrSupport.INITMETHOD_ACC,
+ InstrSupport.INITMETHOD_NAME, B_DESC, null, null);
+ final int maxStack = accessorGenerator.generateDataAccessor(classId,
+ className, probeCount, mv);
+ mv.visitInsn(Opcodes.ARETURN);
+ mv.visitMaxs(maxStack, 3);
+ mv.visitEnd();
+ }
+
+}
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
index 0c668817..72c8dd68 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactory.java
@@ -50,6 +50,10 @@ public final class ProbeArrayStrategyFactory {
if (counter.getCount() == 0) {
return new NoneProbeArrayStrategy();
}
+ if (version >= Opcodes.V11 && counter.hasMethods()) {
+ return new CondyProbeArrayStrategy(className, true, classId,
+ accessorGenerator);
+ }
if (version >= Opcodes.V1_8 && counter.hasMethods()) {
return new InterfaceFieldProbeArrayStrategy(className, classId,
counter.getCount(), accessorGenerator);
@@ -58,6 +62,10 @@ public final class ProbeArrayStrategyFactory {
counter.getCount(), accessorGenerator);
}
} else {
+ if (version >= Opcodes.V11) {
+ return new CondyProbeArrayStrategy(className, false, classId,
+ accessorGenerator);
+ }
return new ClassFieldProbeArrayStrategy(className, classId,
InstrSupport.needsFrames(version), accessorGenerator);
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index ea9c75b7..15250c84 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -22,6 +22,9 @@
<h3>New Features</h3>
<ul>
+ <li>Instrumentation does not add synthetic field to Java 11+ class files,
+ however still adds synthetic method
+ (GitHub <a href="https://github.com/jacoco/jacoco/issues/845">#845</a>).</li>
<li>Branches added by the Kotlin compiler version 1.3.30 for suspending lambdas
and functions are filtered out during generation of report
(GitHub <a href="https://github.com/jacoco/jacoco/issues/849">#849</a>).</li>