diff --git a/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java b/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java index 70eb921a12454..60021db386e1f 100644 --- a/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java +++ b/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java @@ -25,23 +25,43 @@ package java.lang.reflect; +import java.lang.classfile.ClassFile; + +import jdk.internal.javac.PreviewFeature; + /** - * Class file format versions of the Java virtual machine. - * - * See the appropriate edition of The Java Virtual Machine - * Specification for information about a particular class file - * format version. - * - *

Note that additional class file format version constants will be - * added to model future releases of the Java Virtual Machine - * Specification. + * Class file format versions of the Java virtual machine. Each class file + * format has a particular set of VM features. See The Java Virtual + * Machine Specification, Section {@jvms 4.1} for the list of class + * file format versions supported by the current release. The JVMS also + * describes, for each VM feature, the versions that support that feature, if + * it is not supported by all class file format versions. + *

+ * Future editions of the JVMS may retroactively loosen restrictions imposed on + * a class file format version; for example, the {@linkplain #RELEASE_1 version + * for 1.1} is later extended to allow invocations of static methods in + * interfaces, introduced by Java SE 8; such a class file might not run on the + * release that introduced its format version. For each Java SE release, see + * the corresponding edition of the JVMS for requirements on class files to run + * on that Java SE release. + *

+ * Additional class file format version constants will be added to model future + * class file formats defined by future releases of the JVMS. + *

+ * A special constant, {@link #CURRENT_PREVIEW}, representing the + * preview VM features of the current Java SE release, is not a class file + * format version, but can be viewed as a future class file format version. + * Each of the preview VM features is described by a separate document on the + * site that hosts the corresponding edition of JVMS. Unlike the features in + * class file format versions, the preview VM features are only supported when + * preview features are enabled, and are not {@linkplain #isSupported() + * supported by future releases}. * * @apiNote - * The complete version used in a class file includes a major version - * and a minor version; this enum only models the major version. A - * Java virtual machine implementation is required to support a range - * of major versions; see the corresponding edition of the The - * Java Virtual Machine Specification for details. + * Each class file format version corresponds to exactly one major version and + * one or more minor versions. Each major version corresponds to one class file + * format version, except for {@value ClassFile#JAVA_1_VERSION}, which {@link + * #RELEASE_0} and {@link #RELEASE_1} both correspond to. * * @since 20 * @see System#getProperties System property {@code java.class.version} @@ -50,8 +70,8 @@ @SuppressWarnings("doclint:reference") // cross-module links public enum ClassFileFormatVersion { /* - * Summary of class file format evolution; previews are listed for - * convenience, but they are not modeled by this enum. + * Summary of class file format evolution; previews listed for convenience + * * 1.1: InnerClasses, Synthetic, Deprecated attributes * 1.2: ACC_STRICT modifier * 1.3: no changes @@ -89,6 +109,7 @@ public enum ClassFileFormatVersion { * 22: no changes * 23: no changes * 24: no changes + * 25: no changes */ /** @@ -316,11 +337,10 @@ public enum ClassFileFormatVersion { * The version introduced by the Java Platform, Standard Edition * 21. * - * @since 21 - * * @see * The Java Virtual Machine Specification, Java SE 21 Edition + * @since 21 */ RELEASE_21(65), @@ -328,11 +348,10 @@ public enum ClassFileFormatVersion { * The version introduced by the Java Platform, Standard Edition * 22. * - * @since 22 - * * @see * The Java Virtual Machine Specification, Java SE 22 Edition + * @since 22 */ RELEASE_22(66), @@ -340,11 +359,10 @@ public enum ClassFileFormatVersion { * The version introduced by the Java Platform, Standard Edition * 23. * - * @since 23 - * * @see * The Java Virtual Machine Specification, Java SE 23 Edition + * @since 23 */ RELEASE_23(67), @@ -352,11 +370,10 @@ public enum ClassFileFormatVersion { * The version introduced by the Java Platform, Standard Edition * 24. * - * @since 24 - * * @see * The Java Virtual Machine Specification, Java SE 24 Edition + * @since 24 */ RELEASE_24(68), @@ -364,17 +381,53 @@ public enum ClassFileFormatVersion { * The version introduced by the Java Platform, Standard Edition * 25. * - * @since 25 - * * @see * The Java Virtual Machine Specification, Java SE 25 Edition + * @since 25 */ RELEASE_25(69), - ; // Reduce code churn when appending new constants - // Note to maintainers: when adding constants for newer releases, - // the implementation of latest() must be updated too. + // Note to maintainers: Add new constants right above. + // The implementation of latest() must be updated too. + /** + * An enum constant representing all preview VM features of the {@linkplain + * #latest() current Java SE release} in addition to those of the latest + * class file format version. Unlike VM features associated to enum + * constants representing a class file format version, VM features + * associated to this enum constant are not {@linkplain #isSupported() + * supported} by later Java SE releases. + *

+ * {@code class} files using any preview feature from the current Java SE + * release uses the same major version from that release, but uses the minor + * version {@value %04x ClassFile#PREVIEW_MINOR_VERSION} with all bits set + * to {@code 1}. This Java Runtime Environment does not load any {@code + * class} file using preview features from other Java SE releases. + * + * @apiNote + * While this is not a class file format version, it can be considered as + * the class file format version of an arbitrary future Java SE release. + * Programmers should test their programs with preview features enabled to + * ensure the program is compatible with future Java SE releases. + *

+ * This is a reflective preview API to allow tools running in Java runtime + * environments with no preview feature enabled to access information + * related to preview features. + *

+ * As each Java SE release does not support preview features from any other + * release, this constant does not represent those features, and there is + * no constant representing such features this Java Runtime Environment is + * unaware of. Programmers must check the current Java SE version when + * accessing the preview VM features with this constant. + * + * @see + * JEP 12: Preview Features + * @see + * Java SE Specifications + * @since 25 + */ + @PreviewFeature(feature = PreviewFeature.Feature.LANGUAGE_MODEL, reflective = true) + CURRENT_PREVIEW(ClassFile.latestMajorVersion()); private final int major; @@ -389,6 +442,17 @@ public static ClassFileFormatVersion latest() { return RELEASE_25; } + /** + * {@return whether VM features associated with this enum constant will be + * supported by future Java SE releases} Returns {@code false} only for + * {@link #CURRENT_PREVIEW}. + * + * @since 25 + */ + public boolean isSupported() { + return this != CURRENT_PREVIEW; + } + /** * {@return the major class file version as an integer} * @jvms 4.1 The {@code ClassFile} Structure @@ -436,11 +500,15 @@ public static ClassFileFormatVersion valueOf(Runtime.Version rv) { * runtime version has a {@linkplain Runtime.Version#feature() * feature} large enough to support this class file format version * and has no other elements set. - * + *

* Class file format versions greater than or equal to {@link - * RELEASE_6} have non-{@code null} results. + * #RELEASE_6} have non-{@code null} results. {@link #isSupported() + * isSupported()} determines if runtime versions with greater + * feature support this class file format version. */ public Runtime.Version runtimeVersion() { + if (this == CURRENT_PREVIEW) + return latest().runtimeVersion(); // Starting with Java SE 6, the leading digit was the primary // way of identifying the platform version. if (this.compareTo(RELEASE_6) >= 0) { diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 7461815e4c502..3222b54b805b9 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -80,6 +80,10 @@ public enum Feature { KEY_DERIVATION, @JEP(number = 502, title = "Stable Values", status = "Preview") STABLE_VALUES, + /** + * Reflective preview APIs to access preview language and VM + * features as a whole. + */ LANGUAGE_MODEL, /** * A key for testing. diff --git a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java index 6bc1dab1f8ab7..3374a827e74ef 100644 --- a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java +++ b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java @@ -25,15 +25,26 @@ package javax.lang.model; +import jdk.internal.javac.PreviewFeature; + /** * Source versions of the Java programming language. * * See the appropriate edition of * The Java Language Specification * for information about a particular source version. - * - *

Note that additional source version constants will be added to - * model future releases of the language. + *

+ * Additional source version constants will be added to model future releases + * of the language. + *

+ * A special constant, {@link #CURRENT_PREVIEW}, representing the + * preview language features of the current Java SE release, is not a source + * version, but can be viewed as a future source version. Each of the preview + * language features is described by a separate document on the site that hosts + * the corresponding edition of JLS. Unlike the features in source versions, + * the preview language features are only supported when preview features are + * enabled, and are not {@linkplain #isSupported() supported by future + * releases}. * * @since 1.6 * @see java.lang.reflect.ClassFileFormatVersion @@ -171,13 +182,13 @@ public enum SourceVersion { * Additions in this release include diamond syntax for * constructors, {@code try}-with-resources, strings in switch, * binary literals, and multi-catch. - * @since 1.7 * * @see * The Java Language Specification, Java SE 7 Edition * @see * JSR 334: Small Enhancements to the Java™ Programming Language + * @since 1.7 */ RELEASE_7, @@ -186,13 +197,13 @@ public enum SourceVersion { * 8. * * Additions in this release include lambda expressions and default methods. - * @since 1.8 * * @see * The Java Language Specification, Java SE 8 Edition * @see * JSR 335: Lambda Expressions for the Java™ Programming Language + * @since 1.8 */ RELEASE_8, @@ -203,8 +214,6 @@ public enum SourceVersion { * Additions in this release include modules and removal of a * single underscore from the set of legal identifier names. * - * @since 9 - * * @see * The Java Language Specification, Java SE 9 Edition @@ -212,6 +221,7 @@ public enum SourceVersion { * JSR 376: Java™ Platform Module System * @see * JEP 213: Milling Project Coin + * @since 9 */ RELEASE_9, @@ -222,13 +232,12 @@ public enum SourceVersion { * Additions in this release include local-variable type inference * ({@code var}). * - * @since 10 - * * @see * The Java Language Specification, Java SE 10 Edition * @see * JEP 286: Local-Variable Type Inference + * @since 10 */ RELEASE_10, @@ -239,13 +248,12 @@ public enum SourceVersion { * Additions in this release include local-variable syntax for * lambda parameters. * - * @since 11 - * * @see * The Java Language Specification, Java SE 11 Edition * @see * JEP 323: Local-Variable Syntax for Lambda Parameters + * @since 11 */ RELEASE_11, @@ -254,11 +262,10 @@ public enum SourceVersion { * 12. * No major changes from the prior release. * - * @since 12 - * * @see * The Java Language Specification, Java SE 12 Edition + * @since 12 */ RELEASE_12, @@ -267,11 +274,10 @@ public enum SourceVersion { * 13. * No major changes from the prior release. * - * @since 13 - * * @see * The Java Language Specification, Java SE 13 Edition + * @since 13 */ RELEASE_13, @@ -281,13 +287,12 @@ public enum SourceVersion { * * Additions in this release include switch expressions. * - * @since 14 - * * @see * The Java Language Specification, Java SE 14 Edition * @see * JEP 361: Switch Expressions + * @since 14 */ RELEASE_14, @@ -297,13 +302,12 @@ public enum SourceVersion { * * Additions in this release include text blocks. * - * @since 15 - * * @see * The Java Language Specification, Java SE 15 Edition * @see * JEP 378: Text Blocks + * @since 15 */ RELEASE_15, @@ -314,8 +318,6 @@ public enum SourceVersion { * Additions in this release include records and pattern matching * for {@code instanceof}. * - * @since 16 - * * @see * The Java Language Specification, Java SE 16 Edition @@ -323,6 +325,7 @@ public enum SourceVersion { * JEP 394: Pattern Matching for instanceof * @see * JEP 395: Records + * @since 16 */ RELEASE_16, @@ -333,8 +336,6 @@ public enum SourceVersion { * Additions in this release include sealed classes and * restoration of always-strict floating-point semantics. * - * @since 17 - * * @see * The Java Language Specification, Java SE 17 Edition @@ -342,6 +343,7 @@ public enum SourceVersion { * JEP 306: Restore Always-Strict Floating-Point Semantics * @see * JEP 409: Sealed Classes + * @since 17 */ RELEASE_17, @@ -351,11 +353,10 @@ public enum SourceVersion { * * No major changes from the prior release. * - * @since 18 - * * @see * The Java Language Specification, Java SE 18 Edition + * @since 18 */ RELEASE_18, @@ -365,11 +366,10 @@ public enum SourceVersion { * * No major changes from the prior release. * - * @since 19 - * * @see * The Java Language Specification, Java SE 19 Edition + * @since 19 */ RELEASE_19, @@ -379,11 +379,10 @@ public enum SourceVersion { * * No major changes from the prior release. * - * @since 20 - * * @see * The Java Language Specification, Java SE 20 Edition + * @since 20 */ RELEASE_20, @@ -394,8 +393,6 @@ public enum SourceVersion { * Additions in this release include record patterns and pattern * matching for {@code switch}. * - * @since 21 - * * @see * The Java Language Specification, Java SE 21 Edition @@ -403,6 +400,7 @@ public enum SourceVersion { * JEP 440: Record Patterns * @see * JEP 441: Pattern Matching for switch + * @since 21 */ RELEASE_21, @@ -413,13 +411,12 @@ public enum SourceVersion { * Additions in this release include unnamed variables and unnamed * patterns. * - * @since 22 - * * @see * The Java Language Specification, Java SE 22 Edition * @see * JEP 456: Unnamed Variables & Patterns + * @since 22 */ RELEASE_22, @@ -427,11 +424,10 @@ public enum SourceVersion { * The version introduced by the Java Platform, Standard Edition * 23. * - * @since 23 - * * @see * The Java Language Specification, Java SE 23 Edition + * @since 23 */ RELEASE_23, @@ -439,11 +435,10 @@ public enum SourceVersion { * The version introduced by the Java Platform, Standard Edition * 24. * - * @since 24 - * * @see * The Java Language Specification, Java SE 24 Edition + * @since 24 */ RELEASE_24, @@ -454,8 +449,6 @@ public enum SourceVersion { * Additions in this release include module import declarations * and compact source files and instance main methods. * - * @since 25 - * * @see * The Java Language Specification, Java SE 25 Edition @@ -463,12 +456,45 @@ public enum SourceVersion { * JEP 511: Module Import Declarations * @see * JEP 512: Compact Source Files and Instance Main Methods + * @since 25 */ RELEASE_25, - ; // Reduce code churn when appending new constants - // Note that when adding constants for newer releases, the - // behavior of latest() and latestSupported() must be updated too. + // Note to maintainers: Add new constants right above. + // The implementation of latest() must be updated too. + // Also update the dummy SourceVersion for processing/model/TestSourceVersion. + /** + * An enum constant representing all preview language features of the + * {@linkplain #latest() current Java SE release} in addition to those of + * the latest source version. Unlike language features associated to enum + * constants representing a source version, language features associated to + * this enum constant are not {@linkplain #isSupported() supported} by later + * Java SE releases. + * + * @apiNote + * While this is not a source version, it can be considered as the source + * version of an arbitrary future Java SE release. Programmers should test + * compiling their programs with preview features enabled to ensure the + * program is compatible with future Java SE releases. + *

+ * This is a reflective preview API to allows tools running in Java runtime + * environments with no preview feature enabled to access information + * related to preview features. + *

+ * As each Java SE release does not support preview features from any other + * release, this constant does not represent those features, and there is + * no constant representing such features this Java Runtime Environment is + * unaware of. Programmers must check the current Java SE version when + * accessing the preview language features with this constant. + * + * @see + * JEP 12: Preview Features + * @see + * Java SE Specifications + * @since 25 + */ + @PreviewFeature(feature = PreviewFeature.Feature.LANGUAGE_MODEL, reflective = true) + CURRENT_PREVIEW; /** * {@return the latest source version that can be modeled} @@ -489,7 +515,7 @@ public static SourceVersion latest() { private static SourceVersion getLatestSupported() { int intVersion = Runtime.version().feature(); return (intVersion >= 11) ? - valueOf("RELEASE_" + Math.min(25, intVersion)): + valueOf("RELEASE_" + Math.min(latest().ordinal(), intVersion)): RELEASE_10; } @@ -498,7 +524,7 @@ private static SourceVersion getLatestSupported() { * current execution environment} {@code RELEASE_9} or later must * be returned. * - * @apiNote This method is included alongside {@link latest} to + * @apiNote This method is included alongside {@link #latest} to * allow identification of situations where the language model API * is running on a platform version different from the latest * version modeled by the API. One way that sort of situation can @@ -746,18 +772,34 @@ public static SourceVersion valueOf(Runtime.Version rv) { return valueOf("RELEASE_" + rv.feature()); } + /** + * {@return whether language features associated with this enum constant + * will be supported by future Java SE releases} Returns {@code false} only + * for {@link #CURRENT_PREVIEW}. + * + * @since 25 + */ + public boolean isSupported() { + return this != CURRENT_PREVIEW; + } + /** * {@return the least runtime version that supports this source * version; otherwise {@code null}} The returned runtime version * has a {@linkplain Runtime.Version#feature() feature} large * enough to support this source version and has no other elements * set. - * + *

* Source versions greater than or equal to {@link RELEASE_6} - * have non-{@code null} results. + * have non-{@code null} results. {@link #isSupported() isSupported()} + * determines if runtime versions with greater feature support this source + * version. + * * @since 18 */ public Runtime.Version runtimeVersion() { + if (this == CURRENT_PREVIEW) + return latest().runtimeVersion(); // The javax.lang.model API was added in JDK 6; for now, // limiting supported range to 6 and up. if (this.compareTo(RELEASE_6) >= 0) { diff --git a/test/jdk/java/lang/reflect/ClassFileFormatVersionTest.java b/test/jdk/java/lang/reflect/ClassFileFormatVersionTest.java new file mode 100644 index 0000000000000..9262a4130c082 --- /dev/null +++ b/test/jdk/java/lang/reflect/ClassFileFormatVersionTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.classfile.ClassFile; +import java.lang.reflect.ClassFileFormatVersion; +import java.util.Arrays; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static java.lang.reflect.ClassFileFormatVersion.CURRENT_PREVIEW; +import static java.lang.reflect.ClassFileFormatVersion.latest; +import static org.junit.jupiter.api.Assertions.*; + +/* + * @test + * @bug 8355536 + * @summary Sanity test for ClassFileFormatVersion + * @run junit ClassFileFormatVersionTest + */ +class ClassFileFormatVersionTest { + @Test + void testLatest() { + var latest = ClassFileFormatVersion.latest(); + assertNotSame(CURRENT_PREVIEW, latest); + assertEquals(ClassFile.latestMajorVersion(), latest.major()); + assertTrue(latest.isSupported()); + assertEquals(ClassFileFormatVersion.values().length - 2, latest.ordinal()); + } + + @Test + void testCurrentPreview() { + assertTrue(ClassFileFormatVersion.latest().compareTo(CURRENT_PREVIEW) < 0); + assertEquals(ClassFile.latestMajorVersion(), CURRENT_PREVIEW.major()); + assertFalse(CURRENT_PREVIEW.isSupported()); + assertEquals(ClassFileFormatVersion.values().length - 1, CURRENT_PREVIEW.ordinal()); + assertEquals(latest().runtimeVersion(), CURRENT_PREVIEW.runtimeVersion()); + } + + static Stream actualCffvs() { + return Arrays.stream(ClassFileFormatVersion.values()).filter(ClassFileFormatVersion::isSupported); + } + + @ParameterizedTest + @MethodSource("actualCffvs") + void testEachVersion(ClassFileFormatVersion cffv) { + if (cffv.compareTo(ClassFileFormatVersion.RELEASE_6) >= 0) { + assertEquals(cffv.major() - 44, cffv.runtimeVersion().feature()); + assertEquals(cffv, ClassFileFormatVersion.valueOf(cffv.runtimeVersion())); + } + if (cffv != ClassFileFormatVersion.RELEASE_0) { + assertEquals(cffv, ClassFileFormatVersion.fromMajor(cffv.major())); + } + } + + @Test + void testFromMajor() { + for (int i = -1; i < ClassFile.JAVA_1_VERSION; i++) { + final int major = i; + assertThrows(IllegalArgumentException.class, () -> ClassFileFormatVersion.fromMajor(major)); + } + for (int i = ClassFile.JAVA_1_VERSION; i <= ClassFile.latestMajorVersion(); i++) { + var cffv = ClassFileFormatVersion.fromMajor(i); + assertTrue(cffv.isSupported()); + assertEquals(i, cffv.major()); + assertNotSame(CURRENT_PREVIEW, cffv); + } + assertThrows(IllegalArgumentException.class, () -> ClassFileFormatVersion.fromMajor(ClassFile.latestMajorVersion() + 1)); + } +} diff --git a/test/langtools/tools/javac/processing/model/TestSourceVersion.java b/test/langtools/tools/javac/processing/model/TestSourceVersion.java index 3a56bf76007fe..eb3f0cbd4d4f4 100644 --- a/test/langtools/tools/javac/processing/model/TestSourceVersion.java +++ b/test/langtools/tools/javac/processing/model/TestSourceVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,75 +23,113 @@ /* * @test - * @bug 7025809 8028543 6415644 8028544 8029942 8187951 8193291 8196551 8233096 8275308 + * @bug 7025809 8028543 6415644 8028544 8029942 8187951 8193291 8196551 8233096 + * 8275308 8355536 * @summary Test latest, latestSupported, underscore as keyword, etc. * @author Joseph D. Darcy * @modules java.compiler * jdk.compiler + * @run junit/othervm -DTestSourceVersion.DIFFERENT_LATEST_SUPPORTED=false TestSourceVersion + * @build java.compiler/javax.lang.model.SourceVersion + * @run junit/othervm -DTestSourceVersion.DIFFERENT_LATEST_SUPPORTED=true TestSourceVersion */ import java.util.*; import java.util.function.Predicate; +import java.util.stream.Stream; + import javax.lang.model.SourceVersion; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import static javax.lang.model.SourceVersion.*; +import static org.junit.jupiter.api.Assertions.*; /** * Verify behavior of latest[Supported] and other methods. + * There's a copy of "updated" SourceVersion in java.compiler subdirectory + * to emulate running with a newer java.compiler module. */ public class TestSourceVersion { - public static void main(String... args) { - testLatestSupported(); - testVersionVaryingKeywords(); - testRestrictedKeywords(); - testVar(); - testYield(); - testValueOfRV(); - testRuntimeVersion(); - } - private static void testLatestSupported() { + private static final boolean DIFFERENT_LATEST_SUPPORTED = Boolean.getBoolean("TestSourceVersion.DIFFERENT_LATEST_SUPPORTED"); + + @Test + void testLatestSupported() { SourceVersion[] values = SourceVersion.values(); - SourceVersion last = values[values.length - 1]; + SourceVersion last = values[values.length - 2]; SourceVersion latest = SourceVersion.latest(); SourceVersion latestSupported = SourceVersion.latestSupported(); - if (latest == last && - latestSupported == SourceVersion.valueOf("RELEASE_" + - Runtime.version().feature()) && - (latest == latestSupported || - (latest.ordinal() - latestSupported.ordinal() == 1)) ) - return; - else { - throw new RuntimeException("Unexpected release value(s) found:\n" + - "latest:\t" + latest + "\n" + - "latestSupported:\t" + latestSupported); + assertSame(last, latest); + assertSame(latestSupported, SourceVersion.valueOf("RELEASE_" + Runtime.version().feature())); + assertSame(Runtime.version().feature(), latestSupported.runtimeVersion().feature()); + if (DIFFERENT_LATEST_SUPPORTED) { + assertEquals(latestSupported.ordinal(), latest.ordinal() - 1, () -> latestSupported.toString() + " ordinal"); + } else { + assertSame(latest, latestSupported); } } - private static void testVersionVaryingKeywords() { + @Test + void testCurrentPreview() { + final SourceVersion preview = CURRENT_PREVIEW; + + assertFalse(preview.isSupported()); + assertNotSame(CURRENT_PREVIEW, SourceVersion.latest()); + assertNotSame(CURRENT_PREVIEW, SourceVersion.latestSupported()); + assertEquals(values().length - 1, CURRENT_PREVIEW.ordinal()); + + assertEquals(latest().runtimeVersion(), CURRENT_PREVIEW.runtimeVersion()); + } + + static Stream actualSvs() { + return Arrays.stream(SourceVersion.values()).filter(SourceVersion::isSupported); + } + + @ParameterizedTest + @MethodSource("actualSvs") + void testEachVersion(SourceVersion sv) { + if (sv.compareTo(SourceVersion.RELEASE_6) >= 0) { + assertEquals(sv, SourceVersion.valueOf(sv.runtimeVersion())); + } + Runtime.Version result = sv.runtimeVersion(); + if (sv.compareTo(RELEASE_6) < 0) { + assertNull(result); + } else { + Runtime.Version expected = Runtime.Version.parse(Integer.toString(sv.ordinal())); + assertEquals(expected, result); + } + } + + static Stream keywordStart() { Map keyWordStart = Map.of("strictfp", RELEASE_2, "assert", RELEASE_4, "enum", RELEASE_5, "_", RELEASE_9); - for (Map.Entry entry : keyWordStart.entrySet()) { - String key = entry.getKey(); - SourceVersion value = entry.getValue(); + return keyWordStart.entrySet().stream().map(e -> Arguments.of(e.getKey(), e.getValue())); + } - check(true, key, (String s) -> isKeyword(s), "keyword", latest()); - check(false, key, (String s) -> isName(s), "name", latest()); + @ParameterizedTest + @MethodSource("keywordStart") + void testVersionVaryingKeywords(String key, SourceVersion value) { + check(true, key, (String s) -> isKeyword(s), "keyword", latest()); + check(false, key, (String s) -> isName(s), "name", latest()); - for(SourceVersion version : SourceVersion.values()) { - boolean isKeyword = version.compareTo(value) >= 0; + for(SourceVersion version : SourceVersion.values()) { + boolean isKeyword = version.compareTo(value) >= 0; - check(isKeyword, key, (String s) -> isKeyword(s, version), "keyword", version); - check(!isKeyword, key, (String s) -> isName(s, version), "name", version); - } + check(isKeyword, key, (String s) -> isKeyword(s, version), "keyword", version); + check(!isKeyword, key, (String s) -> isName(s, version), "name", version); } } - private static void testRestrictedKeywords() { + @Test + void testRestrictedKeywords() { // Restricted keywords are not full keywords /* @@ -114,7 +152,8 @@ private static void testRestrictedKeywords() { } } - private static void testVar() { + @Test + void testVar() { for (SourceVersion version : SourceVersion.values()) { Predicate isKeywordVersion = (String s) -> isKeyword(s, version); Predicate isNameVersion = (String s) -> isName(s, version); @@ -126,7 +165,8 @@ private static void testVar() { } } - private static void testYield() { + @Test + void testYield() { for (SourceVersion version : SourceVersion.values()) { Predicate isKeywordVersion = (String s) -> isKeyword(s, version); Predicate isNameVersion = (String s) -> isName(s, version); @@ -138,11 +178,11 @@ private static void testYield() { } } - private static void check(boolean expected, - String input, - Predicate predicate, - String message, - SourceVersion version) { + void check(boolean expected, + String input, + Predicate predicate, + String message, + SourceVersion version) { boolean result = predicate.test(input); if (result != expected) { throw new RuntimeException("Unexpected " + message + "-ness of " + input + @@ -155,11 +195,10 @@ private static void check(boolean expected, * SourceVersion properly. The SourceVersion result is only a * function of the feature() component of a Runtime.Version. */ - private static void testValueOfRV() { + @Test + void testValueOfRV() { for (SourceVersion sv : SourceVersion.values()) { - if (sv == RELEASE_0) { - continue; - } else { + if (sv != RELEASE_0 && sv.isSupported()) { // Plain mapping; e.g. "17" -> RELEASE_17 String featureBase = Integer.toString(sv.ordinal()); checkValueOfResult(sv, featureBase); @@ -179,7 +218,7 @@ private static void testValueOfRV() { } } - private static void checkValueOfResult(SourceVersion expected, String versionString) { + void checkValueOfResult(SourceVersion expected, String versionString) { Runtime.Version rv = Runtime.Version.parse(versionString); SourceVersion result = SourceVersion.valueOf(rv); if (result != expected) { @@ -188,22 +227,4 @@ private static void checkValueOfResult(SourceVersion expected, String versionStr " intead of " + expected); } } - - private static void testRuntimeVersion() { - for (SourceVersion sv : SourceVersion.values()) { - Runtime.Version result = sv.runtimeVersion(); - if (sv.compareTo(RELEASE_6) < 0) { - if (result != null) { - throw new RuntimeException("Unexpected result non-null " + result + - " as runtime version of " + sv); - } - } else { - Runtime.Version expected = Runtime.Version.parse(Integer.toString(sv.ordinal())); - if (!result.equals(expected)) { - throw new RuntimeException("Unexpected result " + result + - " as runtime version of " + sv); - } - } - } - } } diff --git a/test/langtools/tools/javac/processing/model/java.compiler/javax/lang/model/SourceVersion.java b/test/langtools/tools/javac/processing/model/java.compiler/javax/lang/model/SourceVersion.java new file mode 100644 index 0000000000000..ab46746de58d4 --- /dev/null +++ b/test/langtools/tools/javac/processing/model/java.compiler/javax/lang/model/SourceVersion.java @@ -0,0 +1,813 @@ +/* + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.lang.model; + +import jdk.internal.javac.PreviewFeature; + +/** + * Source versions of the Java programming language. + * + * See the appropriate edition of + * The Java Language Specification + * for information about a particular source version. + *

+ * Additional source version constants will be added to model future releases + * of the language. + *

+ * A special constant, {@link #CURRENT_PREVIEW}, representing the + * preview language features of the current Java SE release, is not a source + * version, but can be viewed as a future source version. Each of the preview + * language features is described by a separate document on the site that hosts + * the corresponding edition of JLS. Unlike the features in source versions, + * the preview language features are only supported when preview features are + * enabled, and are not {@linkplain #isSupported() supported by future + * releases}. + * + * @since 1.6 + * @see java.lang.reflect.ClassFileFormatVersion + */ +public enum SourceVersion { + /* + * Summary of language evolution + * 1.1: nested classes + * 1.2: strictfp + * 1.3: no changes + * 1.4: assert + * 1.5: annotations, generics, autoboxing, var-args... + * 1.6: no changes + * 1.7: diamond syntax, try-with-resources, etc. + * 1.8: lambda expressions and default methods + * 9: modules, small cleanups to 1.7 and 1.8 changes + * 10: local-variable type inference (var) + * 11: local-variable syntax for lambda parameters + * 12: no changes (switch expressions in preview) + * 13: no changes (text blocks in preview; switch expressions in + * second preview) + * 14: switch expressions (pattern matching and records in + * preview; text blocks in second preview) + * 15: text blocks (sealed classes in preview; records and pattern + * matching in second preview) + * 16: records and pattern matching (sealed classes in second preview) + * 17: sealed classes, floating-point always strict (pattern + * matching for switch in preview) + * 18: no changes (pattern matching for switch in second preview) + * 19: no changes (pattern matching for switch in third preview, + * record patterns in preview) + * 20: no changes (pattern matching for switch in fourth preview, + * record patterns in second preview) + * 21: pattern matching for switch and record patterns (string + * templates in preview, unnamed patterns and variables in + * preview, unnamed classes and instance main methods in preview) + * 22: unnamed variables & patterns (statements before super(...) + * in preview, string templates in second preview, implicitly + * declared classes and instance main methods in second preview) + * 23: no changes (primitive Types in Patterns, instanceof, and + * switch in preview, module Import Declarations in preview, + * implicitly declared classes and instance main in third + * preview, flexible constructor bodies in second preview) + * 24: no changes (primitive Types in Patterns, instanceof, and + * switch in second preview, module Import Declarations in second + * preview, simple source files and instance main in fourth + * preview, flexible constructor bodies in third preview) + * 25: module import declarations, compact source files and + * instance main methods, + */ + + /** + * The original version. + * + * The language described in + * The Java Language Specification, First Edition. + */ + RELEASE_0, + + /** + * The version introduced by the Java Platform 1.1. + * + * The language is {@code RELEASE_0} augmented with nested classes + * as described in the 1.1 update to The Java Language + * Specification, First Edition. + */ + RELEASE_1, + + /** + * The version introduced by the Java 2 Platform, Standard Edition, + * v 1.2. + * + * The language described in + * The Java Language Specification, + * Second Edition, which includes the {@code + * strictfp} modifier. + */ + RELEASE_2, + + /** + * The version introduced by the Java 2 Platform, Standard Edition, + * v 1.3. + * + * No major changes from {@code RELEASE_2}. + */ + RELEASE_3, + + /** + * The version introduced by the Java 2 Platform, Standard Edition, + * v 1.4. + * + * Added a simple assertion facility. + * + * @see + * JSR 41: A Simple Assertion Facility + */ + RELEASE_4, + + /** + * The version introduced by the Java 2 Platform, Standard + * Edition 5.0. + * + * The language described in + * The Java Language Specification, + * Third Edition. First release to support + * generics, annotations, autoboxing, var-args, enhanced {@code + * for} loop, and hexadecimal floating-point literals. + * + * @see + * JSR 14: Add Generic Types To The Java™ Programming Language + * @see + * JSR 175: A Metadata Facility for the Java™ Programming Language + * @see + * JSR 201: Extending the Java™ Programming Language with Enumerations, + * Autoboxing, Enhanced for loops and Static Import + */ + RELEASE_5, + + /** + * The version introduced by the Java Platform, Standard Edition + * 6. + * + * No major changes from {@code RELEASE_5}. + * + * @see + * The Java Language Specification, Third Edition + */ + RELEASE_6, + + /** + * The version introduced by the Java Platform, Standard Edition + * 7. + * + * Additions in this release include diamond syntax for + * constructors, {@code try}-with-resources, strings in switch, + * binary literals, and multi-catch. + * + * @see + * The Java Language Specification, Java SE 7 Edition + * @see + * JSR 334: Small Enhancements to the Java™ Programming Language + * @since 1.7 + */ + RELEASE_7, + + /** + * The version introduced by the Java Platform, Standard Edition + * 8. + * + * Additions in this release include lambda expressions and default methods. + * + * @see + * The Java Language Specification, Java SE 8 Edition + * @see + * JSR 335: Lambda Expressions for the Java™ Programming Language + * @since 1.8 + */ + RELEASE_8, + + /** + * The version introduced by the Java Platform, Standard Edition + * 9. + * + * Additions in this release include modules and removal of a + * single underscore from the set of legal identifier names. + * + * @see + * The Java Language Specification, Java SE 9 Edition + * @see + * JSR 376: Java™ Platform Module System + * @see + * JEP 213: Milling Project Coin + * @since 9 + */ + RELEASE_9, + + /** + * The version introduced by the Java Platform, Standard Edition + * 10. + * + * Additions in this release include local-variable type inference + * ({@code var}). + * + * @see + * The Java Language Specification, Java SE 10 Edition + * @see + * JEP 286: Local-Variable Type Inference + * @since 10 + */ + RELEASE_10, + + /** + * The version introduced by the Java Platform, Standard Edition + * 11. + * + * Additions in this release include local-variable syntax for + * lambda parameters. + * + * @see + * The Java Language Specification, Java SE 11 Edition + * @see + * JEP 323: Local-Variable Syntax for Lambda Parameters + * @since 11 + */ + RELEASE_11, + + /** + * The version introduced by the Java Platform, Standard Edition + * 12. + * No major changes from the prior release. + * + * @see + * The Java Language Specification, Java SE 12 Edition + * @since 12 + */ + RELEASE_12, + + /** + * The version introduced by the Java Platform, Standard Edition + * 13. + * No major changes from the prior release. + * + * @see + * The Java Language Specification, Java SE 13 Edition + * @since 13 + */ + RELEASE_13, + + /** + * The version introduced by the Java Platform, Standard Edition + * 14. + * + * Additions in this release include switch expressions. + * + * @see + * The Java Language Specification, Java SE 14 Edition + * @see + * JEP 361: Switch Expressions + * @since 14 + */ + RELEASE_14, + + /** + * The version introduced by the Java Platform, Standard Edition + * 15. + * + * Additions in this release include text blocks. + * + * @see + * The Java Language Specification, Java SE 15 Edition + * @see + * JEP 378: Text Blocks + * @since 15 + */ + RELEASE_15, + + /** + * The version introduced by the Java Platform, Standard Edition + * 16. + * + * Additions in this release include records and pattern matching + * for {@code instanceof}. + * + * @see + * The Java Language Specification, Java SE 16 Edition + * @see + * JEP 394: Pattern Matching for instanceof + * @see + * JEP 395: Records + * @since 16 + */ + RELEASE_16, + + /** + * The version introduced by the Java Platform, Standard Edition + * 17. + * + * Additions in this release include sealed classes and + * restoration of always-strict floating-point semantics. + * + * @see + * The Java Language Specification, Java SE 17 Edition + * @see + * JEP 306: Restore Always-Strict Floating-Point Semantics + * @see + * JEP 409: Sealed Classes + * @since 17 + */ + RELEASE_17, + + /** + * The version introduced by the Java Platform, Standard Edition + * 18. + * + * No major changes from the prior release. + * + * @see + * The Java Language Specification, Java SE 18 Edition + * @since 18 + */ + RELEASE_18, + + /** + * The version introduced by the Java Platform, Standard Edition + * 19. + * + * No major changes from the prior release. + * + * @see + * The Java Language Specification, Java SE 19 Edition + * @since 19 + */ + RELEASE_19, + + /** + * The version introduced by the Java Platform, Standard Edition + * 20. + * + * No major changes from the prior release. + * + * @see + * The Java Language Specification, Java SE 20 Edition + * @since 20 + */ + RELEASE_20, + + /** + * The version introduced by the Java Platform, Standard Edition + * 21. + * + * Additions in this release include record patterns and pattern + * matching for {@code switch}. + * + * @see + * The Java Language Specification, Java SE 21 Edition + * @see + * JEP 440: Record Patterns + * @see + * JEP 441: Pattern Matching for switch + * @since 21 + */ + RELEASE_21, + + /** + * The version introduced by the Java Platform, Standard Edition + * 22. + * + * Additions in this release include unnamed variables and unnamed + * patterns. + * + * @see + * The Java Language Specification, Java SE 22 Edition + * @see + * JEP 456: Unnamed Variables & Patterns + + * @since 22 + */ + RELEASE_22, + + /** + * The version introduced by the Java Platform, Standard Edition + * 23. + * + * @see + * The Java Language Specification, Java SE 23 Edition + * @since 23 + */ + RELEASE_23, + + /** + * The version introduced by the Java Platform, Standard Edition + * 24. + * + * @see + * The Java Language Specification, Java SE 24 Edition + * @since 24 + */ + RELEASE_24, + + /** + * The version introduced by the Java Platform, Standard Edition + * 25. + * + * Additions in this release include module import declarations + * and compact source files and instance main methods. + * + * @see + * The Java Language Specification, Java SE 25 Edition + * @see + * JEP 511: Module Import Declarations + * @see + * JEP 512: Compact Source Files and Instance Main Methods + * @since 25 + */ + RELEASE_25, + RELEASE_26, + + // Note to maintainers: Add new constants right above. + // The implementation of latest() must be updated too. + // Also update the dummy SourceVersion for processing/model/TestSourceVersion. + /** + * An enum constant representing all preview language features of the + * {@linkplain #latest() current Java SE release} in addition to those of + * the latest source version. Unlike language features associated to enum + * constants representing a source version, language features associated to + * this enum constant are not {@linkplain #isSupported() supported} by later + * Java SE releases. + * + * @apiNote + * While this is not a source version, it can be considered as the source + * version of an arbitrary future Java SE release. Programmers should test + * compiling their programs with preview features enabled to ensure the + * program is compatible with future Java SE releases. + *

+ * This is a reflective preview API to allows tools running in Java runtime + * environments with no preview feature enabled to access information + * related to preview features. + *

+ * As each Java SE release does not support preview features from any other + * release, this constant does not represent those features, and there is + * no constant representing such features this Java Runtime Environment is + * unaware of. Programmers must check the current Java SE version when + * accessing the preview language features with this constant. + * + * @see + * JEP 12: Preview Features + * @see + * Java SE Specifications + * @since 25 + */ + @PreviewFeature(feature = PreviewFeature.Feature.LANGUAGE_MODEL, reflective = true) + CURRENT_PREVIEW; + + /** + * {@return the latest source version that can be modeled} + */ + public static SourceVersion latest() { + return RELEASE_26; + } + + private static final SourceVersion latestSupported = getLatestSupported(); + + /* + * The integer version to enum constant mapping implemented by + * this method assumes the JEP 322: "Time-Based Release + * Versioning" scheme is in effect. This scheme began in JDK + * 10. If the JDK versioning scheme is revised, this method may + * need to be updated accordingly. + */ + private static SourceVersion getLatestSupported() { + int intVersion = Runtime.version().feature(); + return (intVersion >= 11) ? + valueOf("RELEASE_" + Math.min(latest().ordinal(), intVersion)): + RELEASE_10; + } + + /** + * {@return the latest source version fully supported by the + * current execution environment} {@code RELEASE_9} or later must + * be returned. + * + * @apiNote This method is included alongside {@link #latest} to + * allow identification of situations where the language model API + * is running on a platform version different from the latest + * version modeled by the API. One way that sort of situation can + * occur is if an IDE or similar tool is using the API to model + * source version N while running on platform version + * (N - 1). Running in this configuration is + * supported by the API. Running an API on platform versions + * earlier than (N - 1) or later than N + * may or may not work as an implementation detail. If an + * annotation processor was generating code to run under the + * current execution environment, the processor should only use + * platform features up to the {@code latestSupported} release, + * which may be earlier than the {@code latest} release. + */ + public static SourceVersion latestSupported() { + return latestSupported; + } + + /** + * Returns whether or not {@code name} is a syntactically valid + * identifier (simple name) or keyword in the latest source + * version. The method returns {@code true} if the name consists + * of an initial character for which {@link + * Character#isJavaIdentifierStart(int)} returns {@code true}, + * followed only by characters for which {@link + * Character#isJavaIdentifierPart(int)} returns {@code true}. + * This pattern matches regular identifiers, keywords, contextual + * keywords, boolean literals, and the null literal. + * + * The method returns {@code false} for all other strings. + * + * @param name the string to check + * @return {@code true} if this string is a + * syntactically valid identifier or keyword, {@code false} + * otherwise. + * + * @jls 3.8 Identifiers + */ + public static boolean isIdentifier(CharSequence name) { + String id = name.toString(); + + if (id.length() == 0) { + return false; + } + int cp = id.codePointAt(0); + if (!Character.isJavaIdentifierStart(cp)) { + return false; + } + for (int i = Character.charCount(cp); + i < id.length(); + i += Character.charCount(cp)) { + cp = id.codePointAt(i); + if (!Character.isJavaIdentifierPart(cp)) { + return false; + } + } + return true; + } + + /** + * Returns whether or not {@code name} is a syntactically valid + * qualified name in the latest source version. + * + * Syntactically, a qualified name is a sequence of identifiers + * separated by period characters ("{@code .}"). This method + * splits the input string into period-separated segments and + * applies checks to each segment in turn. + * + * Unlike {@link #isIdentifier isIdentifier}, this method returns + * {@code false} for keywords, boolean literals, and the null + * literal in any segment. + * + * This method returns {@code true} for contextual + * keywords. + * + * @param name the string to check + * @return {@code true} if this string is a + * syntactically valid name, {@code false} otherwise. + * @jls 3.9 Keywords + * @jls 6.2 Names and Identifiers + */ + public static boolean isName(CharSequence name) { + return isName(name, latest()); + } + + /** + * Returns whether or not {@code name} is a syntactically valid + * qualified name in the given source version. + * + * Syntactically, a qualified name is a sequence of identifiers + * separated by period characters ("{@code .}"). This method + * splits the input string into period-separated segments and + * applies checks to each segment in turn. + * + * Unlike {@link #isIdentifier isIdentifier}, this method returns + * {@code false} for keywords, boolean literals, and the null + * literal in any segment. + * + * This method returns {@code true} for contextual + * keywords. + * + * @param name the string to check + * @param version the version to use + * @return {@code true} if this string is a + * syntactically valid name, {@code false} otherwise. + * @jls 3.9 Keywords + * @jls 6.2 Names and Identifiers + * @since 9 + */ + public static boolean isName(CharSequence name, SourceVersion version) { + String id = name.toString(); + + for(String s : id.split("\\.", -1)) { + if (!isIdentifier(s) || isKeyword(s, version)) + return false; + } + return true; + } + + /** + * Returns whether or not {@code s} is a keyword, a boolean literal, + * or the null literal in the latest source version. + * This method returns {@code false} for contextual + * keywords. + * + * @param s the string to check + * @return {@code true} if {@code s} is a keyword, a boolean + * literal, or the null literal, {@code false} otherwise. + * @jls 3.9 Keywords + * @jls 3.10.3 Boolean Literals + * @jls 3.10.8 The Null Literal + */ + public static boolean isKeyword(CharSequence s) { + return isKeyword(s, latest()); + } + + /** + * Returns whether or not {@code s} is a keyword, a boolean literal, + * or the null literal in the given source version. + * This method returns {@code false} for contextual + * keywords. + * + * @param s the string to check + * @param version the version to use + * @return {@code true} if {@code s} is a keyword, a boolean + * literal, or the null literal, {@code false} otherwise. + * @jls 3.9 Keywords + * @jls 3.10.3 Boolean Literals + * @jls 3.10.8 The Null Literal + * @since 9 + */ + public static boolean isKeyword(CharSequence s, SourceVersion version) { + String id = s.toString(); + switch(id) { + // A trip through history + case "strictfp": + return version.compareTo(RELEASE_2) >= 0; + + case "assert": + return version.compareTo(RELEASE_4) >= 0; + + case "enum": + return version.compareTo(RELEASE_5) >= 0; + + case "_": + return version.compareTo(RELEASE_9) >= 0; + + // case "non-sealed": can be added once it is a keyword only + // dependent on release and not also preview features being + // enabled. + + // Keywords common across versions + + // Modifiers + case "public": case "protected": case "private": + case "abstract": case "static": case "final": + case "transient": case "volatile": case "synchronized": + case "native": + + // Declarations + case "class": case "interface": case "extends": + case "package": case "throws": case "implements": + + // Primitive types and void + case "boolean": case "byte": case "char": + case "short": case "int": case "long": + case "float": case "double": + case "void": + + // Control flow + case "if": case "else": + case "try": case "catch": case "finally": + case "do": case "while": + case "for": case "continue": + case "switch": case "case": case "default": + case "break": case "throw": case "return": + + // Other keywords + case "this": case "new": case "super": + case "import": case "instanceof": + + // Forbidden! + case "goto": case "const": + + // literals + case "null": case "true": case "false": + return true; + + default: + return false; + } + } + + /** + * {@return the latest source version that is usable under the + * runtime version argument} If the runtime version's {@linkplain + * Runtime.Version#feature() feature} is greater than the feature + * of the {@linkplain #runtimeVersion() runtime version} of the + * {@linkplain #latest() latest source version}, an {@code + * IllegalArgumentException} is thrown. + * + *

Because the source versions of the Java programming language + * have so far followed a linear progression, only the feature + * component of a runtime version is queried to determine the + * mapping to a source version. If that linearity changes in the + * future, other components of the runtime version may influence + * the result. + * + * @apiNote + * An expression to convert from a string value, for example + * {@code "17"}, to the corresponding source version, {@code + * RELEASE_17}, is: + * + * {@snippet lang="java" : + * SourceVersion.valueOf(Runtime.Version.parse("17"))} + * + * @param rv runtime version to map to a source version + * @throws IllegalArgumentException if the feature of version + * argument is greater than the feature of the platform version. + * @since 18 + */ + public static SourceVersion valueOf(Runtime.Version rv) { + // Could also implement this as a switch where a case was + // added with each new release. + return valueOf("RELEASE_" + rv.feature()); + } + + /** + * {@return whether language features associated with this enum constant + * will be supported by future Java SE releases} Returns {@code false} only + * for {@link #CURRENT_PREVIEW}. + * + * @since 25 + */ + public boolean isSupported() { + return this != CURRENT_PREVIEW; + } + + /** + * {@return the least runtime version that supports this source + * version; otherwise {@code null}} The returned runtime version + * has a {@linkplain Runtime.Version#feature() feature} large + * enough to support this source version and has no other elements + * set. + *

+ * Source versions greater than or equal to {@link RELEASE_6} + * have non-{@code null} results. {@link #isSupported() isSupported()} + * determines if runtime versions with greater feature support this source + * version. + * + * @since 18 + */ + public Runtime.Version runtimeVersion() { + if (this == CURRENT_PREVIEW) + return latest().runtimeVersion(); + // The javax.lang.model API was added in JDK 6; for now, + // limiting supported range to 6 and up. + if (this.compareTo(RELEASE_6) >= 0) { + return Runtime.Version.parse(Integer.toString(ordinal())); + } else { + return null; + } + } +}