Fix KProperty.javaField for fake overrides
#KT-8131 Fixed
This commit is contained in:
Vendored
+22
@@ -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"
|
||||
}
|
||||
+15
@@ -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"
|
||||
}
|
||||
+21
@@ -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)
|
||||
|
||||
@@ -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<out R> 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 {
|
||||
|
||||
@@ -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<T : Any>(override val jClass: Class<T>) : KDeclaration
|
||||
override val nestedClasses: Collection<KClass<*>>
|
||||
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) }
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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<Field> = 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)
|
||||
}
|
||||
|
||||
@@ -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 <R> reflectionCall(block: () -> R): R =
|
||||
try {
|
||||
@@ -34,7 +55,7 @@ internal inline fun <R> 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<*> ?:
|
||||
|
||||
Reference in New Issue
Block a user