Fix KProperty.javaField for fake overrides

#KT-8131 Fixed
This commit is contained in:
Alexander Udalov
2016-02-20 12:45:29 +03:00
parent 534a3a11d6
commit 1c0bbcd72b
8 changed files with 106 additions and 43 deletions
@@ -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"
}
@@ -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"
}
@@ -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<*> ?: