Do not rely on descriptors in KTypeParameterImpl.equals/hashCode

Descriptors are cached via weak references in moduleByClassLoader.kt and
can be garbage-collected at any point. So relying on identity of
descriptors in KTypeParameterImpl is dangerous because the same type
parameter can be represented by different descriptors. For example, the
test equalsOnFunctionParameters.kt was flaky before this change because
of this issue, and that could be reproduced by running it a few hundred
times in the same process.

Instead, use the type parameter's container (which is either KClass or
KCallable) and name, in equals/hashCode. KClass and KCallable already
have equals/hashCode independent of descriptors, so this works in case
the descriptor is invalidated.
This commit is contained in:
Alexander Udalov
2020-05-27 15:54:06 +02:00
parent d25f9dee62
commit 55f384cb04
14 changed files with 279 additions and 53 deletions
@@ -26192,6 +26192,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsHashCode.kt");
}
@TestMetadata("typeParametersEqualsWithClearCaches.kt")
public void testTypeParametersEqualsWithClearCaches() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsWithClearCaches.kt");
}
@TestMetadata("typeParametersToString.kt")
public void testTypeParametersToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersToString.kt");
@@ -27078,6 +27083,16 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reflection/typeParameters/declarationSiteVariance.kt");
}
@TestMetadata("innerGenericParameter.kt")
public void testInnerGenericParameter() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/innerGenericParameter.kt");
}
@TestMetadata("javaGenericTypeConstructor.kt")
public void testJavaGenericTypeConstructor() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/javaGenericTypeConstructor.kt");
}
@TestMetadata("typeParametersAndNames.kt")
public void testTypeParametersAndNames() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/typeParametersAndNames.kt");
@@ -0,0 +1,56 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FILE: box.kt
import kotlin.test.assertEquals
inline fun check(message: String, generate: () -> Any?) {
val x1: Any?
val x2: Any?
try {
x1 = generate()
// Force clear the internal maps, as if the weak values in them are garbage-collected.
kotlin.reflect.jvm.internal.ReflectionFactoryImpl.clearCaches()
x2 = generate()
} catch (e: Throwable) {
throw AssertionError("Fail $message", e)
}
assertEquals(x1, x2, "Fail equals $message")
assertEquals(x2, x1, "Fail equals $message")
assertEquals(x1.hashCode(), x2.hashCode(), "Fail hashCode $message")
}
class C<T> {
fun <V> v(): V? = null
fun t(): T? = null
val <U> U.u: U get() = this
}
fun <W> W.w() {}
val <X> X.x: X get() = this
fun box(): String {
check("T from C's typeParameters") { C::class.typeParameters.single() }
check("V from v's typeParameters") { C::class.members.single { it.name == "v" }.typeParameters.single() }
check("V from v's returnType") { C::class.members.single { it.name == "v" }.returnType.classifier }
check("T from t's returnType") { C::class.members.single { it.name == "t" }.returnType.classifier }
check("U from u's parameter type") { C::class.members.single { it.name == "u" }.parameters[1].type.classifier }
check("W from w's parameter type") { Any::w.parameters.single().type.classifier }
check("X from x's parameter type") { Any::x.parameters.single().type.classifier }
check("Z from J's typeParameters") { J::class.typeParameters.single() }
check("Z from z's returnType") { J::class.members.single { it.name == "z" }.returnType.classifier }
return "OK"
}
// FILE: J.java
public interface J<Z> {
Z z();
}
@@ -0,0 +1,28 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.reflect.KVariance
import kotlin.test.assertEquals
class A<out T> {
inner class B<in U> {
fun test(u: U): T? = null
}
}
fun box(): String {
val fn = A.B::class.members.single { it.name == "test" }
val t = A::class.typeParameters.single()
val u = A.B::class.typeParameters.single()
assertEquals("T", t.name)
assertEquals(KVariance.OUT, t.variance)
assertEquals("U", u.name)
assertEquals(KVariance.IN, u.variance)
assertEquals(t, fn.returnType.classifier)
assertEquals(u, fn.parameters[1].type.classifier)
return "OK"
}
@@ -0,0 +1,30 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FILE: test.kt
import kotlin.reflect.KVariance
import kotlin.test.assertEquals
fun box(): String {
val ctor = J::class.constructors.single()
val ab = ctor.typeParameters
assertEquals(2, ab.size, ab.toString())
assertEquals("A", ab[0].name)
assertEquals(KVariance.INVARIANT, ab[0].variance)
assertEquals("B", ab[1].name)
assertEquals(KVariance.INVARIANT, ab[1].variance)
// TODO: currently fails with "AssertionError: Expected <A>, actual <A>"
// assertEquals(ab[0], ctor.parameters[0].type.classifier)
assertEquals(ab[1], ctor.parameters[1].type.classifier)
return "OK"
}
// FILE: J.java
public class J<A> {
public <B> J(A a, B b) {}
}
@@ -27778,6 +27778,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsHashCode.kt");
}
@TestMetadata("typeParametersEqualsWithClearCaches.kt")
public void testTypeParametersEqualsWithClearCaches() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsWithClearCaches.kt");
}
@TestMetadata("typeParametersToString.kt")
public void testTypeParametersToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersToString.kt");
@@ -28664,6 +28669,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/typeParameters/declarationSiteVariance.kt");
}
@TestMetadata("innerGenericParameter.kt")
public void testInnerGenericParameter() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/innerGenericParameter.kt");
}
@TestMetadata("javaGenericTypeConstructor.kt")
public void testJavaGenericTypeConstructor() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/javaGenericTypeConstructor.kt");
}
@TestMetadata("typeParametersAndNames.kt")
public void testTypeParametersAndNames() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/typeParametersAndNames.kt");
@@ -25412,6 +25412,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsHashCode.kt");
}
@TestMetadata("typeParametersEqualsWithClearCaches.kt")
public void testTypeParametersEqualsWithClearCaches() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsWithClearCaches.kt");
}
@TestMetadata("typeParametersToString.kt")
public void testTypeParametersToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersToString.kt");
@@ -26298,6 +26303,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/typeParameters/declarationSiteVariance.kt");
}
@TestMetadata("innerGenericParameter.kt")
public void testInnerGenericParameter() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/innerGenericParameter.kt");
}
@TestMetadata("javaGenericTypeConstructor.kt")
public void testJavaGenericTypeConstructor() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/javaGenericTypeConstructor.kt");
}
@TestMetadata("typeParametersAndNames.kt")
public void testTypeParametersAndNames() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/typeParametersAndNames.kt");
@@ -26192,6 +26192,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsHashCode.kt");
}
@TestMetadata("typeParametersEqualsWithClearCaches.kt")
public void testTypeParametersEqualsWithClearCaches() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsWithClearCaches.kt");
}
@TestMetadata("typeParametersToString.kt")
public void testTypeParametersToString() throws Exception {
runTest("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersToString.kt");
@@ -27078,6 +27083,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/typeParameters/declarationSiteVariance.kt");
}
@TestMetadata("innerGenericParameter.kt")
public void testInnerGenericParameter() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/innerGenericParameter.kt");
}
@TestMetadata("javaGenericTypeConstructor.kt")
public void testJavaGenericTypeConstructor() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/javaGenericTypeConstructor.kt");
}
@TestMetadata("typeParametersAndNames.kt")
public void testTypeParametersAndNames() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeParameters/typeParametersAndNames.kt");
@@ -20,7 +20,7 @@ import kotlin.reflect.jvm.internal.calls.Caller
import kotlin.reflect.jvm.javaType
import kotlin.reflect.jvm.jvmErasure
internal abstract class KCallableImpl<out R> : KCallable<R> {
internal abstract class KCallableImpl<out R> : KCallable<R>, KTypeParameterOwnerImpl {
abstract val descriptor: CallableMemberDescriptor
// The instance which is used to perform a positional call, i.e. `call`
@@ -82,7 +82,7 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
get() = _returnType()
private val _typeParameters = ReflectProperties.lazySoft {
descriptor.typeParameters.map(::KTypeParameterImpl)
descriptor.typeParameters.map { descriptor -> KTypeParameterImpl(this, descriptor) }
}
override val typeParameters: List<KTypeParameter>
@@ -40,7 +40,9 @@ import org.jetbrains.kotlin.descriptors.runtime.components.ReflectKotlinClass
import org.jetbrains.kotlin.descriptors.runtime.structure.functionClassArity
import org.jetbrains.kotlin.descriptors.runtime.structure.wrapperByPrimitive
internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclarationContainerImpl(), KClass<T>, KClassifierImpl {
internal class KClassImpl<T : Any>(
override val jClass: Class<T>
) : KDeclarationContainerImpl(), KClass<T>, KClassifierImpl, KTypeParameterOwnerImpl {
inner class Data : KDeclarationContainerImpl.Data() {
val descriptor: ClassDescriptor by ReflectProperties.lazySoft {
val classId = classId
@@ -115,7 +117,7 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
}
val typeParameters: List<KTypeParameter> by ReflectProperties.lazySoft {
descriptor.declaredTypeParameters.map(::KTypeParameterImpl)
descriptor.declaredTypeParameters.map { descriptor -> KTypeParameterImpl(this@KClassImpl, descriptor) }
}
val supertypes: List<KType> by ReflectProperties.lazySoft {
@@ -17,7 +17,11 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
import org.jetbrains.kotlin.descriptors.runtime.components.RuntimeModuleData
import org.jetbrains.kotlin.descriptors.runtime.components.tryLoadClass
import org.jetbrains.kotlin.descriptors.runtime.structure.createArrayType
import org.jetbrains.kotlin.descriptors.runtime.structure.safeClassLoader
import org.jetbrains.kotlin.descriptors.runtime.structure.wrapperByPrimitive
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.renderer.DescriptorRenderer
@@ -25,11 +29,6 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import kotlin.jvm.internal.ClassBasedDeclarationContainer
import org.jetbrains.kotlin.descriptors.runtime.components.RuntimeModuleData
import org.jetbrains.kotlin.descriptors.runtime.components.tryLoadClass
import org.jetbrains.kotlin.descriptors.runtime.structure.createArrayType
import org.jetbrains.kotlin.descriptors.runtime.structure.safeClassLoader
import org.jetbrains.kotlin.descriptors.runtime.structure.wrapperByPrimitive
internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
abstract inner class Data {
@@ -51,17 +50,10 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
abstract fun getLocalProperty(index: Int): PropertyDescriptor?
protected fun getMembers(scope: MemberScope, belonginess: MemberBelonginess): Collection<KCallableImpl<*>> {
val visitor = object : DeclarationDescriptorVisitorEmptyBodies<KCallableImpl<*>, Unit>() {
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Unit): KCallableImpl<*> =
createProperty(descriptor)
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Unit): KCallableImpl<*> =
KFunctionImpl(this@KDeclarationContainerImpl, descriptor)
val visitor = object : CreateKCallableVisitor(this) {
override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Unit): KCallableImpl<*> =
throw IllegalStateException("No constructors should appear in this scope: $descriptor")
throw IllegalStateException("No constructors should appear here: $descriptor")
}
return scope.getContributedDescriptors().mapNotNull { descriptor ->
if (descriptor is CallableMemberDescriptor &&
descriptor.visibility != Visibilities.INVISIBLE_FAKE &&
@@ -78,26 +70,6 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
member.kind.isReal == (this == DECLARED)
}
private fun createProperty(descriptor: PropertyDescriptor): KPropertyImpl<*> {
val receiverCount = (descriptor.dispatchReceiverParameter?.let { 1 } ?: 0) +
(descriptor.extensionReceiverParameter?.let { 1 } ?: 0)
when {
descriptor.isVar -> when (receiverCount) {
0 -> return KMutableProperty0Impl<Any?>(this, descriptor)
1 -> return KMutableProperty1Impl<Any?, Any?>(this, descriptor)
2 -> return KMutableProperty2Impl<Any?, Any?, Any?>(this, descriptor)
}
else -> when (receiverCount) {
0 -> return KProperty0Impl<Any?>(this, descriptor)
1 -> return KProperty1Impl<Any?, Any?>(this, descriptor)
2 -> return KProperty2Impl<Any?, Any?, Any?>(this, descriptor)
}
}
throw KotlinReflectionInternalError("Unsupported property: $descriptor")
}
fun findPropertyDescriptor(name: String, signature: String): PropertyDescriptor {
val match = LOCAL_PROPERTY_SIGNATURE.matchEntire(signature)
if (match != null) {
@@ -63,7 +63,7 @@ internal class KTypeImpl(
return KClassImpl(jClass)
}
is TypeParameterDescriptor -> return KTypeParameterImpl(descriptor)
is TypeParameterDescriptor -> return KTypeParameterImpl(null, descriptor)
is TypeAliasDescriptor -> TODO("Type alias classifiers are not yet supported")
else -> return null
}
@@ -16,14 +16,23 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.runtime.components.ReflectKotlinClass
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.jvm.internal.TypeParameterReference
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
import kotlin.reflect.KVariance
internal class KTypeParameterImpl(override val descriptor: TypeParameterDescriptor) : KTypeParameter, KClassifierImpl {
internal class KTypeParameterImpl(
container: KTypeParameterOwnerImpl?,
override val descriptor: TypeParameterDescriptor,
) : KTypeParameter, KClassifierImpl {
override val name: String
get() = descriptor.name.asString()
@@ -45,11 +54,41 @@ internal class KTypeParameterImpl(override val descriptor: TypeParameterDescript
override val isReified: Boolean
get() = descriptor.isReified
private val container: KTypeParameterOwnerImpl = container ?: run {
when (val declaration = descriptor.containingDeclaration) {
is ClassDescriptor -> {
declaration.toKClassImpl()
}
is CallableMemberDescriptor -> {
val callableContainerClass = when (val callableContainer = declaration.containingDeclaration) {
is ClassDescriptor -> {
callableContainer.toKClassImpl()
}
else -> {
val deserializedMember = declaration as? DeserializedMemberDescriptor
?: throw KotlinReflectionInternalError("Non-class callable descriptor must be deserialized: $declaration")
deserializedMember.getContainerClass().kotlin as KClassImpl<*>
}
}
declaration.accept(CreateKCallableVisitor(callableContainerClass), Unit)
}
else -> throw KotlinReflectionInternalError("Unknown type parameter container: $declaration")
}
}
private fun ClassDescriptor.toKClassImpl(): KClassImpl<*> =
toJavaClass()?.kotlin as KClassImpl<*>?
?: throw KotlinReflectionInternalError("Type parameter container is not resolved: $containingDeclaration")
private fun DeserializedMemberDescriptor.getContainerClass(): Class<*> =
containerSource.safeAs<JvmPackagePartSource>()?.knownJvmBinaryClass.safeAs<ReflectKotlinClass>()?.klass
?: throw KotlinReflectionInternalError("Container of deserialized member is not resolved: $this")
override fun equals(other: Any?) =
other is KTypeParameterImpl && descriptor == other.descriptor
other is KTypeParameterImpl && container == other.container && name == other.name
override fun hashCode() =
descriptor.hashCode()
container.hashCode() * 31 + name.hashCode()
override fun toString() =
TypeParameterReference.toString(this)
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.reflect.jvm.internal
import kotlin.reflect.KTypeParameter
interface KTypeParameterOwnerImpl {
val typeParameters: List<KTypeParameter>
}
@@ -20,6 +20,14 @@ import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
import org.jetbrains.kotlin.descriptors.runtime.components.ReflectAnnotationSource
import org.jetbrains.kotlin.descriptors.runtime.components.ReflectKotlinClass
import org.jetbrains.kotlin.descriptors.runtime.components.RuntimeSourceElementFactory
import org.jetbrains.kotlin.descriptors.runtime.components.tryLoadClass
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaAnnotation
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaClass
import org.jetbrains.kotlin.descriptors.runtime.structure.safeClassLoader
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
@@ -32,23 +40,16 @@ import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
import java.lang.reflect.Type
import kotlin.jvm.internal.FunctionReference
import kotlin.jvm.internal.PropertyReference
import kotlin.reflect.KType
import kotlin.reflect.KVisibility
import kotlin.reflect.full.IllegalCallableAccessException
import kotlin.reflect.jvm.internal.calls.createAnnotationInstance
import org.jetbrains.kotlin.descriptors.runtime.components.ReflectAnnotationSource
import org.jetbrains.kotlin.descriptors.runtime.components.ReflectKotlinClass
import org.jetbrains.kotlin.descriptors.runtime.components.RuntimeSourceElementFactory
import org.jetbrains.kotlin.descriptors.runtime.components.tryLoadClass
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaAnnotation
import org.jetbrains.kotlin.descriptors.runtime.structure.ReflectJavaClass
import org.jetbrains.kotlin.descriptors.runtime.structure.safeClassLoader
import org.jetbrains.kotlin.resolve.isInlineClassType
import java.lang.reflect.Type
import kotlin.reflect.KType
internal val JVM_STATIC = FqName("kotlin.jvm.JvmStatic")
@@ -217,3 +218,29 @@ internal fun defaultPrimitiveValue(type: Type): Any? =
else -> throw UnsupportedOperationException("Unknown primitive: $type")
}
} else null
internal open class CreateKCallableVisitor(private val container: KDeclarationContainerImpl) :
DeclarationDescriptorVisitorEmptyBodies<KCallableImpl<*>, Unit>() {
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Unit): KCallableImpl<*> {
val receiverCount = (descriptor.dispatchReceiverParameter?.let { 1 } ?: 0) +
(descriptor.extensionReceiverParameter?.let { 1 } ?: 0)
when {
descriptor.isVar -> when (receiverCount) {
0 -> return KMutableProperty0Impl<Any?>(container, descriptor)
1 -> return KMutableProperty1Impl<Any?, Any?>(container, descriptor)
2 -> return KMutableProperty2Impl<Any?, Any?, Any?>(container, descriptor)
}
else -> when (receiverCount) {
0 -> return KProperty0Impl<Any?>(container, descriptor)
1 -> return KProperty1Impl<Any?, Any?>(container, descriptor)
2 -> return KProperty2Impl<Any?, Any?, Any?>(container, descriptor)
}
}
throw KotlinReflectionInternalError("Unsupported property: $descriptor")
}
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Unit): KCallableImpl<*> =
KFunctionImpl(container, descriptor)
}