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 20, 2016
1 parent c72f350 commit b4dfd8f
Show file tree
Hide file tree
Showing 12 changed files with 260 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 @@ -23,6 +23,7 @@
import org.jacoco.core.data.ExecutionData;
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.internal.ContentTypeDetector;
import org.jacoco.core.internal.Java9Support;
import org.jacoco.core.internal.Pack200Streams;
import org.jacoco.core.internal.analysis.ClassAnalyzer;
import org.jacoco.core.internal.analysis.ClassCoverageImpl;
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.readFully(input), 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.readFully(input), 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.readFully(input), 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
115 changes: 115 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,115 @@
/*******************************************************************************
* 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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.objectweb.asm.Opcodes;

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

/**
* Version of the Java 9 class file format.
*/
public static final int V1_9 = Opcodes.V1_8 + 1;

private Java9Support() {
}

/**
* Reads all bytes from an input stream into a byte array.
*
* @param is
* the input stream to read from
* @return a byte array containing all the bytes from the stream
* @throws IOException
* if an I/O error occurs
*/
public static byte[] readFully(final InputStream is)
throws IOException {
if (is == null) {
throw new IllegalArgumentException();
}
final byte[] buf = new byte[1024];
final ByteArrayOutputStream out = new ByteArrayOutputStream();
while (true) {
int r = is.read(buf);
if (r == -1) {
break;
}
out.write(buf, 0, r);
}
return out.toByteArray();
}

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));
}

/**
* Determines whether class definition contains {@link #V1_9} version.
*
* @param buffer
* definition of the class
* @return <code>true</code> if class definition contains Java 9 version
*/
public static boolean isPatchRequired(byte[] buffer) {
return readShort(buffer, 6) == V1_9;
}

/**
* Returns new definition of class with version {@link Opcodes#V1_8},
* if it has version {@link #V1_9}.
*
* @param buffer
* definition of the class
* @return new definition of the class
*/
public static byte[] downgradeIfRequired(byte[] buffer) {
return isPatchRequired(buffer) ? downgrade(buffer) : buffer;
}

/**
* Replaces version in the definition of class on {@link Opcodes#V1_8}.
*
* @param b
* definition of the class
* @return new definition of the class
*/
public static byte[] downgrade(byte[] b) {
byte[] result = new byte[b.length];
System.arraycopy(b, 0, result, 0, b.length);
putShort(result, 6, Opcodes.V1_8);
return result;
}

/**
* Replaces version in the definition of class on {@link #V1_9}.
*
* @param b
* definition of the class
*/
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 b4dfd8f

Please sign in to comment.