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:
Alexander Udalov
2019-08-07 15:44:30 +02:00
parent e207c96336
commit b970a57adb
9 changed files with 82 additions and 17 deletions
@@ -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)
}
@@ -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)
}
}
@@ -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? {
@@ -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
) {
}
@@ -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
)
}
}
@@ -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? {