Skip to content

Commit fba5534

Browse files
Godinmarchof
authored andcommitted
ContentTypeDetector should recognize future versions of Java class files (bazel-contrib#952)
1 parent e1a9d4b commit fba5534

File tree

5 files changed

+97
-22
lines changed

5 files changed

+97
-22
lines changed

org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,23 @@ private static byte[] createClass(final int version) {
125125
return cw.toByteArray();
126126
}
127127

128+
/**
129+
* @see #analyzeAll_should_throw_exception_for_unsupported_class_file_version()
130+
*/
131+
@Test
132+
public void analyzeClass_should_throw_exception_for_unsupported_class_file_version() {
133+
final byte[] bytes = createClass(Opcodes.V14 + 1);
134+
try {
135+
analyzer.analyzeClass(bytes, "UnsupportedVersion");
136+
fail("exception expected");
137+
} catch (IOException e) {
138+
assertEquals("Error while analyzing UnsupportedVersion.",
139+
e.getMessage());
140+
assertEquals("Unsupported class file major version 59",
141+
e.getCause().getMessage());
142+
}
143+
}
144+
128145
@Test
129146
public void testAnalyzeClassFromStream() throws IOException {
130147
analyzer.analyzeClass(TargetLoader.getClassData(AnalyzerTest.class),
@@ -196,6 +213,24 @@ public void testAnalyzeClass_BrokenStream() throws IOException {
196213
}
197214
}
198215

216+
/**
217+
* @see #analyzeClass_should_throw_exception_for_unsupported_class_file_version()
218+
*/
219+
@Test
220+
public void analyzeAll_should_throw_exception_for_unsupported_class_file_version() {
221+
final byte[] bytes = createClass(Opcodes.V14 + 1);
222+
try {
223+
analyzer.analyzeAll(new ByteArrayInputStream(bytes),
224+
"UnsupportedVersion");
225+
fail("exception expected");
226+
} catch (IOException e) {
227+
assertEquals("Error while analyzing UnsupportedVersion.",
228+
e.getMessage());
229+
assertEquals("Unsupported class file major version 59",
230+
e.getCause().getMessage());
231+
}
232+
}
233+
199234
@Test
200235
public void testAnalyzeAll_Class() throws IOException {
201236
final int count = analyzer.analyzeAll(

org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,23 @@ private static byte[] createClass(final int version) {
117117
return cw.toByteArray();
118118
}
119119

120+
/**
121+
* @see #instrumentAll_should_throw_exception_for_unsupported_class_file_version()
122+
*/
123+
@Test
124+
public void instrument_should_throw_exception_for_unsupported_class_file_version() {
125+
final byte[] bytes = createClass(Opcodes.V14 + 1);
126+
try {
127+
instrumenter.instrument(bytes, "UnsupportedVersion");
128+
fail("exception expected");
129+
} catch (final IOException e) {
130+
assertEquals("Error while instrumenting UnsupportedVersion.",
131+
e.getMessage());
132+
assertEquals("Unsupported class file major version 59",
133+
e.getCause().getMessage());
134+
}
135+
}
136+
120137
@Test
121138
public void testInstrumentClass() throws Exception {
122139
byte[] bytes = instrumenter.instrument(
@@ -202,6 +219,24 @@ public void testSerialization() throws Exception {
202219
assertEquals("Hello42", obj2.toString());
203220
}
204221

222+
/**
223+
* @see #instrument_should_throw_exception_for_unsupported_class_file_version()
224+
*/
225+
@Test
226+
public void instrumentAll_should_throw_exception_for_unsupported_class_file_version() {
227+
final byte[] bytes = createClass(Opcodes.V14 + 1);
228+
try {
229+
instrumenter.instrumentAll(new ByteArrayInputStream(bytes),
230+
new ByteArrayOutputStream(), "UnsupportedVersion");
231+
fail("exception expected");
232+
} catch (final IOException e) {
233+
assertEquals("Error while instrumenting UnsupportedVersion.",
234+
e.getMessage());
235+
assertEquals("Unsupported class file major version 59",
236+
e.getCause().getMessage());
237+
}
238+
}
239+
205240
@Test
206241
public void testInstrumentAll_Class() throws IOException {
207242
InputStream in = TargetLoader.getClassData(getClass());

org.jacoco.core.test/src/org/jacoco/core/internal/ContentTypeDetectorTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,21 @@ public void should_detect_java_14_with_preview_features() throws IOException {
180180
assertContent();
181181
}
182182

183+
@Test
184+
public void should_detect_java_42() throws IOException {
185+
initData(0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x56);
186+
assertEquals(ContentTypeDetector.CLASSFILE, detector.getType());
187+
assertContent();
188+
}
189+
190+
@Test
191+
public void should_not_detect_MachO_fat_binary_with_44_architectures()
192+
throws IOException {
193+
initData(0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x2C);
194+
assertEquals(ContentTypeDetector.UNKNOWN, detector.getType());
195+
assertContent();
196+
}
197+
183198
@Test
184199
public void testMachObjectFile() throws IOException {
185200
initData(0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x02);

org.jacoco.core/src/org/jacoco/core/internal/ContentTypeDetector.java

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
import java.io.IOException;
1717
import java.io.InputStream;
1818

19-
import org.objectweb.asm.Opcodes;
20-
2119
/**
2220
* Detector for content types of binary streams based on a magic headers.
2321
*/
@@ -73,26 +71,14 @@ private static int determineType(final InputStream in) throws IOException {
7371
case PACK200FILE:
7472
return PACK200FILE;
7573
case CLASSFILE:
76-
// also verify version to distinguish from Mach Object files:
77-
switch (readInt(in)) {
78-
case Opcodes.V1_1:
79-
case Opcodes.V1_2:
80-
case Opcodes.V1_3:
81-
case Opcodes.V1_4:
82-
case Opcodes.V1_5:
83-
case Opcodes.V1_6:
84-
case Opcodes.V1_7:
85-
case Opcodes.V1_8:
86-
case Opcodes.V9:
87-
case Opcodes.V10:
88-
case Opcodes.V11:
89-
case Opcodes.V11 | Opcodes.V_PREVIEW:
90-
case Opcodes.V12:
91-
case Opcodes.V12 | Opcodes.V_PREVIEW:
92-
case Opcodes.V13:
93-
case Opcodes.V13 | Opcodes.V_PREVIEW:
94-
case (Opcodes.V13 + 1):
95-
case (Opcodes.V13 + 1) | Opcodes.V_PREVIEW:
74+
// Mach-O fat/universal binaries have the same magic header as Java
75+
// class files, number of architectures is stored in unsigned 4
76+
// bytes in the same place and in the same big-endian order as major
77+
// and minor version of class file. Hopefully on practice number of
78+
// architectures in single executable is less than 45, which is
79+
// major version of Java 1.1 class files:
80+
final int majorVersion = readInt(in) & 0xFFFF;
81+
if (majorVersion >= 45) {
9682
return CLASSFILE;
9783
}
9884
}

org.jacoco.doc/docroot/doc/changes.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ <h3>API Changes</h3>
7070
a coverage ratio limit is configured outside the range [0,1] to avoid
7171
common configuration mistakes
7272
(GitHub <a href="https://github.com/jacoco/jacoco/issues/783">#783</a>).</li>
73+
<li>Unsupported class file versions are now consistently reported as exceptions
74+
by all methods of <code>Analyzer</code> and <code>Instrumenter</code> and
75+
thus also during report generation and offline instrumentation
76+
(GitHub <a href="https://github.com/jacoco/jacoco/issues/952">#952</a>).</li>
7377
</ul>
7478

7579
<h2>Release 0.8.4 (2019/05/08)</h2>

0 commit comments

Comments
 (0)