Skip to content

Commit 655fb78

Browse files
udalovSpace Team
authored andcommitted
Reflection: always compute JVM signature of built-in functions manually
This fixes "KotlinReflectionInternalError: Unknown origin" when computing JVM signatures of functions which have no origin (neither Java element, nor `@kotlin.Metadata`, nor `.kotlin_builtins`). Besides `Enum.values/valueOf` and `Cloneable.clone` handled previously, this also includes `invoke` of function types, which are synthesized by the compiler during compilation. The idea of this code was probably to make sure that all known cases of functions without origin would be listed in the `isKnownBuiltInFunction` function, because some built-ins might have a different JVM signature than what is computed by the naive algorithm. However, it seems safer to try to compute the JVM signature anyway than failing early. The code in KT-83608 started to fail in 2.3.0 because of the bug fix made in KT-79206. Previously, classifier for suspend function types was incorrectly null, and it did not occur to me that some code might've relied on this wrong behavior. RCA: see above. #KT-42199 Fixed #KT-83608 Fixed (cherry picked from commit 9e2049c)
1 parent 58d3875 commit 655fb78

25 files changed

Lines changed: 149 additions & 16 deletions

File tree

analysis/low-level-api-fir/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLBlackBoxTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

analysis/low-level-api-fir/tests-gen/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLReversedBlackBoxTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// TARGET_BACKEND: JVM
2+
// WITH_REFLECT
3+
4+
import kotlin.reflect.KClass
5+
import kotlin.test.assertEquals
6+
import kotlin.test.assertNull
7+
8+
fun interface FunInterface : () -> Int
9+
fun suspendFunction(): suspend () -> Int = null!!
10+
11+
private fun check(expectedInvoke: String, klass: KClass<*>) {
12+
val members = klass.members
13+
assertEquals(setOf("equals", "hashCode", "toString", "invoke"), members.map { it.name }.toSet())
14+
assertEquals(expectedInvoke, members.single { it.name == "invoke" }.toString())
15+
}
16+
17+
fun box(): String {
18+
check("fun () -> R.invoke(): R", Function0::class)
19+
check("fun (P1) -> R.invoke(P1): R", Function1::class)
20+
check("fun FunInterface.invoke(): kotlin.Int", FunInterface::class)
21+
22+
val suspendFun = ::suspendFunction.returnType.classifier
23+
check("fun (P1) -> R.invoke(P1): R", suspendFun as KClass<*>)
24+
return "OK"
25+
}

compiler/testData/ir/irText/expressions/funInterface/noConversionForSubtype.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ fun createNewPlugin(): SimpleRibCoroutineWorker {
2727
fun main() {
2828
foo(FooWorker())
2929
bar(FooWorker())
30-
}
30+
}

compiler/testData/ir/irText/expressions/funInterface/noConversionForSubtypeGeneric.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ fun createNewPlugin(): SimpleRibCoroutineWorker<MyInterface> {
2727
fun main() {
2828
foo<MyInterface>(FooWorker())
2929
bar<MyInterface>(FooWorker())
30-
}
30+
}

core/reflection.jvm/src/kotlin/reflect/jvm/internal/Builtins.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ internal fun createFunctionKmClass(arity: Int): KmClass = KmClass().apply {
2626
}))
2727
})
2828

29-
// TODO (KT-80710): `invoke` function (note that even though it's created in the old reflection, it causes a failure KT-42199.)
29+
// TODO (KT-80710): `invoke` function.
3030
}

core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,7 @@ internal object RuntimeTypeMapper {
219219
}
220220
}
221221

222-
if (isKnownBuiltInFunction(function)) {
223-
return mapJvmFunctionSignature(function)
224-
}
225-
226-
throw KotlinReflectionInternalError("Unknown origin of $function (${function.javaClass})")
222+
return mapJvmFunctionSignature(function)
227223
}
228224

229225
fun mapPropertySignature(possiblyOverriddenProperty: PropertyDescriptor): JvmPropertySignature {
@@ -254,14 +250,6 @@ internal object RuntimeTypeMapper {
254250
)
255251
}
256252

257-
private fun isKnownBuiltInFunction(descriptor: FunctionDescriptor): Boolean {
258-
if (DescriptorFactory.isEnumValueOfMethod(descriptor) || DescriptorFactory.isEnumValuesMethod(descriptor)) return true
259-
260-
if (descriptor.name == CloneableClassScope.CLONE_NAME && descriptor.valueParameters.isEmpty()) return true
261-
262-
return false
263-
}
264-
265253
private fun mapJvmFunctionSignature(descriptor: FunctionDescriptor): JvmFunctionSignature.KotlinFunction =
266254
JvmFunctionSignature.KotlinFunction(
267255
JvmMemberSignature.Method(mapName(descriptor), descriptor.computeJvmDescriptor(withName = false))

js/js.tests/klib-compatibility/tests-gen/org/jetbrains/kotlin/js/test/klib/CustomJsCompilerFirstPhaseTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/klib-compatibility/tests-gen/org/jetbrains/kotlin/js/test/klib/CustomJsCompilerSecondPhaseTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)