Java Code Coverage Mechanics - EclipseCon Europe 2017
Java Code Coverage Mechanics - EclipseCon Europe 2017
Java Code Coverage Mechanics - EclipseCon Europe 2017
Code
Coverage
Mechanics
by Evgeny Mandrikov
at EclipseCon Europe 2017
Evgeny Mandrikov Marc Hoffmann
@_Godin_ @marcandsweep
Godin marchof
EclEmma
Eclipse
Community
Awards
Observer Effect
*.class *.exec
As easy as setting an arg for the JVM
class PreMain {
public static void premain(
String options,
Instrumentation instrumentation
) throws Exception {
instrumentation.addTransformer(…);
}
}
Java Byte Code Instrumentation
Probe Strategies
JaCoCo Validation Test Suite
Tested on 5 major JDK versions
Not only issues in JaCoCo...
Probe
class Fun {
static final synthetic boolean $assertionsDisabled
= Fun.class.desiredAssertionStatus();
void fun(boolean x) {
if (! $assertionsDisabled)
if (! x)
throw new AssertionError();
}
}
Nice idea!
class Fun {
static final synthetic boolean[] $jacocoData = … ;
void fun() {
boolean[] probes = $jacocoData;
probes[0] = true;
…
probes[…] = true;
}
}
Bridge Methods
synthetic static
Fun[] values();
Lambdas
// javac Fun.java
return downgrade(classBytes)
// javap -version ? upgrade(transform(classBytes))
1.7.0_80 : transform(classBytes);
// javap -v Fun
major version: 53
class Fun {
static final synthetic boolean[] $jacocoData = … ;
void fun() {
boolean[] probes = $jacocoData;
probes[0] = true; // NullPointerException
…
probes[…] = true;
}
}
Bad Cycle - Solution
class Fun {
static final synthetic boolean[] $jacocoData;
void fun() {
boolean[] probes = $jacocoInit();
…
Bad Cycle - Solution?
class Fun {
static final synthetic boolean[] $jacocoData;
void fun() {
boolean[] probes = $jacocoInit();
…
Java Virtual Machine Specification
“6.5. putstatic
if the field is final, it must be declared in the current class,
and the instruction must occur in the <clinit> method of the
current class. Otherwise, an IllegalAccessError is thrown”
Checked since JDK 9 EA b127 for class files version 53.
Bad Cycle - Correct Solution
class Fun {
static final synthetic boolean[] $jacocoData;
void fun() {
boolean[] probes = $jacocoInit();
…
Interfaces
Object access =
java.util.UUID.$jacocoAccess; // created at agent startup
access.equals(args);