diff options
12 files changed, 260 insertions, 7 deletions
diff --git a/jacoco-maven-plugin.test/it/it-java9/invoker.properties b/jacoco-maven-plugin.test/it/it-java9/invoker.properties new file mode 100644 index 00000000..750530bc --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-java9/invoker.properties @@ -0,0 +1 @@ +invoker.java.version = 1.9+ diff --git a/jacoco-maven-plugin.test/it/it-java9/pom.xml b/jacoco-maven-plugin.test/it/it-java9/pom.xml new file mode 100644 index 00000000..37b70d98 --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-java9/pom.xml @@ -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> diff --git a/jacoco-maven-plugin.test/it/it-java9/src/main/java/Example.java b/jacoco-maven-plugin.test/it/it-java9/src/main/java/Example.java new file mode 100644 index 00000000..2f24b3c3 --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-java9/src/main/java/Example.java @@ -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); + } + +} diff --git a/jacoco-maven-plugin.test/it/it-java9/src/test/java/ExampleTest.java b/jacoco-maven-plugin.test/it/it-java9/src/test/java/ExampleTest.java new file mode 100644 index 00000000..f89af13a --- /dev/null +++ b/jacoco-maven-plugin.test/it/it-java9/src/test/java/ExampleTest.java @@ -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"); + } + +} diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/ContentTypeDetectorTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/ContentTypeDetectorTest.java index 09b653b8..b6f272c1 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/ContentTypeDetectorTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/ContentTypeDetectorTest.java @@ -108,6 +108,13 @@ public class ContentTypeDetectorTest { } @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); assertEquals(ContentTypeDetector.UNKNOWN, detector.getType()); 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 08f66f83..c51a58a9 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 @@ -30,6 +30,7 @@ import java.io.IOException; 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; @@ -83,6 +84,11 @@ public class ClassFileVersionsTest { 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); @@ -95,7 +101,7 @@ public class ClassFileVersionsTest { 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 diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java index ac86128f..7ab9003a 100644 --- a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java +++ b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java @@ -23,6 +23,7 @@ import java.util.zip.ZipInputStream; 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; @@ -123,7 +124,8 @@ public class Analyzer { 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); } @@ -142,7 +144,7 @@ public class Analyzer { 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); } 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 0d4eea6c..5a0ce7ab 100644 --- a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java +++ b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java @@ -22,6 +22,7 @@ import java.util.zip.ZipInputStream; 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; @@ -98,7 +99,14 @@ public class Instrumenter { 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); } @@ -119,7 +127,7 @@ public class Instrumenter { 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); } @@ -141,7 +149,7 @@ public class Instrumenter { 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); } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/ContentTypeDetector.java b/org.jacoco.core/src/org/jacoco/core/internal/ContentTypeDetector.java index 5bd22a47..7f734c10 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/ContentTypeDetector.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/ContentTypeDetector.java @@ -82,6 +82,7 @@ public class ContentTypeDetector { case Opcodes.V1_6: case Opcodes.V1_7: case Opcodes.V1_8: + case Java9Support.V1_9: return CLASSFILE; } } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/Java9Support.java b/org.jacoco.core/src/org/jacoco/core/internal/Java9Support.java new file mode 100644 index 00000000..fae1e0e4 --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/internal/Java9Support.java @@ -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); + } + +} diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/ModifiedSystemClassRuntime.java b/org.jacoco.core/src/org/jacoco/core/runtime/ModifiedSystemClassRuntime.java index 19c6ef91..24e975ee 100644 --- a/org.jacoco.core/src/org/jacoco/core/runtime/ModifiedSystemClassRuntime.java +++ b/org.jacoco.core/src/org/jacoco/core/runtime/ModifiedSystemClassRuntime.java @@ -20,6 +20,7 @@ import java.lang.reflect.Field; 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; @@ -154,7 +155,7 @@ public class ModifiedSystemClassRuntime extends AbstractRuntime { */ 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) { diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html index 60d4a63a..d484dae4 100644 --- a/org.jacoco.doc/docroot/doc/changes.html +++ b/org.jacoco.doc/docroot/doc/changes.html @@ -30,6 +30,8 @@ <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> |