Generate version requirement on inline functions since API version 1.4
The old compiler will crash if it tries to inline a function that's passing a lambda parameter into the new parameter null check method `Intrinsics.checkNotNullParameter` because that usage is not considered as inlinable by the old compiler (it only knows about `Intrinsics.checkParameterIsNotNull`). Therefore we require that these functions can only be read by compilers of version 1.3.50 or greater. #KT-22275 Fixed
This commit is contained in:
@@ -55,10 +55,13 @@ class JavaClassesSerializerExtension : KotlinSerializerExtensionBase(BuiltInSeri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeFunction(descriptor: FunctionDescriptor,
|
override fun serializeFunction(
|
||||||
proto: ProtoBuf.Function.Builder,
|
descriptor: FunctionDescriptor,
|
||||||
childSerializer: DescriptorSerializer) {
|
proto: ProtoBuf.Function.Builder,
|
||||||
super.serializeFunction(descriptor, proto, childSerializer)
|
versionRequirementTable: MutableVersionRequirementTable?,
|
||||||
|
childSerializer: DescriptorSerializer
|
||||||
|
) {
|
||||||
|
super.serializeFunction(descriptor, proto, versionRequirementTable, childSerializer)
|
||||||
if (descriptor.visibility == JavaVisibilities.PACKAGE_VISIBILITY) {
|
if (descriptor.visibility == JavaVisibilities.PACKAGE_VISIBILITY) {
|
||||||
proto.setExtension(JavaClassProtoBuf.isPackagePrivateMethod, true)
|
proto.setExtension(JavaClassProtoBuf.isPackagePrivateMethod, true)
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-3
@@ -5,11 +5,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.serialization
|
package org.jetbrains.kotlin.codegen.serialization
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
import org.jetbrains.kotlin.codegen.createFreeFakeLocalPropertyDescriptor
|
import org.jetbrains.kotlin.codegen.createFreeFakeLocalPropertyDescriptor
|
||||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*
|
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.config.ApiVersion
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
@@ -45,7 +47,8 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
|||||||
private val useTypeTable = state.useTypeTableInSerializer
|
private val useTypeTable = state.useTypeTableInSerializer
|
||||||
private val moduleName = state.moduleName
|
private val moduleName = state.moduleName
|
||||||
private val classBuilderMode = state.classBuilderMode
|
private val classBuilderMode = state.classBuilderMode
|
||||||
private val isReleaseCoroutines = state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
private val languageVersionSettings = state.languageVersionSettings
|
||||||
|
private val isParamAssertionsDisabled = state.isParamAssertionsDisabled
|
||||||
override val metadataVersion = state.metadataVersion
|
override val metadataVersion = state.metadataVersion
|
||||||
|
|
||||||
override fun shouldUseTypeTable(): Boolean = useTypeTable
|
override fun shouldUseTypeTable(): Boolean = useTypeTable
|
||||||
@@ -165,7 +168,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeFunction(
|
override fun serializeFunction(
|
||||||
descriptor: FunctionDescriptor, proto: ProtoBuf.Function.Builder, childSerializer: DescriptorSerializer
|
descriptor: FunctionDescriptor,
|
||||||
|
proto: ProtoBuf.Function.Builder,
|
||||||
|
versionRequirementTable: MutableVersionRequirementTable?,
|
||||||
|
childSerializer: DescriptorSerializer
|
||||||
) {
|
) {
|
||||||
val method = getBinding(METHOD_FOR_FUNCTION, descriptor)
|
val method = getBinding(METHOD_FOR_FUNCTION, descriptor)
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
@@ -174,8 +180,25 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
|||||||
proto.setExtension(JvmProtoBuf.methodSignature, signature)
|
proto.setExtension(JvmProtoBuf.methodSignature, signature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor.needsInlineParameterNullCheckRequirement()) {
|
||||||
|
versionRequirementTable?.writeInlineParameterNullCheckRequirement(proto::addVersionRequirement)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MutableVersionRequirementTable.writeInlineParameterNullCheckRequirement(add: (Int) -> Unit) {
|
||||||
|
if (languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4) {
|
||||||
|
// Since Kotlin 1.4, we generate a call to Intrinsics.checkNotNullParameter in inline functions which causes older compilers
|
||||||
|
// (earlier than 1.3.50) to crash because a functional parameter in this position can't be inlined
|
||||||
|
add(writeVersionRequirement(1, 3, 50, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FunctionDescriptor.needsInlineParameterNullCheckRequirement(): Boolean =
|
||||||
|
isInline && !isSuspend && !isParamAssertionsDisabled &&
|
||||||
|
!Visibilities.isPrivate(visibility) &&
|
||||||
|
(valueParameters.any { it.type.isFunctionType } || extensionReceiverParameter?.type?.isFunctionType == true)
|
||||||
|
|
||||||
override fun serializeProperty(
|
override fun serializeProperty(
|
||||||
descriptor: PropertyDescriptor,
|
descriptor: PropertyDescriptor,
|
||||||
proto: ProtoBuf.Property.Builder,
|
proto: ProtoBuf.Property.Builder,
|
||||||
@@ -212,6 +235,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
|||||||
writeVersionRequirement(1, 2, 70, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable)
|
writeVersionRequirement(1, 2, 70, ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION, versionRequirementTable)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getter?.needsInlineParameterNullCheckRequirement() == true || setter?.needsInlineParameterNullCheckRequirement() == true) {
|
||||||
|
versionRequirementTable?.writeInlineParameterNullCheckRequirement(proto::addVersionRequirement)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PropertyDescriptor.isJvmFieldPropertyInInterfaceCompanion(): Boolean {
|
private fun PropertyDescriptor.isJvmFieldPropertyInInterfaceCompanion(): Boolean {
|
||||||
@@ -329,6 +356,6 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun releaseCoroutines(): Boolean {
|
override fun releaseCoroutines(): Boolean {
|
||||||
return isReleaseCoroutines
|
return languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -91,16 +91,18 @@ class JsKlibMetadataSerializerExtension(
|
|||||||
super.serializeProperty(descriptor, proto, versionRequirementTable, childSerializer)
|
super.serializeProperty(descriptor, proto, versionRequirementTable, childSerializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeFunction(descriptor: FunctionDescriptor,
|
override fun serializeFunction(
|
||||||
proto: ProtoBuf.Function.Builder,
|
descriptor: FunctionDescriptor,
|
||||||
childSerializer: DescriptorSerializer
|
proto: ProtoBuf.Function.Builder,
|
||||||
|
versionRequirementTable: MutableVersionRequirementTable?,
|
||||||
|
childSerializer: DescriptorSerializer
|
||||||
) {
|
) {
|
||||||
uniqId(descriptor)?.let { proto.setExtension(JsKlibMetadataProtoBuf.functionUniqId, it) }
|
uniqId(descriptor)?.let { proto.setExtension(JsKlibMetadataProtoBuf.functionUniqId, it) }
|
||||||
val id = getFileId(descriptor)
|
val id = getFileId(descriptor)
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
proto.setExtension(JsKlibMetadataProtoBuf.functionContainingFileId, id)
|
proto.setExtension(JsKlibMetadataProtoBuf.functionContainingFileId, id)
|
||||||
}
|
}
|
||||||
super.serializeFunction(descriptor, proto, childSerializer)
|
super.serializeFunction(descriptor, proto, versionRequirementTable, childSerializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFileId(descriptor: DeclarationDescriptor): Int? {
|
private fun getFileId(descriptor: DeclarationDescriptor): Int? {
|
||||||
|
|||||||
+1
-1
@@ -353,7 +353,7 @@ class DescriptorSerializer private constructor(
|
|||||||
|
|
||||||
contractSerializer.serializeContractOfFunctionIfAny(descriptor, builder, this)
|
contractSerializer.serializeContractOfFunctionIfAny(descriptor, builder, this)
|
||||||
|
|
||||||
extension.serializeFunction(descriptor, builder, local)
|
extension.serializeFunction(descriptor, builder, versionRequirementTable, local)
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ abstract class SerializerExtension {
|
|||||||
open fun serializeFunction(
|
open fun serializeFunction(
|
||||||
descriptor: FunctionDescriptor,
|
descriptor: FunctionDescriptor,
|
||||||
proto: ProtoBuf.Function.Builder,
|
proto: ProtoBuf.Function.Builder,
|
||||||
|
versionRequirementTable: MutableVersionRequirementTable?,
|
||||||
childSerializer: DescriptorSerializer
|
childSerializer: DescriptorSerializer
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -55,6 +55,7 @@ abstract class KotlinSerializerExtensionBase(private val protocol: SerializerExt
|
|||||||
override fun serializeFunction(
|
override fun serializeFunction(
|
||||||
descriptor: FunctionDescriptor,
|
descriptor: FunctionDescriptor,
|
||||||
proto: ProtoBuf.Function.Builder,
|
proto: ProtoBuf.Function.Builder,
|
||||||
|
versionRequirementTable: MutableVersionRequirementTable?,
|
||||||
childSerializer: DescriptorSerializer
|
childSerializer: DescriptorSerializer
|
||||||
) {
|
) {
|
||||||
for (annotation in descriptor.nonSourceAnnotations) {
|
for (annotation in descriptor.nonSourceAnnotations) {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
inline fun doRun(f: () -> Unit) {}
|
||||||
|
|
||||||
|
// Note that although lambdas are not inlined in property accessors (neither in setter parameter, nor in extension receiver parameter),
|
||||||
|
// we still generate version requirements, just in case we support inlining here in the future.
|
||||||
|
inline var lambdaVarProperty: () -> Unit
|
||||||
|
get() = {}
|
||||||
|
set(value) { value() }
|
||||||
|
|
||||||
|
inline var (() -> String).extensionProperty: String
|
||||||
|
get() = this()
|
||||||
|
set(value) { this() }
|
||||||
@@ -40,7 +40,10 @@ class JvmVersionRequirementTest : AbstractVersionRequirementTest() {
|
|||||||
languageVersionSettings = LanguageVersionSettingsImpl(
|
languageVersionSettings = LanguageVersionSettingsImpl(
|
||||||
languageVersion,
|
languageVersion,
|
||||||
ApiVersion.createByLanguageVersion(languageVersion),
|
ApiVersion.createByLanguageVersion(languageVersion),
|
||||||
mapOf(JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ENABLE),
|
mapOf(
|
||||||
|
JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ENABLE,
|
||||||
|
AnalysisFlags.explicitApiVersion to true
|
||||||
|
),
|
||||||
emptyMap()
|
emptyMap()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -73,4 +76,16 @@ class JvmVersionRequirementTest : AbstractVersionRequirementTest() {
|
|||||||
fqNames = listOf("test.Base.Companion.foo")
|
fqNames = listOf("test.Base.Companion.foo")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testInlineParameterNullCheck() {
|
||||||
|
doTest(
|
||||||
|
VersionRequirement.Version(1, 3, 50), DeprecationLevel.ERROR, null, COMPILER_VERSION, null,
|
||||||
|
fqNames = listOf(
|
||||||
|
"test.doRun",
|
||||||
|
"test.lambdaVarProperty",
|
||||||
|
"test.extensionProperty"
|
||||||
|
),
|
||||||
|
customLanguageVersion = LanguageVersion.KOTLIN_1_4
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -67,14 +67,17 @@ class KotlinJavascriptSerializerExtension(
|
|||||||
super.serializeProperty(descriptor, proto, versionRequirementTable, childSerializer)
|
super.serializeProperty(descriptor, proto, versionRequirementTable, childSerializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeFunction(descriptor: FunctionDescriptor,
|
override fun serializeFunction(
|
||||||
proto: ProtoBuf.Function.Builder,
|
descriptor: FunctionDescriptor,
|
||||||
childSerializer: DescriptorSerializer) {
|
proto: ProtoBuf.Function.Builder,
|
||||||
|
versionRequirementTable: MutableVersionRequirementTable?,
|
||||||
|
childSerializer: DescriptorSerializer
|
||||||
|
) {
|
||||||
val id = getFileId(descriptor)
|
val id = getFileId(descriptor)
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
proto.setExtension(JsProtoBuf.functionContainingFileId, id)
|
proto.setExtension(JsProtoBuf.functionContainingFileId, id)
|
||||||
}
|
}
|
||||||
super.serializeFunction(descriptor, proto, childSerializer)
|
super.serializeFunction(descriptor, proto, versionRequirementTable, childSerializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFileId(descriptor: DeclarationDescriptor): Int? {
|
private fun getFileId(descriptor: DeclarationDescriptor): Int? {
|
||||||
|
|||||||
Reference in New Issue
Block a user