From b092334631eb6a9041426d7496faa55d72578bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sol=C3=B3rzano?= Date: Tue, 29 Aug 2023 13:47:53 +0200 Subject: [PATCH] Add java bytecode class file version detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jorge Solórzano --- .../java/version/JavaClassfileVersion.java | 141 ++++++++++++++++++ .../version/JavaClassfileVersionParser.java | 55 +++++++ .../languages/java/version/JavaVersion.java | 2 +- .../java/version/JavaClassVersionTest.java | 79 ++++++++++ .../classfile.version/helloworld-10.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-11.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-12.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-13.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-14.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-15.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-16.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-17.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-18.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-19.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-20.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-21.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-22.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-8.class | Bin 0 -> 424 bytes .../classfile.version/helloworld-9.class | Bin 0 -> 424 bytes .../helloworld-preview.class | Bin 0 -> 489 bytes 20 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersion.java create mode 100644 plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersionParser.java create mode 100644 plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaClassVersionTest.java create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-10.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-11.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-12.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-13.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-14.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-15.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-16.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-17.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-18.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-19.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-20.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-21.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-22.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-8.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-9.class create mode 100644 plexus-java/src/test/resources/classfile.version/helloworld-preview.class diff --git a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersion.java b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersion.java new file mode 100644 index 0000000..e909f8f --- /dev/null +++ b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersion.java @@ -0,0 +1,141 @@ +package org.codehaus.plexus.languages.java.version; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Reads the bytecode of a Java class to detect the major, minor and Java + * version that was compiled. + * + * @author Jorge Solórzano + */ +public final class JavaClassfileVersion { + + private final int major; + private final int minor; + + JavaClassfileVersion(int major, int minor) { + if (major < 45) { + throw new IllegalArgumentException("Java class major version must be 45 or above."); + } + this.major = major; + this.minor = minor; + } + + /** + * Reads the bytecode of a Java class file and returns the + * {@link JavaClassfileVersion}. + * + * @param bytes {@code byte[]} of the Java class file + * @return the {@link JavaClassfileVersion} of the byte array + */ + public static JavaClassfileVersion of(byte[] bytes) { + return JavaClassfileVersionParser.of(bytes); + } + + /** + * Reads the bytecode of a Java class file and returns the + * {@link JavaClassfileVersion}. + * + * @param path {@link Path} of the Java class file + * @return the {@link JavaClassfileVersion} of the path java class + */ + public static JavaClassfileVersion of(Path path) { + try { + byte[] readAllBytes = Files.readAllBytes(path); + return of(readAllBytes); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + /** + * JavaVersion of the class file version detected. + * + * @return JavaVersion based on the major version of the class file. + */ + public JavaVersion javaVersion() { + int javaVer = major - 44; + String javaVersion = javaVer < 9 ? "1." + javaVer : Integer.toString(javaVer); + + return JavaVersion.parse(javaVersion); + } + + /** + * Returns the major version of the parsed classfile. + * + * @return the major classfile version + */ + public int majorVersion() { + return major; + } + + /** + * Returns the minor version of the parsed classfile. + * + * @return the minor classfile version + */ + public int minorVersion() { + return minor; + } + + /** + * Returns if the classfile use preview features. + * + * @return {@code true} if the classfile use preview features. + */ + public boolean isPreview() { + return minor == 65535; + } + + /** + * Returns a String representation of the Java class file version, e.g. + * {@code 65.0 (Java 21)}. + * + * @return String representation of the Java class file version + */ + @Override + public String toString() { + return major + "." + minor + " (Java " + javaVersion() + ")"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + major; + result = prime * result + minor; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof JavaClassfileVersion)) return false; + JavaClassfileVersion other = (JavaClassfileVersion) obj; + if (major != other.major) return false; + if (minor != other.minor) return false; + return true; + } +} diff --git a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersionParser.java b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersionParser.java new file mode 100644 index 0000000..a994180 --- /dev/null +++ b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaClassfileVersionParser.java @@ -0,0 +1,55 @@ +package org.codehaus.plexus.languages.java.version; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * This class is intented to be package-private and consumed by + * {@link JavaClassfileVersion}. + * + * @author Jorge Solórzano + */ +final class JavaClassfileVersionParser { + + private JavaClassfileVersionParser() {} + + /** + * Reads the bytecode of a Java class file and returns the {@link JavaClassfileVersion}. + * + * @param in {@code byte[]} of the Java class file + * @return the {@link JavaClassfileVersion} of the input stream + */ + public static JavaClassfileVersion of(byte[] bytes) { + try (final DataInputStream data = new DataInputStream(new ByteArrayInputStream(bytes))) { + if (0xCAFEBABE != data.readInt()) { + throw new IOException("Invalid java class file header"); + } + int minor = data.readUnsignedShort(); + int major = data.readUnsignedShort(); + return new JavaClassfileVersion(major, minor); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } +} diff --git a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java index b80e7cc..245527e 100644 --- a/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java +++ b/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java @@ -60,7 +60,7 @@ private JavaVersion(String rawVersion, boolean isMajor) { * Actual parsing is done when calling {@link #compareTo(JavaVersion)} * * @param s the version string, never {@code null} - * @return the version wrapped in a JavadocVersion + * @return the version wrapped in a JavaVersion */ public static JavaVersion parse(String s) { return new JavaVersion(s, !s.startsWith("1.")); diff --git a/plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaClassVersionTest.java b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaClassVersionTest.java new file mode 100644 index 0000000..eaec9c5 --- /dev/null +++ b/plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaClassVersionTest.java @@ -0,0 +1,79 @@ +package org.codehaus.plexus.languages.java.version; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class JavaClassVersionTest { + + @ParameterizedTest + @MethodSource("provideClassFiles") + void testFilesClassVersions(Path filePath) { + String fileName = filePath.getFileName().toString(); + int javaVersion = Integer.valueOf(fileName.substring(fileName.indexOf("-") + 1, fileName.length() - 6)); + JavaClassfileVersion classVersion = JavaClassfileVersion.of(filePath); + assertEquals(javaVersion + 44, classVersion.majorVersion()); + assertEquals(0, classVersion.minorVersion()); + assertEquals(JavaVersion.parse("" + javaVersion), classVersion.javaVersion()); + } + + static Stream provideClassFiles() { + List paths; + try (DirectoryStream directoryStream = + Files.newDirectoryStream(Paths.get("src/test/resources/classfile.version/"), "*-[0-9]?.class")) { + paths = StreamSupport.stream(directoryStream.spliterator(), false) + .filter(Files::isRegularFile) + .collect(Collectors.toList()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return paths.stream(); + } + + @Test + void testJavaClassPreview() { + Path previewFile = Paths.get("src/test/resources/classfile.version/helloworld-preview.class"); + JavaClassfileVersion previewClass = JavaClassfileVersion.of(previewFile); + assertTrue(previewClass.isPreview()); + assertEquals(20 + 44, previewClass.majorVersion()); + assertEquals(JavaVersion.parse("20"), previewClass.javaVersion()); + } + + @Test + void testJavaClassVersionMajor45orAbove() { + assertThrows( + IllegalArgumentException.class, + () -> new JavaClassfileVersion(44, 0), + "Java class major version must be 45 or above."); + } + + @Test + void equalsContract() { + JavaClassfileVersion javaClassVersion = new JavaClassfileVersion(65, 0); + JavaClassfileVersion previewFeature = new JavaClassfileVersion(65, 65535); + assertNotEquals(javaClassVersion, previewFeature); + assertNotEquals(javaClassVersion.hashCode(), previewFeature.hashCode()); + + JavaClassfileVersion javaClassVersionOther = new JavaClassfileVersion(65, 0); + assertEquals(javaClassVersion, javaClassVersionOther); + assertEquals(javaClassVersion.hashCode(), javaClassVersionOther.hashCode()); + assertEquals(javaClassVersion.javaVersion(), javaClassVersionOther.javaVersion()); + assertEquals(javaClassVersion.javaVersion(), previewFeature.javaVersion()); + } +} diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-10.class b/plexus-java/src/test/resources/classfile.version/helloworld-10.class new file mode 100644 index 0000000000000000000000000000000000000000..925195184622cdaaa6512d6b86a80beb688ca775 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59a2j;zbaVqC%t>Pf5GfEt_45iQ;4FLBWF$ z;6sVC^-zTFVdi7@o8Jzz-#?#U0FKe|P(qo*@lipQp+1kF;!wqU7G5WFG07RKCsND& zl%d=oj9t{=a(F)0;QzBnFORuM$z=*^YK;~kX~LUK>O9Ib5vRi%HW<7Mp_J)f(nB8V z*yL#VXkv?@xuS=GKUjj&1nvJ(7vm99>e+BG=4cc2-a?;Erlt(enVAZPU?jD;D$+z` zw{fDV;-s-8PP>1%3PhC16xl?aOKT36$Rj%#x@gg|?6Hy71%_=>9m>pB4nM$NNighC mwAWU<*u@@YYwc01t-5dEjrDf~OS}CDz@hzC$g)qmgo9s7B3C^C literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-11.class b/plexus-java/src/test/resources/classfile.version/helloworld-11.class new file mode 100644 index 0000000000000000000000000000000000000000..e7b2672567091a4aacbc3242a7ca129b6f1ab332 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59a8h;zbaVqC%t>Pf5GfEt_45iQ;4FLBWF$ z;6sVC^-zTFVdi7@o8Jzz-#?#U0FKe|P(qo*@lipQp+1kF;!wqU7G5WFG07RKCsND& zl%d=oj9t{=a(F)0;QzBnFORuM$z=*^YK;~kX~LUK>O9Ib5vRi%HW<7Mp_J)f(nB8V z*yL#VXkv?@xuS=GKUjj&1nvJ(7vm99>e+BG=4cc2-a?;Erlt(enVAZPU?jD;D$+z` zw{fDV;-s-8PP>1%3PhC16xl?aOKT36$Rj%#x@gg|?6Hy71%_=>9m>pB4nM$NNighC mwAWU<*u@@YYwc01t-5dEjrDf~OS}CDz@hzC$g)qmgo9s8!&g24 literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-12.class b/plexus-java/src/test/resources/classfile.version/helloworld-12.class new file mode 100644 index 0000000000000000000000000000000000000000..5cd0678436376f05f81cc95c845ca0233b151629 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59TUVya*yvREYHADQTCwWwR?WQG6^tD0uJz zd?<0Y9*WRC%zVs#^V?ze`{(luz%e==N+@$UJ}Rg()aUV29I9B)!s}!%COJd(L~5Cz zGL-v+v5Oj94$sFL{D1c78kVSM)IO2TM?zp#4AUVmv}hJsS?j9BqQ$Tj;aN)Re(FGgHA3jHDJ_tvzbBRrd|NvHp%=X}2E%IJDmiS@ubnaPSLFWLG}` literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-13.class b/plexus-java/src/test/resources/classfile.version/helloworld-13.class new file mode 100644 index 0000000000000000000000000000000000000000..e95b0b4788dcc2c34c9698b94d66e4b52ad04904 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59TZtFM@~^6(YTOO4_Av+3ZS86dy|u3Lbm_ zA4;69haz+jGas|x{C1fA{`vd@aEy+J63QHoj|!>`^?Cdhhbq>y@H&}`NzPC`ky_@b z4CVe{?4kyj!}GBQ|DQd2dCWyhE>lobYqS7K6W(M}=TV-CI33oo!Qfp8rA+sd9`aDf zCP%|Z6I%?;6+I06!4i}vX#bD87>|%r&xV6BN1LGc7W!;5HDz$l%v3N0BdNtzktQO$ zjT1!`Cyga>+Wos#AfiO3$R^@kT63^O9@)XrMT?eYkBzi0Fl>|RP-eDr_yP7xf?mVMGC9Q*=L1y?`- literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-14.class b/plexus-java/src/test/resources/classfile.version/helloworld-14.class new file mode 100644 index 0000000000000000000000000000000000000000..c0d06625d48538abba2dd081e3348480cac32894 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59V&giy$IJg-9=+l6I+EHoFoN#mCZvf(IYK zhZ1M&p$Of>%*X6Eza3`3e?Gqe9HZl*gffTYqk<|!eI7r>p^Eh^yiVp~k~36Kq?Y+9 zL%BZ~yQsnC@O-Sn|7VY09&?eB%M{eq8ZAK5gg2Sgd6Z`&PKPyYFnAY2Dbu~Ahdk7= z$0FG z<3v%#Nn=TzcK>b_h$xXMvWYmC)*LL6M|LoD(V}J9VF}n>+cAbcKZ>4L;J0eWuJ5j2ft9JS3v*( literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-15.class b/plexus-java/src/test/resources/classfile.version/helloworld-15.class new file mode 100644 index 0000000000000000000000000000000000000000..1b62db66e892f44689aae50bc7d3b7534ed7da04 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%v-b2m1gKFM@~^6(YTO+O$jEvdKzJ6dy|u3Lbm_ zA4;69haz+jGas|x{C1fA{`vd@5TWOxf+~mOqlP*|b1t65P>XCfyiVqFQZUp{RHlkk zhH5w%yJ*1W@O-Sn|7VY19t)Y0%aqjA87)A{3~zFk6>*VEkwy({FnAYIYtz4^hdeZ~ z$OcR;k zibPYzNrfU#H@sT~B1&w^d?L@4H3v)Nu^kM3bZA-j*huRF!#1fNWo9c!A7HN}7+kIyZs2jq5am#vQN5#gI`idS3&>) literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-16.class b/plexus-java/src/test/resources/classfile.version/helloworld-16.class new file mode 100644 index 0000000000000000000000000000000000000000..bade14e5e1a9b3658cab6731183cb1071d5f2244 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59R?{ya*yvREYHAY11xs%Vr}nQG6^tD0uJz zd?<0Y9*WRC%zVs#^V?ze`{(luzz7`=C6qZF9~D#?>I?BCLM_ZZyp9)gnln^K%BcL5 zq1+!#T-4xlcs@4Z|FcIgkGV|9l@`?08ZSXA4R11K@+i-wNQO0RF?bhJ>$H1G4|%9# zo1@{Qi5-UKnjQxJUsk=EK2+qfO9z3w=&AJ!5dr(wSrk#>&X6B8g>o zD`HI*ClQJ`?f%_55K*GE$fojKS#z*Lp4h?AMT?eYkBzi0Fzk}*P-eDr`~mh#f?mP67d9Q^`Q=vPAk literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-17.class b/plexus-java/src/test/resources/classfile.version/helloworld-17.class new file mode 100644 index 0000000000000000000000000000000000000000..461c9cd3c677aa24e7d152dd79eb79590ed764c6 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59R@?UIY;-Dnxqml(b9Tve}iGC_a`R6g>C< zK9o3H4@Kx6Wht(14pppY;dL?>lboS?EVax} z8Or^^*hLL4hv#Dr{y%&4@|cU1T&AF=)@T8eCcMd{&Z9gNaXPGFgTcEHN}28@J>;Q| zO^$|-Cbk%wD|#6CgC!_U(EcBFF&-hMo(%_Mjy6H>E%e!BYRcf8nWKOIQRusiC07b literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-18.class b/plexus-java/src/test/resources/classfile.version/helloworld-18.class new file mode 100644 index 0000000000000000000000000000000000000000..5ae0ca175d7aa3c611baea9d0c7874501b1b1be4 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59R?Xy$B*wREYHADQTCwWwR?WQG6^tD0uJz zd?<0Y9*WRC%zVs#^V?ze`{(luzz7`=C6qZF9~D#?>Wla(4ppq@;dQbQ)10AtDz(f< z4CVe{;-UtZ!}GBL|DQd2dCWyhE>lobYrF(W6W(M}=TV-CI33oo#o%2CrA+sd9`aDf zHb=up6FUseH9ZXc!4i}vX#bD89FLGv&xeBvN1LGc7W!;5HDhqj%uFx@W2wbektQO$ zjT1!`Cyga>+WotAAfiO3$fn|4T63^Mp4h?AMT?eYkBzi0Fzk}*P-eDr`~mh#f?mP67d9Q^`UDpy4S literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-19.class b/plexus-java/src/test/resources/classfile.version/helloworld-19.class new file mode 100644 index 0000000000000000000000000000000000000000..bd24b66f44e7bfe938aadae86b92de61e813c10d GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59R?XdJsgUs1WJJQ_?PV%Vt+%qWD;PQ1IXb z_)y|(JrtpPnE9Cf=C{M__s{1SfKzlllu+hyd{j_nsL$i4I8?Eoh1bbkOmc?mvD7j@ zVJPn(!u*I*;;9#Obhx4F>N*C}p~r^pJ-- zHaQwTn%H7!uIORl50;=bLHmEy#dw62dNv%4Iobrhx6o&ksVReVW~PE67)dRziZl_~ zZJa2oIB6`2)9&A`0ud!LMK%%V(wc)M^2iQ`E?TrKdu*h2fnl3ehcdI3!w;}m5)3;O m?X}e|cCkm8kVSM)IO2TM?zp#4AUVmv}hJsS?j9BqQ$Tj;aN)Re(FGgHA3jHDJ_tvzbBRrd|NvHp%=X}2E%IJDmiS@ubnaPSLSY*$AB literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-21.class b/plexus-java/src/test/resources/classfile.version/helloworld-21.class new file mode 100644 index 0000000000000000000000000000000000000000..e57d1b96074bf49da2d45d54c114f4205d8bfe5b GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59R?Xf(Rl~REYHADQTCwWwR?WQG6^tD0uJz zd?<0Y9*WRC%zVs#^V?ze`{(luz$rQ&N+@$UJ}Rg()aUV29I9B)!s}!%COJd(SZbM{ zFqHd)v5Oj94$sFL{D1c78kVSM)IO2TM?zp#4AUVmv}hJsS?j9BqQ$Tj;aN)Re(FGgHA3jHDJ_tvzbBRrd|NvHp%=X}2E%IJDmiS@ubnaPSLU4Od72 literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-22.class b/plexus-java/src/test/resources/classfile.version/helloworld-22.class new file mode 100644 index 0000000000000000000000000000000000000000..ff268d8e47c0dc0eb526d1dadc2129dbb1d5b5bb GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59R?X9u!2Rs1WJJQ_?PV%Vt+%qWD;PQ1IXb z_)y|(JrtpPnE9Cf=C{M__s{1SfKzlllu+hyd{j_nsL$i4I8?Eoh1bbkOmc?mvD7j@ zVJPn(!u*I*;;9#Obhx4F>N*C}p~r^pJ-- zHaQwTn%H7!uIORl50;=bLHmEy#dw62dNv%4Iobrhx6o&ksVReVW~PE67)dRziZl_~ zZJa2oIB6`2)9&A`0ud!LMK%%V(wc)M^2iQ`E?TrKdu*h2fnl3ehcdI3!w;}m5)3;O m?X}e|cCkmn;zbaVqC%t>Pf5GfEt_45iQ;4FLBWF$ z;6sVC^-zTFVdi7@o8Jzz-#?#U0FKe|P(qo*@lipQp+1kF;!wqU7G5WFG07RKCsND& zl%d=oj9t{=a(F)0;QzBnFORuM$z=*^YK;~kX~LUK>O9Ib5vRi%HW<7Mp_J)f(nB8V z*yL#VXkv?@xuS=GKUjj&1nvJ(7vm99>e+BG=4cc2-a?;Erlt(enVAZPU?jD;D$+z` zw{fDV;-s-8PP>1%3PhC16xl?aOKT36$Rj%#x@gg|?6Hy71%_=>9m>pB4nM$NNighC mwAWU<*u@@YYwc01t-5dEjrDf~OS}CDz@hzC$g)qmgo9s3;8!~U literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-9.class b/plexus-java/src/test/resources/classfile.version/helloworld-9.class new file mode 100644 index 0000000000000000000000000000000000000000..06772b55cf41a9aa7ff1f8675d0d3b4725c308e2 GIT binary patch literal 424 zcmZvZ&q~8U5XQf4({2-^sjaPl3%wLt59Z{l;zbaVqC%t>Pf5GfEt_45iQ;4FLBWF$ z;6sVC^-zTFVdi7@o8Jzz-#?#U0FKe|P(qo*@lipQp+1kF;!wqU7G5WFG07RKCsND& zl%d=oj9t{=a(F)0;QzBnFORuM$z=*^YK;~kX~LUK>O9Ib5vRi%HW<7Mp_J)f(nB8V z*yL#VXkv?@xuS=GKUjj&1nvJ(7vm99>e+BG=4cc2-a?;Erlt(enVAZPU?jD;D$+z` zw{fDV;-s-8PP>1%3PhC16xl?aOKT36$Rj%#x@gg|?6Hy71%_=>9m>pB4nM$NNighC mwAWU<*u@@YYwc01t-5dEjrDf~OS}CDz@hzC$g)qmgo9s5fmb{L literal 0 HcmV?d00001 diff --git a/plexus-java/src/test/resources/classfile.version/helloworld-preview.class b/plexus-java/src/test/resources/classfile.version/helloworld-preview.class new file mode 100644 index 0000000000000000000000000000000000000000..7b77d720801fc837b49d57b9d013380575fb9b5c GIT binary patch literal 489 zcmZ`$%T59@6g`Du>Wrf(D&i|qLkzf(55O2BiHQl31q2ta3REa5GfA1z?{cMa;RpCp z;;paI1#NQA$?eI>xwmicFR!y1&avwwgDi&=AP1LWWi0MRO^a|?YxTymA2Yb8Dpc_q zL$*@w`dEO+;Rjem@K4?u87T$~UUb)0CQgKAII2{eso|HRe32+Ukfz-F(bYMY8T^sd zI(mqVCKIO-4J1RpsX}?3OnTCEL{C%4nFtj!6f3uXjN7qM;jmuql1n>EOkZ9qE6%5u zCpHE{a21BqG_;sb<&pvb literal 0 HcmV?d00001