diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaFieldGetterSetter.kt b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaFieldGetterSetter.kt new file mode 100644 index 00000000000..d32b3fc3c59 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaFieldGetterSetter.kt @@ -0,0 +1,22 @@ +// KT-8131 Cannot find backing field in ancestor class via reflection + +import kotlin.reflect.* +import kotlin.reflect.jvm.* + +open class TestBase { + var id = 0L +} + +class TestChild : TestBase() + +fun box(): String { + val property = TestChild::class.memberProperties.first { it.name == "id" } as KMutableProperty<*> + if (property.javaField == null) + return "Fail: no field" + if (property.javaGetter == null) + return "Fail: no getter" + if (property.javaSetter == null) + return "Fail: no setter" + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaMethod.kt b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaMethod.kt new file mode 100644 index 00000000000..d22cecef010 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaMethod.kt @@ -0,0 +1,15 @@ +import kotlin.reflect.* +import kotlin.reflect.jvm.* + +open class TestBase { + fun id() = 0L +} + +class TestChild : TestBase() + +fun box(): String { + if (TestChild::class.memberFunctions.first { it.name == "id" }.javaMethod == null) + return "No method for TestChild.id()" + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java index 3d6ea539fba..cfff5ce6775 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java @@ -3987,6 +3987,27 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FakeOverrides extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInFakeOverrides() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("javaFieldGetterSetter.kt") + public void testJavaFieldGetterSetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaFieldGetterSetter.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("javaMethod.kt") + public void testJavaMethod() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/fakeOverrides/javaMethod.kt"); + doTestWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/jvmStatic") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt index 4732d212b4e..8f019d187b7 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt @@ -16,6 +16,7 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil @@ -50,17 +51,29 @@ internal abstract class DescriptorBasedProperty protected constructor( val jvmSignature = RuntimeTypeMapper.mapPropertySignature(descriptor) when (jvmSignature) { is KotlinProperty -> { + val descriptor = jvmSignature.descriptor JvmProtoBufUtil.getJvmFieldSignature(jvmSignature.proto, jvmSignature.nameResolver, jvmSignature.typeTable)?.let { - container.findFieldBySignature( - it.name, JvmAbi.isCompanionObjectWithBackingFieldsInOuter(descriptor.containingDeclaration) - ) + val owner = if (JvmAbi.isCompanionObjectWithBackingFieldsInOuter(descriptor.containingDeclaration)) { + container.jClass.enclosingClass + } + else descriptor.containingDeclaration.let { containingDeclaration -> + if (containingDeclaration is ClassDescriptor) containingDeclaration.toJavaClass() + else container.jClass + } + + try { + owner?.getDeclaredField(it.name) + } + catch (e: NoSuchFieldException) { + null + } } } is JavaField -> jvmSignature.field } } - // Used in subclasses as an implementation of an irrelevant property from KPropertyImpl + @Suppress("unused") // Used in subclasses as an implementation of an irrelevant property from KPropertyImpl val javaField: Field? get() = javaField_() override fun equals(other: Any?): Boolean { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 9882aeb758b..73d6e29335f 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -21,11 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.reflect.tryLoadClass -import org.jetbrains.kotlin.load.java.sources.JavaSourceElement -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.reflect.ReflectKotlinClass import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.JavaToKotlinClassMap @@ -122,22 +118,14 @@ internal class KClassImpl(override val jClass: Class) : KDeclaration override val nestedClasses: Collection> get() = descriptor.unsubstitutedInnerClassesScope.getContributedDescriptors().filterNot(DescriptorUtils::isEnumEntry).mapNotNull { nestedClass -> - val source = (nestedClass as DeclarationDescriptorWithSource).source - when (source) { - is KotlinJvmBinarySourceElement -> - (source.binaryClass as ReflectKotlinClass).klass - is JavaSourceElement -> - (source.javaElement as ReflectJavaClass).element - SourceElement.NO_SOURCE -> { - // If neither a Kotlin class nor a Java class, it must be a built-in - val classId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(DescriptorUtils.getFqName(nestedClass)) - ?: throw KotlinReflectionInternalError("Class with no source must be a built-in: $nestedClass") - val packageName = classId.packageFqName.asString() - val className = classId.relativeClassName.asString().replace('.', '$') - // All pseudo-classes like String.Companion must be accessible from the current class loader - (this as Any).javaClass.safeClassLoader.tryLoadClass("$packageName.$className") - } - else -> throw KotlinReflectionInternalError("Unsupported class: $nestedClass (source = $source)") + (nestedClass as ClassDescriptor).toJavaClass() ?: run { + // If neither a Kotlin class nor a Java class, it must be a built-in + val classId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(DescriptorUtils.getFqName(nestedClass)) + ?: throw KotlinReflectionInternalError("Class with no source must be a built-in: $nestedClass") + val packageName = classId.packageFqName.asString() + val className = classId.relativeClassName.asString().replace('.', '$') + // All pseudo-classes like String.Companion must be accessible from the current class loader + (this as Any).javaClass.safeClassLoader.tryLoadClass("$packageName.$className") } }.map { KClassImpl(it) } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt index abf5e33d0a3..85ee47a6b40 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.load.kotlin.reflect.RuntimeModuleData import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.scopes.MemberScope import java.lang.reflect.Constructor -import java.lang.reflect.Field import java.lang.reflect.Method import kotlin.jvm.internal.ClassBasedDeclarationContainer import kotlin.reflect.KCallable @@ -235,18 +234,6 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain return result } - // TODO: check resulting field's type - fun findFieldBySignature(name: String, isCompanionOfClass: Boolean): Field? { - val owner = if (isCompanionOfClass) jClass.enclosingClass else jClass - - return try { - owner.getDeclaredField(name) - } - catch (e: NoSuchFieldException) { - null - } - } - companion object { private val DEFAULT_CONSTRUCTOR_MARKER = Class.forName("kotlin.jvm.internal.DefaultConstructorMarker") } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt index ad5a709a035..c7fb4894252 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -18,8 +18,6 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement -import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.TypeUtils @@ -109,9 +107,7 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean fun computeFieldCaller(field: Field): FunctionCaller = when { isInsideClassCompanionObject() -> { - val containingDeclaration = descriptor.containingDeclaration as ClassDescriptor - val sourceElement = containingDeclaration.source as KotlinJvmBinarySourceElement - val klass = (sourceElement.binaryClass as ReflectKotlinClass).klass + val klass = (descriptor.containingDeclaration as ClassDescriptor).toJavaClass()!! if (isGetter) FunctionCaller.ClassCompanionFieldGetter(field, klass) else FunctionCaller.ClassCompanionFieldSetter(field, klass) } 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 22fd42b3e53..61e6424bd0d 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -16,6 +16,11 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.load.java.components.RuntimeSourceElementFactory +import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaClass +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass import org.jetbrains.kotlin.name.FqName import kotlin.jvm.internal.FunctionReference import kotlin.jvm.internal.PropertyReference @@ -23,6 +28,22 @@ import kotlin.reflect.IllegalCallableAccessException internal val JVM_STATIC = FqName("kotlin.jvm.JvmStatic") +internal fun ClassDescriptor.toJavaClass(): Class<*>? { + val source = source + return when (source) { + is KotlinJvmBinarySourceElement -> { + (source.binaryClass as ReflectKotlinClass).klass + } + is RuntimeSourceElementFactory.RuntimeSourceElement -> { + (source.javaElement as ReflectJavaClass).element + } + else -> { + // This is a built-in class + null + } + } +} + // TODO: wrap other exceptions internal inline fun reflectionCall(block: () -> R): R = try { @@ -34,7 +55,7 @@ internal inline fun reflectionCall(block: () -> R): R = internal fun Any?.asKFunctionImpl(): KFunctionImpl? = this as? KFunctionImpl ?: - (this as? FunctionReference)?.compute() as? KFunctionImpl // + (this as? FunctionReference)?.compute() as? KFunctionImpl internal fun Any?.asKPropertyImpl(): KPropertyImpl<*>? = this as? KPropertyImpl<*> ?: