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,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
childSerializer: DescriptorSerializer) {
|
||||
super.serializeFunction(descriptor, proto, childSerializer)
|
||||
override fun serializeFunction(
|
||||
descriptor: FunctionDescriptor,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable?,
|
||||
childSerializer: DescriptorSerializer
|
||||
) {
|
||||
super.serializeFunction(descriptor, proto, versionRequirementTable, childSerializer)
|
||||
if (descriptor.visibility == JavaVisibilities.PACKAGE_VISIBILITY) {
|
||||
proto.setExtension(JavaClassProtoBuf.isPackagePrivateMethod, true)
|
||||
}
|
||||
|
||||
+30
-3
@@ -5,11 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.serialization
|
||||
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.createFreeFakeLocalPropertyDescriptor
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
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 moduleName = state.moduleName
|
||||
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 fun shouldUseTypeTable(): Boolean = useTypeTable
|
||||
@@ -165,7 +168,10 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
||||
}
|
||||
|
||||
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)
|
||||
if (method != null) {
|
||||
@@ -174,8 +180,25 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
||||
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(
|
||||
descriptor: PropertyDescriptor,
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
if (getter?.needsInlineParameterNullCheckRequirement() == true || setter?.needsInlineParameterNullCheckRequirement() == true) {
|
||||
versionRequirementTable?.writeInlineParameterNullCheckRequirement(proto::addVersionRequirement)
|
||||
}
|
||||
}
|
||||
|
||||
private fun PropertyDescriptor.isJvmFieldPropertyInInterfaceCompanion(): Boolean {
|
||||
@@ -329,6 +356,6 @@ class JvmSerializerExtension(private val bindings: JvmSerializationBindings, sta
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
override fun serializeFunction(descriptor: FunctionDescriptor,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
childSerializer: DescriptorSerializer
|
||||
override fun serializeFunction(
|
||||
descriptor: FunctionDescriptor,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable?,
|
||||
childSerializer: DescriptorSerializer
|
||||
) {
|
||||
uniqId(descriptor)?.let { proto.setExtension(JsKlibMetadataProtoBuf.functionUniqId, it) }
|
||||
val id = getFileId(descriptor)
|
||||
if (id != null) {
|
||||
proto.setExtension(JsKlibMetadataProtoBuf.functionContainingFileId, id)
|
||||
}
|
||||
super.serializeFunction(descriptor, proto, childSerializer)
|
||||
super.serializeFunction(descriptor, proto, versionRequirementTable, childSerializer)
|
||||
}
|
||||
|
||||
private fun getFileId(descriptor: DeclarationDescriptor): Int? {
|
||||
|
||||
+1
-1
@@ -353,7 +353,7 @@ class DescriptorSerializer private constructor(
|
||||
|
||||
contractSerializer.serializeContractOfFunctionIfAny(descriptor, builder, this)
|
||||
|
||||
extension.serializeFunction(descriptor, builder, local)
|
||||
extension.serializeFunction(descriptor, builder, versionRequirementTable, local)
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ abstract class SerializerExtension {
|
||||
open fun serializeFunction(
|
||||
descriptor: FunctionDescriptor,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable?,
|
||||
childSerializer: DescriptorSerializer
|
||||
) {
|
||||
}
|
||||
|
||||
+1
@@ -55,6 +55,7 @@ abstract class KotlinSerializerExtensionBase(private val protocol: SerializerExt
|
||||
override fun serializeFunction(
|
||||
descriptor: FunctionDescriptor,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable?,
|
||||
childSerializer: DescriptorSerializer
|
||||
) {
|
||||
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(
|
||||
languageVersion,
|
||||
ApiVersion.createByLanguageVersion(languageVersion),
|
||||
mapOf(JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ENABLE),
|
||||
mapOf(
|
||||
JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ENABLE,
|
||||
AnalysisFlags.explicitApiVersion to true
|
||||
),
|
||||
emptyMap()
|
||||
)
|
||||
},
|
||||
@@ -73,4 +76,16 @@ class JvmVersionRequirementTest : AbstractVersionRequirementTest() {
|
||||
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)
|
||||
}
|
||||
|
||||
override fun serializeFunction(descriptor: FunctionDescriptor,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
childSerializer: DescriptorSerializer) {
|
||||
override fun serializeFunction(
|
||||
descriptor: FunctionDescriptor,
|
||||
proto: ProtoBuf.Function.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable?,
|
||||
childSerializer: DescriptorSerializer
|
||||
) {
|
||||
val id = getFileId(descriptor)
|
||||
if (id != null) {
|
||||
proto.setExtension(JsProtoBuf.functionContainingFileId, id)
|
||||
}
|
||||
super.serializeFunction(descriptor, proto, childSerializer)
|
||||
super.serializeFunction(descriptor, proto, versionRequirementTable, childSerializer)
|
||||
}
|
||||
|
||||
private fun getFileId(descriptor: DeclarationDescriptor): Int? {
|
||||
|
||||
Reference in New Issue
Block a user