Skip to content

Commit

Permalink
Add experimental support for Java 9 class files
Browse files Browse the repository at this point in the history
  • Loading branch information
Godin committed May 14, 2016
1 parent fde254d commit 0c24111
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 7 deletions.
1 change: 1 addition & 0 deletions jacoco-maven-plugin.test/it/it-java9/invoker.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
invoker.java.version = 1.9+
70 changes: 70 additions & 0 deletions jacoco-maven-plugin.test/it/it-java9/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2009, 2016 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
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>jacoco</groupId>
<artifactId>setup-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../setup-parent</relativePath>
</parent>

<artifactId>it-java9</artifactId>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<target>1.9</target>
</configuration>
</plugin>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<!-- implementation is needed only for Maven 2 -->
<rule implementation="org.jacoco.maven.RuleConfiguration">
<limits>
<!-- implementation is needed only for Maven 2 -->
<limit implementation="org.jacoco.report.check.Limit">
<counter>INSTRUCTION</counter>
<value>COVEREDCOUNT</value>
<minimum>8</minimum>
<maximum>8</maximum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
19 changes: 19 additions & 0 deletions jacoco-maven-plugin.test/it/it-java9/src/main/java/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2009, 2016 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
*
*******************************************************************************/
public class Example {

public void sayHello(String name) {
// http://openjdk.java.net/jeps/280
System.out.println("Hello, " + name);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2009, 2016 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
*
*******************************************************************************/
import org.junit.Test;

public class ExampleTest {

@Test
public void test() {
new Example().sayHello("test");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ public void testClassFile18() throws IOException {
assertContent();
}

@Test
public void testClassFile19() throws IOException {
initData(0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x35);
assertEquals(ContentTypeDetector.CLASSFILE, detector.getType());
assertContent();
}

@Test
public void testMachObjectFile() throws IOException {
initData(0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x02);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import org.jacoco.core.JaCoCo;
import org.jacoco.core.instr.Instrumenter;
import org.jacoco.core.internal.Java9Support;
import org.jacoco.core.runtime.IRuntime;
import org.jacoco.core.runtime.SystemPropertiesRuntime;
import org.junit.Test;
Expand Down Expand Up @@ -83,6 +84,11 @@ public void test_1_8() throws IOException {
testVersion(V1_8, true);
}

@Test
public void test_1_9() throws IOException {
testVersion(Java9Support.V1_9, true);
}

private void testVersion(int version, boolean frames) throws IOException {
final byte[] original = createClass(version);

Expand All @@ -95,7 +101,7 @@ private void testVersion(int version, boolean frames) throws IOException {

private void assertFrames(byte[] source, boolean expected) {
final boolean[] hasFrames = new boolean[] { false };
new ClassReader(source).accept(
new ClassReader(Java9Support.downgradeIfRequired(source)).accept(
new ClassVisitor(JaCoCo.ASM_API_VERSION) {

@Override
Expand Down
6 changes: 4 additions & 2 deletions org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import org.jacoco.core.data.ExecutionData;
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.internal.Java9Support;
import org.jacoco.core.internal.ContentTypeDetector;
import org.jacoco.core.internal.Pack200Streams;
import org.jacoco.core.internal.analysis.ClassAnalyzer;
Expand Down Expand Up @@ -123,7 +124,8 @@ public void analyzeClass(final ClassReader reader) {
public void analyzeClass(final byte[] buffer, final String location)
throws IOException {
try {
analyzeClass(new ClassReader(buffer));
analyzeClass(
new ClassReader(Java9Support.downgradeIfRequired(buffer)));
} catch (final RuntimeException cause) {
throw analyzerError(location, cause);
}
Expand All @@ -142,7 +144,7 @@ public void analyzeClass(final byte[] buffer, final String location)
public void analyzeClass(final InputStream input, final String location)
throws IOException {
try {
analyzeClass(new ClassReader(input));
analyzeClass(Java9Support.readClass(input, false), location);
} catch (final RuntimeException e) {
throw analyzerError(location, e);
}
Expand Down
14 changes: 11 additions & 3 deletions org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.zip.ZipOutputStream;

import org.jacoco.core.internal.ContentTypeDetector;
import org.jacoco.core.internal.Java9Support;
import org.jacoco.core.internal.Pack200Streams;
import org.jacoco.core.internal.flow.ClassProbesAdapter;
import org.jacoco.core.internal.instr.ClassInstrumenter;
Expand Down Expand Up @@ -98,7 +99,14 @@ public byte[] instrument(final ClassReader reader) {
public byte[] instrument(final byte[] buffer, final String name)
throws IOException {
try {
return instrument(new ClassReader(buffer));
if (Java9Support.isPatchRequired(buffer)) {
final byte[] result = instrument(
new ClassReader(Java9Support.downgrade(buffer)));
Java9Support.upgrade(result);
return result;
} else {
return instrument(new ClassReader(buffer));
}
} catch (final RuntimeException e) {
throw instrumentError(name, e);
}
Expand All @@ -119,7 +127,7 @@ public byte[] instrument(final byte[] buffer, final String name)
public byte[] instrument(final InputStream input, final String name)
throws IOException {
try {
return instrument(new ClassReader(input));
return instrument(Java9Support.readClass(input, false), name);
} catch (final RuntimeException e) {
throw instrumentError(name, e);
}
Expand All @@ -141,7 +149,7 @@ public byte[] instrument(final InputStream input, final String name)
public void instrument(final InputStream input, final OutputStream output,
final String name) throws IOException {
try {
output.write(instrument(new ClassReader(input)));
output.write(instrument(Java9Support.readClass(input, false), name));
} catch (final RuntimeException e) {
throw instrumentError(name, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ private static int determineType(final InputStream in) throws IOException {
case Opcodes.V1_6:
case Opcodes.V1_7:
case Opcodes.V1_8:
case Java9Support.V1_9:
return CLASSFILE;
}
}
Expand Down
98 changes: 98 additions & 0 deletions org.jacoco.core/src/org/jacoco/core/internal/Java9Support.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (c) 2009, 2016 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;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

/**
* Patching for Java 9 classes, so that ASM can read them.
*/
public final class Java9Support {

public static final int V1_9 = Opcodes.V1_8 + 1;

private Java9Support() {
}

/**
* Copy of {@link ClassReader#readClass(InputStream, boolean)}.
*/
public static byte[] readClass(final InputStream is, boolean close)
throws IOException {
if (is == null) {
throw new IOException("Class not found");
}
try {
byte[] b = new byte[is.available()];
int len = 0;
while (true) {
int n = is.read(b, len, b.length - len);
if (n == -1) {
if (len < b.length) {
byte[] c = new byte[len];
System.arraycopy(b, 0, c, 0, len);
b = c;
}
return b;
}
len += n;
if (len == b.length) {
int last = is.read();
if (last < 0) {
return b;
}
byte[] c = new byte[b.length + 1000];
System.arraycopy(b, 0, c, 0, len);
c[len++] = (byte) last;
b = c;
}
}
} finally {
if (close) {
is.close();
}
}
}

private static void putShort(byte[] b, int index, int s) {
b[index] = (byte) (s >>> 8);
b[index + 1] = (byte) s;
}

private static short readShort(byte[] b, int index) {
return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
}

public static boolean isPatchRequired(byte[] buffer) {
return readShort(buffer, 6) == V1_9;
}

public static byte[] downgradeIfRequired(byte[] buffer) {
return isPatchRequired(buffer) ? downgrade(buffer) : buffer;
}

public static byte[] downgrade(byte[] b) {
byte[] result = Arrays.copyOf(b, b.length);
putShort(result, 6, Opcodes.V1_8);
return result;
}

public static void upgrade(byte[] b) {
putShort(b, 6, V1_9);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.security.ProtectionDomain;

import org.jacoco.core.JaCoCo;
import org.jacoco.core.internal.Java9Support;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
Expand Down Expand Up @@ -154,7 +155,7 @@ public byte[] transform(final ClassLoader loader,
*/
public static byte[] instrument(final byte[] source,
final String accessFieldName) {
final ClassReader reader = new ClassReader(source);
final ClassReader reader = new ClassReader(Java9Support.downgradeIfRequired(source));
final ClassWriter writer = new ClassWriter(reader, 0);
reader.accept(new ClassVisitor(JaCoCo.ASM_API_VERSION, writer) {

Expand Down
2 changes: 2 additions & 0 deletions org.jacoco.doc/docroot/doc/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ <h3>New Features</h3>
<li>Renamed "dot" resources in generated HTML reports to become more web
hosting friendly
(GitHub <a href="https://github.com/jacoco/jacoco/issues/401">#401</a>).</li>
<li>Experimental support for Java 9 class files
(GitHub <a href="https://github.com/jacoco/jacoco/issues/406">#406</a>).</li>
</ul>

<h3>Fixed Bugs</h3>
Expand Down

0 comments on commit 0c24111

Please sign in to comment.