diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index e19871abf89..1923db861c7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -272,7 +272,10 @@ class GenerationState private constructor( val rootContext: CodegenContext<*> = RootContext(this) - val classFileVersion: Int = target.bytecodeVersion + val classFileVersion: Int = run { + val minorVersion = if (configuration.getBoolean(JVMConfigurationKeys.ENABLE_JVM_PREVIEW)) 0xffff else 0 + (minorVersion shl 16) + target.majorVersion + } val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index e86edf8ff64..6ba157ce2d2 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -418,6 +418,13 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { ) var useOldInlineClassesManglingScheme: Boolean by FreezableVar(false) + @Argument( + value = "-Xjvm-enable-preview", + description = "Allow using features from Java language that are in preview phase.\n" + + "Works as `--enable-preview` in Java. All class files are marked as preview-generated thus it won't be possible to use them in release environment" + ) + var enableJvmPreview: Boolean by FreezableVar(false) + override fun configureAnalysisFlags(collector: MessageCollector): MutableMap, Any> { val result = super.configureAnalysisFlags(collector) result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index 87cd6044808..16b31435ac4 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt @@ -67,19 +67,19 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu } } - if (languageVersionSettings.supportsFeature(LanguageFeature.JvmRecordSupport) && !jvmTarget.isRecordsAllowed()) { + if (languageVersionSettings.supportsFeature(LanguageFeature.JvmRecordSupport) && !jvmTarget.areRecordsAllowed(arguments.enableJvmPreview)) { messageCollector.report( ERROR, - "-XXLanguage:+${LanguageFeature.JvmRecordSupport} feature is only supported with JVM target ${JvmTarget.JVM_15_PREVIEW.description} or later" + "-XXLanguage:+${LanguageFeature.JvmRecordSupport} feature is only supported with JVM target ${JvmTarget.JVM_15.description} or later" ) } addAll(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, arguments.additionalJavaModules?.asList()) } -private fun JvmTarget.isRecordsAllowed(): Boolean { - if (majorVersion < JvmTarget.JVM_15_PREVIEW.majorVersion) return false - return isPreview || majorVersion > JvmTarget.JVM_15_PREVIEW.majorVersion +private fun JvmTarget.areRecordsAllowed(enableJvmPreview: Boolean): Boolean { + if (majorVersion < JvmTarget.JVM_15.majorVersion) return false + return enableJvmPreview || majorVersion > JvmTarget.JVM_15.majorVersion } fun CompilerConfiguration.configureJdkHome(arguments: K2JVMCompilerArguments): Boolean { @@ -244,6 +244,12 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule) put(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS, arguments.useOldSpilledVarTypeAnalysis) put(JVMConfigurationKeys.USE_OLD_INLINE_CLASSES_MANGLING_SCHEME, arguments.useOldInlineClassesManglingScheme) + put(JVMConfigurationKeys.ENABLE_JVM_PREVIEW, arguments.enableJvmPreview) + + if (arguments.enableJvmPreview) { + getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) + .report(INFO, "Using preview Java language features") + } arguments.declarationsOutputPath?.let { put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) } } diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index bc896a1f725..de4c0f9e015 100644 --- a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -140,4 +140,7 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey USE_OLD_INLINE_CLASSES_MANGLING_SCHEME = CompilerConfigurationKey.create("Use old, 1.4 version of inline classes mangling scheme"); + + public static final CompilerConfigurationKey ENABLE_JVM_PREVIEW = + CompilerConfigurationKey.create("Enable Java language preview features"); } diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmTarget.kt b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmTarget.kt index bcb55969294..883283cc6b8 100644 --- a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmTarget.kt +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmTarget.kt @@ -22,8 +22,6 @@ import org.jetbrains.org.objectweb.asm.Opcodes enum class JvmTarget( override val description: String, val majorVersion: Int, - val descriptionForJavacArgument: String = description, - val isPreview: Boolean = false, ) : TargetPlatformVersion { JVM_1_6("1.6", Opcodes.V1_6), JVM_1_8("1.8", Opcodes.V1_8), @@ -34,22 +32,8 @@ enum class JvmTarget( JVM_13("13", Opcodes.V13), JVM_14("14", Opcodes.V14), JVM_15("15", Opcodes.V15), - JVM_15_PREVIEW( - "15_PREVIEW", Opcodes.V15, - descriptionForJavacArgument = "15", isPreview = true - ), ; - val minorVersion: Int = - if (isPreview) - 0xffff - else - 0 - - val bytecodeVersion: Int by lazy { - (minorVersion shl 16) + majorVersion - } - companion object { @JvmField val DEFAULT = JVM_1_6 @@ -57,14 +41,14 @@ enum class JvmTarget( @JvmStatic fun fromString(string: String) = values().find { it.description == string } - fun getDescription(bytecodeVersion: Int): String { - val platformDescription = values().find { it.bytecodeVersion == bytecodeVersion }?.description ?: when (bytecodeVersion) { + fun getDescription(majorVersion: Int): String { + val platformDescription = values().find { it.majorVersion == majorVersion }?.description ?: when (majorVersion) { Opcodes.V1_7 -> "1.7" else -> null } return if (platformDescription != null) "JVM target $platformDescription" - else "JVM bytecode version $bytecodeVersion" + else "JVM bytecode version $majorVersion" } } } diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index d4a0c34af7c..7d8bc2eaba4 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -22,6 +22,8 @@ where advanced options include: -Xir-do-not-clear-binding-context When using the IR backend, do not clear BindingContext between psi2ir and lowerings -Xemit-jvm-type-annotations Emit JVM type annotations in bytecode + -Xjvm-enable-preview Allow using features from Java language that are in preview phase. + Works as `--enable-preview` in Java. All class files are marked as preview-generated thus it won't be possible to use them in release environment -Xfriend-paths= Paths to output directories for friend modules (whose internals should be visible) -Xmultifile-parts-inherit Compile multifile classes as a hierarchy of parts and facade -Xir-check-local-names Check that names of local classes and anonymous objects are the same in the IR backend as in the old backend diff --git a/compiler/testData/cli/jvm/jvmRecordOk.args b/compiler/testData/cli/jvm/jvmRecordOk.args index b975df8aa47..8e284011c5e 100644 --- a/compiler/testData/cli/jvm/jvmRecordOk.args +++ b/compiler/testData/cli/jvm/jvmRecordOk.args @@ -8,5 +8,6 @@ $TEMP_DIR$ -jdk-home $JDK_15$ -XXLanguage:+JvmRecordSupport +-Xjvm-enable-preview -jvm-target -15_PREVIEW +15 diff --git a/compiler/testData/cli/jvm/jvmRecordOk.out b/compiler/testData/cli/jvm/jvmRecordOk.out index ade09bd2ec2..06702215887 100644 --- a/compiler/testData/cli/jvm/jvmRecordOk.out +++ b/compiler/testData/cli/jvm/jvmRecordOk.out @@ -7,4 +7,6 @@ This mode is not recommended for production use, as no stability/compatibility guarantees are given on compiler or generated code. Use it at your own risk! +warning: language version 1.5 is experimental, there are no backwards compatibility guarantees for new language and library features +info: using preview Java language features OK diff --git a/compiler/testData/cli/jvm/wrongJvmTargetVersion.out b/compiler/testData/cli/jvm/wrongJvmTargetVersion.out index a7a87090cff..7afaea0c1de 100644 --- a/compiler/testData/cli/jvm/wrongJvmTargetVersion.out +++ b/compiler/testData/cli/jvm/wrongJvmTargetVersion.out @@ -1,3 +1,3 @@ error: unknown JVM target version: 1.5 -Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 15_PREVIEW +Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15 COMPILATION_ERROR diff --git a/compiler/testData/codegen/java15/box/records/bytecodeShapeForJava.kt b/compiler/testData/codegen/java15/box/records/bytecodeShapeForJava.kt index 4d5ec2b2c74..ddbd77508f6 100644 --- a/compiler/testData/codegen/java15/box/records/bytecodeShapeForJava.kt +++ b/compiler/testData/codegen/java15/box/records/bytecodeShapeForJava.kt @@ -1,6 +1,7 @@ // !API_VERSION: 1.5 // !LANGUAGE: +JvmRecordSupport -// JVM_TARGET: 15_PREVIEW +// JVM_TARGET: 15 +// ENABLE_JVM_PREVIEW // FILE: JavaClass.java public class JavaClass { public static String box() { diff --git a/compiler/testData/codegen/java15/box/records/dataJvmRecord.kt b/compiler/testData/codegen/java15/box/records/dataJvmRecord.kt index e4e227fa580..4b3d26a5e03 100644 --- a/compiler/testData/codegen/java15/box/records/dataJvmRecord.kt +++ b/compiler/testData/codegen/java15/box/records/dataJvmRecord.kt @@ -1,6 +1,7 @@ // !API_VERSION: 1.5 // !LANGUAGE: +JvmRecordSupport -// JVM_TARGET: 15_PREVIEW +// JVM_TARGET: 15 +// ENABLE_JVM_PREVIEW @JvmRecord data class MyRec(val x: String, val y: R) diff --git a/compiler/testData/codegen/java15/box/records/recordDifferentPropertyOverride.kt b/compiler/testData/codegen/java15/box/records/recordDifferentPropertyOverride.kt index 894b37db7cf..56141cb269f 100644 --- a/compiler/testData/codegen/java15/box/records/recordDifferentPropertyOverride.kt +++ b/compiler/testData/codegen/java15/box/records/recordDifferentPropertyOverride.kt @@ -1,4 +1,5 @@ -// JVM_TARGET: 15_PREVIEW +// JVM_TARGET: 15 +// ENABLE_JVM_PREVIEW // FILE: MyRec.java public record MyRec(String name) implements KI { public String getName() { diff --git a/compiler/testData/codegen/java15/box/records/recordDifferentSyntheticProperty.kt b/compiler/testData/codegen/java15/box/records/recordDifferentSyntheticProperty.kt index 8b68537f4aa..ff3604c49ba 100644 --- a/compiler/testData/codegen/java15/box/records/recordDifferentSyntheticProperty.kt +++ b/compiler/testData/codegen/java15/box/records/recordDifferentSyntheticProperty.kt @@ -1,4 +1,5 @@ -// JVM_TARGET: 15_PREVIEW +// JVM_TARGET: 15 +// ENABLE_JVM_PREVIEW // FILE: MyRec.java public record MyRec(String name) { public String getName() { diff --git a/compiler/testData/codegen/java15/box/records/recordPropertyAccess.kt b/compiler/testData/codegen/java15/box/records/recordPropertyAccess.kt index 222fd6414ac..ef7fb26d562 100644 --- a/compiler/testData/codegen/java15/box/records/recordPropertyAccess.kt +++ b/compiler/testData/codegen/java15/box/records/recordPropertyAccess.kt @@ -1,5 +1,6 @@ // !LANGUAGE: +JvmRecordSupport -// JVM_TARGET: 15_PREVIEW +// JVM_TARGET: 15 +// ENABLE_JVM_PREVIEW // FILE: MyRec.java public record MyRec(String name) {} diff --git a/compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt b/compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt index 1a03e42f875..59f7770f952 100644 --- a/compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt +++ b/compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt @@ -1,6 +1,7 @@ // !API_VERSION: 1.5 // !LANGUAGE: +JvmRecordSupport -// JVM_TARGET: 15_PREVIEW +// JVM_TARGET: 15 +// ENABLE_JVM_PREVIEW // FILE: A.kt @JvmRecord data class MyRecord(val foo: String, val bar: String) diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt index e7805127728..4a3ad7319b7 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt @@ -25,9 +25,16 @@ abstract class AbstractBlackBoxAgainstJavaCodegenTest : AbstractBlackBoxCodegenT override fun doMultiFileTest(wholeFile: File, files: List) { javaClassesOutputDirectory = writeJavaFiles(files)!!.let { directory -> val jvmTargets = files.mapNotNullTo(mutableSetOf()) { it.directives["JVM_TARGET"]?.let((JvmTarget)::fromString) } + val enablePreview = files.any { it.directives.contains("ENABLE_JVM_PREVIEW") } check(jvmTargets.size <= 1) { "Having different JVM_TARGETs for different files is not supported in this test." } CodegenTestUtil.compileJava( - CodegenTestUtil.findJavaSourcesInDirectory(directory), emptyList(), extractJavacOptions(files, jvmTargets.firstOrNull()) + CodegenTestUtil.findJavaSourcesInDirectory(directory), + emptyList(), + extractJavacOptions( + files, + jvmTargets.firstOrNull(), + enablePreview, + ), ) } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java index 73d7b592784..bbe6985df8c 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java @@ -529,7 +529,11 @@ public abstract class CodegenTestCase extends KotlinBaseTest javacOptions = extractJavacOptions(files, configuration.get(JVMConfigurationKeys.JVM_TARGET)); + List javacOptions = extractJavacOptions( + files, + configuration.get(JVMConfigurationKeys.JVM_TARGET), + configuration.getBoolean(JVMConfigurationKeys.ENABLE_JVM_PREVIEW) + ); List finalJavacOptions = prepareJavacOptions(javaClasspath, javacOptions, javaClassesOutputDirectory); try { @@ -551,15 +555,19 @@ public abstract class CodegenTestCase extends KotlinBaseTest javaClasspath) {} @NotNull - protected static List extractJavacOptions(@NotNull List files, @Nullable JvmTarget kotlinTarget) { + protected static List extractJavacOptions( + @NotNull List files, + @Nullable JvmTarget kotlinTarget, + boolean isJvmPreviewEnabled + ) { List javacOptions = new ArrayList<>(0); for (TestFile file : files) { javacOptions.addAll(InTextDirectivesUtils.findListWithPrefixes(file.content, "// JAVAC_OPTIONS:")); } - if (kotlinTarget != null && kotlinTarget.isPreview()) { + if (kotlinTarget != null && isJvmPreviewEnabled) { javacOptions.add("--release"); - javacOptions.add(kotlinTarget.getDescriptionForJavacArgument()); + javacOptions.add(kotlinTarget.getDescription()); javacOptions.add("--enable-preview"); return javacOptions; } @@ -582,7 +590,7 @@ public abstract class CodegenTestCase extends KotlinBaseTest 0) - return kotlinTarget.getDescriptionForJavacArgument(); + return kotlinTarget.getDescription(); if (IS_SOURCE_6_STILL_SUPPORTED) return "1.6"; return null; diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt index 32198172ce9..01078eeefbd 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt @@ -227,6 +227,11 @@ abstract class KotlinBaseTest : KtUsefulTestCase() ?: error("Unknown target: $targetString") configuration.put(JVMConfigurationKeys.JVM_TARGET, jvmTarget) } + + if (directives.contains("ENABLE_JVM_PREVIEW")) { + configuration.put(JVMConfigurationKeys.ENABLE_JVM_PREVIEW, true) + } + val version = directives["LANGUAGE_VERSION"] if (version != null) { throw AssertionError(