diff --git a/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaInstanceField/J.java b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaInstanceField/J.java new file mode 100644 index 00000000000..4027c1cedd5 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaInstanceField/J.java @@ -0,0 +1,3 @@ +public class J { + private String result = "Fail"; +} diff --git a/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaInstanceField/K.kt b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaInstanceField/K.kt new file mode 100644 index 00000000000..8064ec98471 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaInstanceField/K.kt @@ -0,0 +1,10 @@ +import kotlin.reflect.* +import kotlin.reflect.jvm.* + +fun box(): String { + val a = J() + val p = J::class.members.single { it.name == "result" } as KMutableProperty1 + p.isAccessible = true + p[a] = "OK" + return p[a] +} diff --git a/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaStaticField/J.java b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaStaticField/J.java new file mode 100644 index 00000000000..6403ad28ce0 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaStaticField/J.java @@ -0,0 +1,3 @@ +public class J { + private static String result = "Fail"; +} diff --git a/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaStaticField/K.kt b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaStaticField/K.kt new file mode 100644 index 00000000000..14a60a22c27 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaStaticField/K.kt @@ -0,0 +1,10 @@ +import kotlin.reflect.* +import kotlin.reflect.jvm.* + +fun box(): String { + val a = J() + val p = J::class.members.single { it.name == "result" } as KMutableProperty0 + p.isAccessible = true + p.set("OK") + return p.get() +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/properties/privatePlatformStaticVarInObject.kt b/compiler/testData/codegen/boxWithStdlib/reflection/properties/privatePlatformStaticVarInObject.kt new file mode 100644 index 00000000000..4ccc89213f4 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/properties/privatePlatformStaticVarInObject.kt @@ -0,0 +1,25 @@ +import kotlin.reflect.* +import kotlin.reflect.jvm.* +import kotlin.platform.platformStatic as static + +object Obj { + private static var result: String = "Fail" +} + +fun box(): String { + val p = Obj::class.members.single { it.name == "result" } as KMutableProperty1 + p.isAccessible = true + + try { + p[null] = "OK" + return "Fail: set should check that first argument is Obj" + } catch (e: IllegalArgumentException) {} + + try { + p[null] + return "Fail: get should check that first argument is Obj" + } catch (e: IllegalArgumentException) {} + + p[Obj] = "OK" + return p[Obj] +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index 3849b652741..4ea0806b7a9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -303,6 +303,18 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("mutatePrivateJavaInstanceField") + public void testMutatePrivateJavaInstanceField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaInstanceField/"); + doTestWithJava(fileName); + } + + @TestMetadata("mutatePrivateJavaStaticField") + public void testMutatePrivateJavaStaticField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/mutatePrivateJavaStaticField/"); + doTestWithJava(fileName); + } + @TestMetadata("noConflictOnKotlinGetterAndJavaField") public void testNoConflictOnKotlinGetterAndJavaField() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/noConflictOnKotlinGetterAndJavaField/"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 66e30189982..a1ee5dee421 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -3537,6 +3537,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("privatePlatformStaticVarInObject.kt") + public void testPrivatePlatformStaticVarInObject() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/privatePlatformStaticVarInObject.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("privatePropertyCallIsAccessibleOnAccessors.kt") public void testPrivatePropertyCallIsAccessibleOnAccessors() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/properties/privatePropertyCallIsAccessibleOnAccessors.kt"); 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 73d2f6713aa..67cf31c1eb5 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt @@ -17,17 +17,12 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.serialization.ProtoBuf -import org.jetbrains.kotlin.serialization.deserialization.NameResolver -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor -import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf import java.lang.reflect.Field -import java.lang.reflect.Method +import kotlin.reflect.jvm.internal.JvmPropertySignature.JavaField +import kotlin.reflect.jvm.internal.JvmPropertySignature.KotlinProperty abstract class DescriptorBasedProperty protected constructor( - container: KCallableContainerImpl, + internal val container: KCallableContainerImpl, name: String, signature: String, descriptorInitialValue: PropertyDescriptor? @@ -43,47 +38,19 @@ abstract class DescriptorBasedProperty protected constructor( descriptor ) - private data class PropertyProtoData( - val proto: ProtoBuf.Callable, - val nameResolver: NameResolver, - val signature: JvmProtoBuf.JvmPropertySignature - ) - override val descriptor: PropertyDescriptor by ReflectProperties.lazySoft(descriptorInitialValue) { container.findPropertyDescriptor(name, signature) } - // null if this is a property declared in a foreign (Java) class - private val protoData: PropertyProtoData? by ReflectProperties.lazyWeak { - val property = DescriptorUtils.unwrapFakeOverride(descriptor) as? DeserializedPropertyDescriptor - if (property != null) { - val proto = property.proto - if (proto.hasExtension(JvmProtoBuf.propertySignature)) { - return@lazyWeak PropertyProtoData(proto, property.nameResolver, proto.getExtension(JvmProtoBuf.propertySignature)) + internal val javaField: Field? by ReflectProperties.lazySoft { + val jvmSignature = RuntimeTypeMapper.mapPropertySignature(descriptor) + when (jvmSignature) { + is KotlinProperty -> { + if (!jvmSignature.signature.hasField()) null + else container.findFieldBySignature(jvmSignature.proto, jvmSignature.signature.field, jvmSignature.nameResolver) } + is JavaField -> jvmSignature.field } - null - } - - open val javaField: Field? by ReflectProperties.lazySoft { - val proto = protoData - if (proto == null) container.jClass.getField(name) - else if (!proto.signature.hasField()) null - else container.findFieldBySignature(proto.proto, proto.signature.getField(), proto.nameResolver) - } - - open val javaGetter: Method? by ReflectProperties.lazySoft { - val proto = protoData - if (proto == null || !proto.signature.hasGetter()) null - else container.findMethodBySignature(proto.proto, proto.signature.getGetter(), proto.nameResolver, - descriptor.getGetter()?.getVisibility()?.let { Visibilities.isPrivate(it) } ?: false) - } - - open val javaSetter: Method? by ReflectProperties.lazySoft { - val proto = protoData - if (proto == null || !proto.signature.hasSetter()) null - else container.findMethodBySignature(proto.proto, proto.signature.getSetter(), proto.nameResolver, - descriptor.getSetter()?.getVisibility()?.let { Visibilities.isPrivate(it) } ?: false) } override fun equals(other: Any?): Boolean = diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt index c45f2dc8d22..2f120549dae 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt @@ -16,19 +16,32 @@ package kotlin.reflect.jvm.internal +import java.lang.reflect.Member import java.lang.reflect.Constructor as ReflectConstructor +import java.lang.reflect.Field as ReflectField +import java.lang.reflect.Member as ReflectMember import java.lang.reflect.Method as ReflectMethod internal sealed class FunctionCaller { + abstract val member: ReflectMember + abstract fun call(args: Array<*>): Any? - class Constructor(val constructor: ReflectConstructor<*>) : FunctionCaller() { - override fun call(args: Array<*>): Any? { - return constructor.newInstance(*args) + protected fun checkObjectInstance(obj: Any?) { + if (obj == null || !member.declaringClass.isInstance(obj)) { + throw IllegalArgumentException("An object member requires the object instance passed as the first argument.") } } + class Constructor(val constructor: ReflectConstructor<*>) : FunctionCaller() { + override val member: Member get() = constructor + + override fun call(args: Array<*>): Any? = + constructor.newInstance(*args) + } + abstract class Method(val method: ReflectMethod) : FunctionCaller() { + override val member: Member get() = method private val isVoidMethod = method.returnType == Void.TYPE protected fun callMethod(instance: Any?, args: Array<*>): Any? { @@ -40,23 +53,57 @@ internal sealed class FunctionCaller { } class StaticMethod(method: ReflectMethod) : Method(method) { - override fun call(args: Array<*>): Any? { - return callMethod(null, args) - } + override fun call(args: Array<*>): Any? = + callMethod(null, args) } class InstanceMethod(method: ReflectMethod) : Method(method) { - override fun call(args: Array<*>): Any? { - return callMethod(args[0], args.asList().subList(1, args.size()).toTypedArray()) - } + override fun call(args: Array<*>): Any? = + callMethod(args[0], args.asList().subList(1, args.size()).toTypedArray()) } class PlatformStaticInObject(method: ReflectMethod) : Method(method) { override fun call(args: Array<*>): Any? { - if (args.isEmpty() || !method.declaringClass.isInstance(args[0])) { - throw IllegalArgumentException("A function in an object requires the object instance passed as the first argument.") - } + checkObjectInstance(args.firstOrNull()) return callMethod(null, args.asList().subList(1, args.size()).toTypedArray()) } } + + abstract class Field(val field: ReflectField) : FunctionCaller() { + override val member: Member get() = field + } + + class StaticFieldGetter(field: ReflectField) : Field(field) { + override fun call(args: Array<*>): Any? = + field.get(null) + } + + class InstanceFieldGetter(field: ReflectField) : Field(field) { + override fun call(args: Array<*>): Any? = + field.get(args[0]) + } + + class PlatformStaticInObjectFieldGetter(field: ReflectField) : Field(field) { + override fun call(args: Array<*>): Any? { + checkObjectInstance(args.firstOrNull()) + return field.get(null) + } + } + + class StaticFieldSetter(field: ReflectField) : Field(field) { + override fun call(args: Array<*>): Any? = + field.set(null, args[0]) + } + + class InstanceFieldSetter(field: ReflectField) : Field(field) { + override fun call(args: Array<*>): Any? = + field.set(args[0], args[1]) + } + + class PlatformStaticInObjectFieldSetter(field: ReflectField) : Field(field) { + override fun call(args: Array<*>): Any? { + checkObjectInstance(args.firstOrNull()) + return field.set(null, args[1]) + } + } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt index 1716113ae0f..4ff5b02074d 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt @@ -26,6 +26,8 @@ import kotlin.reflect.KType interface KCallableImpl : KCallable, KAnnotatedElementImpl { val descriptor: CallableMemberDescriptor + val caller: FunctionCaller + override val annotated: Annotated get() = descriptor override val parameters: List @@ -51,4 +53,9 @@ interface KCallableImpl : KCallable, KAnnotatedElementImpl { override val returnType: KType get() = KTypeImpl(descriptor.returnType!!) + + @suppress("UNCHECKED_CAST") + override fun call(vararg args: Any?): R = reflectionCall { + return caller.call(args) as R + } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt index 3427875af20..eb78526b947 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt @@ -19,7 +19,6 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.name.FqName import java.lang.reflect.Constructor import java.lang.reflect.Member import java.lang.reflect.Method @@ -50,7 +49,7 @@ open class KFunctionImpl protected constructor( override val name: String get() = descriptor.name.asString() - internal val caller: FunctionCaller by ReflectProperties.lazySoft { + override val caller: FunctionCaller by ReflectProperties.lazySoft { val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor) val member: Member? = when (jvmSignature) { is KotlinFunction -> @@ -74,10 +73,6 @@ open class KFunctionImpl protected constructor( } } - override fun call(vararg args: Any?): Any? = reflectionCall { - caller.call(args) - } - override fun getArity(): Int { return descriptor.valueParameters.size() + (if (descriptor.dispatchReceiverParameter != null) 1 else 0) + @@ -92,8 +87,4 @@ open class KFunctionImpl protected constructor( override fun toString(): String = ReflectionObjectRenderer.renderFunction(descriptor) - - private companion object { - val PLATFORM_STATIC = FqName("kotlin.platform.platformStatic") - } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt index 78b98fdbfe6..9f4990a660b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt @@ -17,7 +17,6 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import java.lang.reflect.Method import kotlin.jvm.internal.MutablePropertyReference0 import kotlin.jvm.internal.PropertyReference0 import kotlin.reflect.KMutableProperty0 @@ -30,17 +29,7 @@ open class KProperty0Impl : DescriptorBasedProperty, KProperty0, KP override val getter by ReflectProperties.lazy { Getter(this) } - override val javaGetter: Method get() = super.javaGetter!! - - @suppress("UNCHECKED_CAST") - override fun get(): R = reflectionCall { - return javaGetter.invoke(null) as R - } - - override fun call(vararg args: Any?): R { - require(args.isEmpty()) { "Property $name expects no arguments, but ${args.size()} were provided." } - return get() - } + override fun get(): R = getter.call() class Getter(override val property: KProperty0Impl) : KPropertyImpl.Getter(), KProperty0.Getter { override fun invoke(): R = property.get() @@ -54,22 +43,10 @@ open class KMutableProperty0Impl : KProperty0Impl, KMutableProperty0, K override val setter by ReflectProperties.lazy { Setter(this) } - override val javaSetter: Method get() = super.javaSetter!! - - override fun set(value: R) { - reflectionCall { - javaSetter.invoke(null, value) - } - } + override fun set(value: R) = setter.call(value) class Setter(override val property: KMutableProperty0Impl) : KMutablePropertyImpl.Setter(), KMutableProperty0.Setter { override fun invoke(value: R): Unit = property.set(value) - - @suppress("UNCHECKED_CAST") - override fun call(vararg args: Any?) { - require(args.size() == 1) { "Property setter for ${property.name} expects one argument, but ${args.size()} were provided." } - property.set(args.single() as R) - } } } @@ -77,11 +54,11 @@ open class KMutableProperty0Impl : KProperty0Impl, KMutableProperty0, K class KProperty0FromReferenceImpl( val reference: PropertyReference0 ) : KProperty0Impl( - reference.getOwner() as KCallableContainerImpl, - reference.getName(), - reference.getSignature() + reference.owner as KCallableContainerImpl, + reference.name, + reference.signature ) { - override val name: String get() = reference.getName() + override val name: String get() = reference.name override fun get(): Any? = reference.get() } @@ -90,11 +67,11 @@ class KProperty0FromReferenceImpl( class KMutableProperty0FromReferenceImpl( val reference: MutablePropertyReference0 ) : KMutableProperty0Impl( - reference.getOwner() as KCallableContainerImpl, - reference.getName(), - reference.getSignature() + reference.owner as KCallableContainerImpl, + reference.name, + reference.signature ) { - override val name: String get() = reference.getName() + override val name: String get() = reference.name override fun get(): Any? = reference.get() diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt index ed8205b06de..97afc4a1bea 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt @@ -17,7 +17,6 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import java.lang.reflect.Modifier import kotlin.jvm.internal.MutablePropertyReference1 import kotlin.jvm.internal.PropertyReference1 import kotlin.reflect.KMutableProperty1 @@ -30,33 +29,13 @@ open class KProperty1Impl : DescriptorBasedProperty, KProperty1 javaField!!.get(receiver) - Modifier.isStatic(getter.modifiers) -> { - // Workaround the case of platformStatic property in object, getter of which doesn't take a receiver - if (getter.parameterTypes.isEmpty()) getter.invoke(null) - else getter.invoke(null, receiver) - } - else -> getter.invoke(receiver) - } as R - } - - @suppress("UNCHECKED_CAST") - override fun call(vararg args: Any?): R { - require(args.size() == 1) { "Property $name expects one argument, but ${args.size()} were provided." } - return get(args.single() as T) - } + override fun get(receiver: T): R = getter.call(receiver) class Getter(override val property: KProperty1Impl) : KPropertyImpl.Getter(), KProperty1.Getter { override fun invoke(receiver: T): R = property.get(receiver) } } - open class KMutableProperty1Impl : KProperty1Impl, KMutableProperty1, KMutablePropertyImpl { constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature) @@ -64,29 +43,10 @@ open class KMutableProperty1Impl : KProperty1Impl, KMutableProperty1 override val setter by ReflectProperties.lazy { Setter(this) } - override fun set(receiver: T, value: R) { - reflectionCall { - val setter = javaSetter - when { - setter == null -> javaField!!.set(receiver, value) - Modifier.isStatic(setter.modifiers) -> { - // Workaround the case of platformStatic property in object, setter of which doesn't take a receiver - if (setter.parameterTypes.size() == 1) setter.invoke(null, value) - else setter.invoke(null, receiver, value) - } - else -> setter.invoke(receiver, value) - } - } - } + override fun set(receiver: T, value: R) = setter.call(receiver, value) class Setter(override val property: KMutableProperty1Impl) : KMutablePropertyImpl.Setter(), KMutableProperty1.Setter { override fun invoke(receiver: T, value: R): Unit = property.set(receiver, value) - - @suppress("UNCHECKED_CAST") - override fun call(vararg args: Any?) { - require(args.size() == 2) { "Property setter for ${property.name} expects two arguments, but ${args.size()} were provided." } - property.set(args[0] as T, args[1] as R) - } } } @@ -94,11 +54,11 @@ open class KMutableProperty1Impl : KProperty1Impl, KMutableProperty1 class KProperty1FromReferenceImpl( val reference: PropertyReference1 ) : KProperty1Impl( - reference.getOwner() as KCallableContainerImpl, - reference.getName(), - reference.getSignature() + reference.owner as KCallableContainerImpl, + reference.name, + reference.signature ) { - override val name: String get() = reference.getName() + override val name: String get() = reference.name override fun get(receiver: Any?): Any? = reference.get(receiver) } @@ -107,11 +67,11 @@ class KProperty1FromReferenceImpl( class KMutableProperty1FromReferenceImpl( val reference: MutablePropertyReference1 ) : KMutableProperty1Impl( - reference.getOwner() as KCallableContainerImpl, - reference.getName(), - reference.getSignature() + reference.owner as KCallableContainerImpl, + reference.name, + reference.signature ) { - override val name: String get() = reference.getName() + override val name: String get() = reference.name override fun get(receiver: Any?): Any? = reference.get(receiver) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt index 61199c69af8..031a7613836 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt @@ -17,8 +17,6 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import java.lang.reflect.Field -import java.lang.reflect.Method import kotlin.reflect.KMutableProperty2 import kotlin.reflect.KProperty2 @@ -29,27 +27,13 @@ open class KProperty2Impl : DescriptorBasedProperty, KProperty2< override val getter by ReflectProperties.lazy { Getter(this) } - override val javaGetter: Method get() = super.javaGetter!! - - override val javaField: Field? get() = null - - @suppress("UNCHECKED_CAST") - override fun get(receiver1: D, receiver2: E): R = reflectionCall { - return javaGetter.invoke(receiver1, receiver2) as R - } - - @suppress("UNCHECKED_CAST") - override fun call(vararg args: Any?): R { - require(args.size() == 2) { "Property $name expects two arguments, but ${args.size()} were provided." } - return get(args[0] as D, args[1] as E) - } + override fun get(receiver1: D, receiver2: E): R = getter.call(receiver1, receiver2) class Getter(override val property: KProperty2Impl) : KPropertyImpl.Getter(), KProperty2.Getter { override fun invoke(receiver1: D, receiver2: E): R = property.get(receiver1, receiver2) } } - class KMutableProperty2Impl : KProperty2Impl, KMutableProperty2, KMutablePropertyImpl { constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature) @@ -57,21 +41,9 @@ class KMutableProperty2Impl : KProperty2Impl, KMutableProperty override val setter by ReflectProperties.lazy { Setter(this) } - override val javaSetter: Method get() = super.javaSetter!! - - override fun set(receiver1: D, receiver2: E, value: R) { - reflectionCall { - javaSetter.invoke(receiver1, receiver2, value) - } - } + override fun set(receiver1: D, receiver2: E, value: R) = setter.call(receiver1, receiver2, value) class Setter(override val property: KMutableProperty2Impl) : KMutablePropertyImpl.Setter(), KMutableProperty2.Setter { override fun invoke(receiver1: D, receiver2: E, value: R): Unit = property.set(receiver1, receiver2, value) - - @suppress("UNCHECKED_CAST") - override fun call(vararg args: Any?) { - require(args.size() == 3) { "Property setter for ${property.name} expects three arguments, but ${args.size()} were provided." } - property.set(args[0] as D, args[1] as E, args[2] as R) - } } } 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 60a333af8a2..4896385ff9b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -16,19 +16,17 @@ package kotlin.reflect.jvm.internal -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor -import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.DescriptorFactory import java.lang.reflect.Field -import java.lang.reflect.Method +import java.lang.reflect.Modifier import kotlin.reflect.KMutableProperty import kotlin.reflect.KProperty interface KPropertyImpl : KProperty, KCallableImpl { val javaField: Field? - val javaGetter: Method? + val container: KCallableContainerImpl override val getter: Getter @@ -36,36 +34,90 @@ interface KPropertyImpl : KProperty, KCallableImpl { override val name: String get() = descriptor.name.asString() - interface Accessor : KProperty.Accessor { - override val property: KPropertyImpl + override val caller: FunctionCaller get() = getter.caller + + abstract class Accessor : KProperty.Accessor { + abstract override val property: KPropertyImpl + + internal abstract val descriptor: PropertyAccessorDescriptor } - abstract class Getter : KProperty.Getter, Accessor, KCallableImpl { + abstract class Getter : Accessor(), KProperty.Getter, KCallableImpl { override val name: String get() = "" override val descriptor: PropertyGetterDescriptor by ReflectProperties.lazySoft { // TODO: default getter created this way won't have any source information - property.descriptor.getGetter() ?: DescriptorFactory.createDefaultGetter(property.descriptor) + property.descriptor.getter ?: DescriptorFactory.createDefaultGetter(property.descriptor) } - override fun call(vararg args: Any?): R = property.call(*args) + override val caller: FunctionCaller by ReflectProperties.lazySoft { + computeCallerForAccessor(isGetter = true) + } } } interface KMutablePropertyImpl : KMutableProperty, KPropertyImpl { - val javaSetter: Method? - override val setter: Setter - abstract class Setter : KMutableProperty.Setter, KPropertyImpl.Accessor, KCallableImpl { + abstract class Setter : KPropertyImpl.Accessor(), KMutableProperty.Setter, KCallableImpl { abstract override val property: KMutablePropertyImpl override val name: String get() = "" override val descriptor: PropertySetterDescriptor by ReflectProperties.lazySoft { // TODO: default setter created this way won't have any source information - property.descriptor.getSetter() ?: DescriptorFactory.createDefaultSetter(property.descriptor) + property.descriptor.setter ?: DescriptorFactory.createDefaultSetter(property.descriptor) + } + + override val caller: FunctionCaller by ReflectProperties.lazySoft { + computeCallerForAccessor(isGetter = false) + } + } +} + + +private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean): FunctionCaller { + fun isPlatformStaticProperty() = + property.descriptor.annotations.findAnnotation(PLATFORM_STATIC) != null + + fun computeFieldCaller(field: Field): FunctionCaller.Field = when { + !Modifier.isStatic(field.modifiers) -> + if (isGetter) FunctionCaller.InstanceFieldGetter(field) + else FunctionCaller.InstanceFieldSetter(field) + isPlatformStaticProperty() -> + if (isGetter) FunctionCaller.PlatformStaticInObjectFieldGetter(field) + else FunctionCaller.PlatformStaticInObjectFieldSetter(field) + else -> + if (isGetter) FunctionCaller.StaticFieldGetter(field) + else FunctionCaller.StaticFieldSetter(field) + } + + val jvmSignature = RuntimeTypeMapper.mapPropertySignature(property.descriptor) + return when (jvmSignature) { + is JvmPropertySignature.KotlinProperty -> { + val accessorSignature = jvmSignature.signature.run { + when { + isGetter -> if (hasGetter()) getter else null + else -> if (hasSetter()) setter else null + } + } + + val accessor = accessorSignature?.let { signature -> + property.container.findMethodBySignature( + jvmSignature.proto, signature, jvmSignature.nameResolver, Visibilities.isPrivate(descriptor.visibility) + ) + } + + when { + accessor == null -> computeFieldCaller(property.javaField!!) + !Modifier.isStatic(accessor.modifiers) -> FunctionCaller.InstanceMethod(accessor) + isPlatformStaticProperty() -> FunctionCaller.PlatformStaticInObject(accessor) + else -> FunctionCaller.StaticMethod(accessor) + } + } + is JvmPropertySignature.JavaField -> { + computeFieldCaller(jvmSignature.field) } } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt index 0901dc36aff..4c37e4a10be 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt @@ -79,17 +79,18 @@ sealed class JvmFunctionSignature { } } -interface JvmPropertySignature { +sealed class JvmPropertySignature { /** * Returns the JVM signature of the getter of this property. In case the property doesn't have a getter, * constructs the signature of its imaginary default getter. See CallableReference#getSignature for more information */ - fun asString(): String + abstract fun asString(): String class KotlinProperty( - private val signature: JvmProtoBuf.JvmPropertySignature, - private val nameResolver: NameResolver - ) : JvmPropertySignature { + val proto: ProtoBuf.Callable, + val signature: JvmProtoBuf.JvmPropertySignature, + val nameResolver: NameResolver + ) : JvmPropertySignature() { private val string: String init { @@ -106,7 +107,7 @@ interface JvmPropertySignature { override fun asString(): String = string } - class JavaImaginaryFieldGetter(private val field: Field) : JvmPropertySignature { + class JavaField(val field: Field) : JvmPropertySignature() { override fun asString(): String = JvmAbi.getterName(field.name) + "()" + @@ -151,13 +152,13 @@ object RuntimeTypeMapper { if (!proto.hasExtension(JvmProtoBuf.propertySignature)) { throw KotlinReflectionInternalError("No metadata found for $property") } - return JvmPropertySignature.KotlinProperty(proto.getExtension(JvmProtoBuf.propertySignature), property.nameResolver) + return JvmPropertySignature.KotlinProperty(proto, proto.getExtension(JvmProtoBuf.propertySignature), property.nameResolver) } else if (property is JavaPropertyDescriptor) { val field = ((property.source as? JavaSourceElement)?.javaElement as? ReflectJavaField)?.member ?: throw KotlinReflectionInternalError("Incorrect resolution sequence for Java field $property") - return JvmPropertySignature.JavaImaginaryFieldGetter(field) + return JvmPropertySignature.JavaField(field) } else throw KotlinReflectionInternalError("Unknown origin of $property (${property.javaClass})") } 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 74d287b4066..89a4e3b91b6 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -16,8 +16,11 @@ package kotlin.reflect.jvm.internal +import org.jetbrains.kotlin.name.FqName import kotlin.reflect.IllegalCallableAccessException +internal val PLATFORM_STATIC = FqName("kotlin.platform.platformStatic") + // TODO: wrap other exceptions internal inline fun reflectionCall(block: () -> R): R = try { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt index 9715c173e0d..e3ec39f3961 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt @@ -55,14 +55,14 @@ public val KProperty<*>.javaField: Field? * or `null` if the property has no getter, for example in case of a simple private `val` in a class. */ public val KProperty<*>.javaGetter: Method? - get() = (this as? KPropertyImpl<*>)?.javaGetter + get() = getter.javaMethod /** * Returns a Java [Method] instance corresponding to the setter of the given mutable property, * or `null` if the property has no setter, for example in case of a simple private `var` in a class. */ public val KMutableProperty<*>.javaSetter: Method? - get() = (this as? KMutablePropertyImpl<*>)?.javaSetter + get() = setter.javaMethod /** @@ -71,9 +71,7 @@ public val KMutableProperty<*>.javaSetter: Method? */ public val KFunction<*>.javaMethod: Method? get() = when (this) { - is KFunctionImpl -> (caller as? FunctionCaller.Method)?.method - is KPropertyImpl.Getter<*> -> property.javaGetter - is KMutablePropertyImpl.Setter<*> -> property.javaSetter + is KCallableImpl<*> -> (caller as? FunctionCaller.Method)?.method else -> null }