diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index c1e37aa78a8..efa6304beea 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -396,6 +396,9 @@ public class ClosureCodegen extends MemberCodegen { } else if (container instanceof PackageFragmentDescriptor) { iv.aconst(state.getTypeMapper().mapOwner(descriptor)); + // Note that this name is not used in reflection. There should be the name of the referenced declaration's module instead, + // but there's no nice API to obtain that name here yet + // TODO: write the referenced declaration's module name and use it in reflection iv.aconst(state.getModuleName()); iv.invokestatic(REFLECTION, "getOrCreateKotlinPackage", Type.getMethodDescriptor(K_DECLARATION_CONTAINER_TYPE, getType(Class.class), getType(String.class)), false); diff --git a/compiler/testData/codegen/box/reflection/functions/functionFromStdlib.kt b/compiler/testData/codegen/box/reflection/functions/functionFromStdlib.kt index 231c44f208d..d7365353542 100644 --- a/compiler/testData/codegen/box/reflection/functions/functionFromStdlib.kt +++ b/compiler/testData/codegen/box/reflection/functions/functionFromStdlib.kt @@ -1,6 +1,9 @@ +// IGNORE_BACKEND: JS // WITH_REFLECT -fun doStuff(fn: String.() -> String) = "ok".fn() +import kotlin.reflect.KFunction1 + +fun doStuff(fn: KFunction1) = fn.call("ok") fun box(): String { return doStuff(String::toUpperCase) diff --git a/compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.kt b/compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.kt new file mode 100644 index 00000000000..298a5e87fb3 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.kt @@ -0,0 +1,13 @@ +// KT-12630 KotlinReflectionInternalError on referencing some functions from stdlib + +// IGNORE_BACKEND: JS +// WITH_REFLECT + +import kotlin.test.* + +fun box(): String { + val asIterable = List::asIterable + assertEquals("fun kotlin.collections.Iterable.asIterable(): kotlin.collections.Iterable", asIterable.toString()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.kt b/compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.kt new file mode 100644 index 00000000000..6f37db1d1f6 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.kt @@ -0,0 +1,13 @@ +// KT-12630 KotlinReflectionInternalError on referencing some functions from stdlib + +// IGNORE_BACKEND: JS +// WITH_REFLECT + +import kotlin.test.* + +fun box(): String { + val lazyOf: (String) -> Lazy = ::lazyOf + assertEquals("fun lazyOf(T): kotlin.Lazy", lazyOf.toString()) + + return "OK" +} diff --git a/compiler/testData/codegen/light-analysis/reflection/functions/functionFromStdlib.txt b/compiler/testData/codegen/light-analysis/reflection/functions/functionFromStdlib.txt index 73fd1c4d5f0..4f34401cc9a 100644 --- a/compiler/testData/codegen/light-analysis/reflection/functions/functionFromStdlib.txt +++ b/compiler/testData/codegen/light-analysis/reflection/functions/functionFromStdlib.txt @@ -1,5 +1,5 @@ @kotlin.Metadata public final class FunctionFromStdlibKt { public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String - public final static @org.jetbrains.annotations.NotNull method doStuff(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): java.lang.String + public final static @org.jetbrains.annotations.NotNull method doStuff(@org.jetbrains.annotations.NotNull p0: kotlin.reflect.KFunction): java.lang.String } diff --git a/compiler/testData/codegen/light-analysis/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.txt b/compiler/testData/codegen/light-analysis/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.txt new file mode 100644 index 00000000000..f66ba38acbf --- /dev/null +++ b/compiler/testData/codegen/light-analysis/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.txt @@ -0,0 +1,4 @@ +@kotlin.Metadata +public final class FunctionFromStdlibMultiFileFacadeKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.txt b/compiler/testData/codegen/light-analysis/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.txt new file mode 100644 index 00000000000..16292f61262 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.txt @@ -0,0 +1,4 @@ +@kotlin.Metadata +public final class FunctionFromStdlibSingleFileFacadeKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 6dfb712d88d..cf1009b0bf3 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -14380,6 +14380,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("functionFromStdlibMultiFileFacade.kt") + public void testFunctionFromStdlibMultiFileFacade() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.kt"); + doTest(fileName); + } + + @TestMetadata("functionFromStdlibSingleFileFacade.kt") + public void testFunctionFromStdlibSingleFileFacade() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.kt"); + doTest(fileName); + } + @TestMetadata("functionToString.kt") public void testFunctionToString() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionToString.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 062f57f962a..a480b384442 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -14380,6 +14380,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("functionFromStdlibMultiFileFacade.kt") + public void testFunctionFromStdlibMultiFileFacade() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.kt"); + doTest(fileName); + } + + @TestMetadata("functionFromStdlibSingleFileFacade.kt") + public void testFunctionFromStdlibSingleFileFacade() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.kt"); + doTest(fileName); + } + @TestMetadata("functionToString.kt") public void testFunctionToString() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionToString.kt"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt index 527889865a7..6ca9366853b 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/header/KotlinClassHeader.kt @@ -55,7 +55,10 @@ class KotlinClassHeader( } val multifileClassName: String? - get() = if (kind == Kind.MULTIFILE_CLASS_PART) extraString else null + get() = extraString.takeIf { kind == Kind.MULTIFILE_CLASS_PART } + + val multifilePartNames: List + get() = data.takeIf { kind == Kind.MULTIFILE_CLASS }?.asList().orEmpty() // TODO: use in incremental compilation val multifileClassKind: MultifileClassKind? diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/ReflectJvmMapping.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/ReflectJvmMapping.kt index 8e08d9d172e..e75837773b4 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/ReflectJvmMapping.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/ReflectJvmMapping.kt @@ -17,18 +17,14 @@ @file:JvmName("ReflectJvmMapping") package kotlin.reflect.jvm -import org.jetbrains.kotlin.load.java.JvmAbi -import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass -import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf -import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil import java.lang.reflect.* import java.util.* -import kotlin.jvm.internal.Reflection import kotlin.reflect.* import kotlin.reflect.full.companionObject import kotlin.reflect.full.functions import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.internal.KPackageImpl import kotlin.reflect.jvm.internal.KTypeImpl import kotlin.reflect.jvm.internal.asKCallableImpl import kotlin.reflect.jvm.internal.asKPropertyImpl @@ -104,21 +100,8 @@ val Field.kotlinProperty: KProperty<*>? } -private fun Member.getKPackage(): KDeclarationContainer? { - // TODO: support multifile classes - val header = ReflectKotlinClass.create(declaringClass)?.classHeader - if (header != null && header.kind == KotlinClassHeader.Kind.FILE_FACADE && header.metadataVersion.isCompatible()) { - // TODO: avoid reading and parsing metadata twice (here and later in KPackageImpl#descriptor) - val (nameResolver, proto) = JvmProtoBufUtil.readPackageDataFrom(header.data!!, header.strings!!) - val moduleName = - if (proto.hasExtension(JvmProtoBuf.packageModuleName)) - nameResolver.getString(proto.getExtension(JvmProtoBuf.packageModuleName)) - else JvmAbi.DEFAULT_MODULE_NAME - return Reflection.getOrCreateKotlinPackage(declaringClass, moduleName) - } - - return null -} +private fun Member.getKPackage(): KDeclarationContainer? = + if (ReflectKotlinClass.create(declaringClass) != null) KPackageImpl(declaringClass) else null /** * Returns a [KFunction] instance corresponding to the given Java [Method] instance, diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt index 5ca15f0fa5e..2b100f478f1 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt @@ -28,20 +28,27 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import kotlin.reflect.KCallable import kotlin.reflect.jvm.internal.KDeclarationContainerImpl.MemberBelonginess.DECLARED -internal class KPackageImpl(override val jClass: Class<*>, val moduleName: String) : KDeclarationContainerImpl() { +internal class KPackageImpl( + override val jClass: Class<*>, + @Suppress("unused") val usageModuleName: String? = null // may be useful for debug +) : KDeclarationContainerImpl() { private inner class Data : KDeclarationContainerImpl.Data() { + val kotlinClass: ReflectKotlinClass? by ReflectProperties.lazySoft { + // TODO: do not read ReflectKotlinClass multiple times + ReflectKotlinClass.create(jClass) + } + val descriptor: PackageViewDescriptor by ReflectProperties.lazySoft { with(moduleData) { - packagePartProvider.registerModule(moduleName) + kotlinClass?.packageModuleName?.let(packagePartProvider::registerModule) module.getPackage(jClass.classId.packageFqName) } } val methodOwner: Class<*> by ReflectProperties.lazy { - val facadeName = ReflectKotlinClass.create(jClass)?.classHeader?.multifileClassName + val facadeName = kotlinClass?.classHeader?.multifileClassName // We need to check isNotEmpty because this is the value read from the annotation which cannot be null. // The default value for 'xs' is empty string, as declared in kotlin.Metadata - // TODO: do not read ReflectKotlinClass multiple times, obtain facade name from descriptor if (facadeName != null && facadeName.isNotEmpty()) { jClass.classLoader.loadClass(facadeName.replace('/', '.')) } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt index 43e2df76366..55808677492 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -20,22 +20,26 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.components.RuntimeSourceElementFactory import org.jetbrains.kotlin.load.java.reflect.tryLoadClass import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaAnnotation import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaClass import org.jetbrains.kotlin.load.java.structure.reflect.safeClassLoader import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.reflect.ReflectAnnotationSource import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf +import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil import kotlin.jvm.internal.FunctionReference import kotlin.jvm.internal.PropertyReference -import kotlin.reflect.full.IllegalCallableAccessException import kotlin.reflect.KVisibility +import kotlin.reflect.full.IllegalCallableAccessException internal val JVM_STATIC = FqName("kotlin.jvm.JvmStatic") @@ -117,3 +121,27 @@ internal fun Any?.asKPropertyImpl(): KPropertyImpl<*>? = internal fun Any?.asKCallableImpl(): KCallableImpl<*>? = this as? KCallableImpl<*> ?: asKFunctionImpl() ?: asKPropertyImpl() + +internal val ReflectKotlinClass.packageModuleName: String? + get() { + val header = classHeader + if (!header.metadataVersion.isCompatible()) return null + + return when (header.kind) { + KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> { + // TODO: avoid reading and parsing metadata twice (here and later in KPackageImpl#descriptor) + val (nameResolver, proto) = JvmProtoBufUtil.readPackageDataFrom(header.data!!, header.strings!!) + // If no packageModuleName extension is written, the name is assumed to be JvmAbi.DEFAULT_MODULE_NAME + // (see JvmSerializerExtension.serializePackage) + if (proto.hasExtension(JvmProtoBuf.packageModuleName)) + nameResolver.getString(proto.getExtension(JvmProtoBuf.packageModuleName)) + else JvmAbi.DEFAULT_MODULE_NAME + } + KotlinClassHeader.Kind.MULTIFILE_CLASS -> { + val partName = header.multifilePartNames.firstOrNull() ?: return null + ReflectKotlinClass.create(klass.classLoader.loadClass(partName.replace('/', '.')))?.packageModuleName + } + else -> null + } + } + diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 10072a11fb9..6e7ba6b1691 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -16859,7 +16859,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("functionFromStdlib.kt") public void testFunctionFromStdlib() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/functions/functionFromStdlib.kt"); - doTest(fileName); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } @TestMetadata("functionReferenceErasedToKFunction.kt") @@ -17645,6 +17651,30 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("functionFromStdlibMultiFileFacade.kt") + public void testFunctionFromStdlibMultiFileFacade() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibMultiFileFacade.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("functionFromStdlibSingleFileFacade.kt") + public void testFunctionFromStdlibSingleFileFacade() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionFromStdlibSingleFileFacade.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + @TestMetadata("functionToString.kt") public void testFunctionToString() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/functionToString.kt");