diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/equalsHashCodeToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/equalsHashCodeToString.kt new file mode 100644 index 00000000000..aa168aa0e93 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/equalsHashCodeToString.kt @@ -0,0 +1,25 @@ +class A + +data class D(val s: String) + +fun box(): String { + val a = A() + assert(A::equals.call(a, a)) + assert(!A::equals.call(a, 0)) + assert(A::hashCode.call(a) == A::hashCode.call(a)) + assert(A::toString.call(a).startsWith("A@")) + + assert(D::equals.call(D("foo"), D("foo"))) + assert(!D::equals.call(D("foo"), D("bar"))) + assert(D::hashCode.call(D("foo")) == D::hashCode.call(D("foo"))) + assert(D::toString.call(D("foo")) == "D(s=foo)") + + assert(Int::equals.call(-1, -1)) + assert(Int::hashCode.call(0) != Int::hashCode.call(1)) + assert(Int::toString.call(42) == "42") + + assert(String::equals.call("beer", "beer")) + String::hashCode.call("beer") + + return String::toString.call("OK") +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/exceptionHappened.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/exceptionHappened.kt new file mode 100644 index 00000000000..aabbbdac620 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/exceptionHappened.kt @@ -0,0 +1,17 @@ +// FULL_JDK + +import java.lang.reflect.InvocationTargetException + +fun fail(message: String) { + throw AssertionError(message) +} + +fun box(): String { + try { + ::fail.call("OK") + } catch (e: InvocationTargetException) { + return e.getTargetException().getMessage().toString() + } + + return "Fail: no exception was thrown" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/innerClassConstructor.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/innerClassConstructor.kt new file mode 100644 index 00000000000..85bd90c6e04 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/innerClassConstructor.kt @@ -0,0 +1,8 @@ +class A { + class Nested(val result: String) + inner class Inner(val result: String) +} + +fun box(): String { + return (A::Nested).call("O").result + (A::Inner).call((::A).call(), "K").result +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/platformStatic.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/platformStatic.kt new file mode 100644 index 00000000000..86d167a68f6 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/platformStatic.kt @@ -0,0 +1,17 @@ +import kotlin.platform.platformStatic as static + +object Obj { + static fun foo() {} +} + +class C { + companion object { + static fun bar() {} + } +} + +fun box(): String { + (Obj::foo).call(Obj) + (C.Companion::bar).call(C.Companion) + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/platformStaticInObjectIncorrectReceiver.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/platformStaticInObjectIncorrectReceiver.kt new file mode 100644 index 00000000000..936aaa1b3b3 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/platformStaticInObjectIncorrectReceiver.kt @@ -0,0 +1,37 @@ +import kotlin.platform.platformStatic as static + +object Obj { + static fun foo(s: String) {} + static fun bar() {} + static fun sly(obj: Obj) {} +} + +fun box(): String { + // This should succeed + (Obj::foo).call(Obj, "") + (Obj::bar).call(Obj) + (Obj::sly).call(Obj, Obj) + + // This shouldn't: first argument should always be Obj + try { + (Obj::foo).call(null, "") + return "Fail foo" + } catch (e: IllegalArgumentException) {} + + try { + (Obj::bar).call("") + return "Fail bar" + } catch (e: IllegalArgumentException) {} + + try { + (Obj::sly).call(Obj) + return "Fail sly 1" + } catch (e: IllegalArgumentException) {} + + try { + (Obj::sly).call(null, Obj) + return "Fail sly 2" + } catch (e: IllegalArgumentException) {} + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/privateProperty.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/privateProperty.kt new file mode 100644 index 00000000000..0d53e266847 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/privateProperty.kt @@ -0,0 +1,17 @@ +import kotlin.reflect.* +import kotlin.reflect.jvm.* +import kotlin.test.* + +class A(private var result: String) + +fun box(): String { + val a = A("abc") + + val p = A::class.declaredProperties.single() as KMutableProperty1 + p.accessible = true + assertEquals("abc", p.call(a)) + assertEquals(Unit, p.setter.call(a, "def")) + assertEquals("def", p.getter.call(a)) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/propertyAccessors.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/propertyAccessors.kt new file mode 100644 index 00000000000..95b3ef9749a --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/propertyAccessors.kt @@ -0,0 +1,47 @@ +import kotlin.reflect.* +import kotlin.test.assertEquals + +val p0 = 1 +val Int.p1: Int get() = this +class A { + val Int.p2: Int get() = this +} + +var globalCounter = 0 + +var mp0 = 1 + set(value) { globalCounter += value } +var Int.mp1: Int + get() = this + set(value) { globalCounter += value } +class B { + var Int.mp2: Int + get() = this + set(value) { globalCounter += value } +} + + +fun box(): String { + assertEquals(1, (::p0).call()) + assertEquals(1, (::p0).getter.call()) + assertEquals(2, (Int::p1).call(2)) + assertEquals(2, (Int::p1).getter.call(2)) + val p2 = A::class.extensionProperties.single() + assertEquals(3, p2.call(A(), 3)) + assertEquals(3, p2.getter.call(A(), 3)) + + assertEquals(1, (::mp0).call()) + assertEquals(1, (::mp0).getter.call()) + assertEquals(2, (Int::mp1).call(2)) + assertEquals(2, (Int::mp1).getter.call(2)) + val mp2 = B::class.extensionProperties.single() as KMutableProperty2 + assertEquals(3, mp2.call(B(), 3)) + assertEquals(3, mp2.getter.call(B(), 3)) + + assertEquals(Unit, (::mp0).setter.call(1)) + assertEquals(Unit, (Int::mp1).setter.call(0, 3)) + assertEquals(Unit, mp2.setter.call(B(), 0, 5)) + if (globalCounter != 9) return "Fail: $globalCounter" + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/returnUnit.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/returnUnit.kt new file mode 100644 index 00000000000..05896d200fe --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/returnUnit.kt @@ -0,0 +1,24 @@ +import kotlin.test.assertEquals + +fun foo() {} + +class A { + fun bar() {} +} + +object O { + kotlin.platform.platformStatic fun baz() {} +} + +fun nullableUnit(unit: Boolean): Unit? = if (unit) Unit else null + +fun box(): String { + assertEquals(Unit, ::foo.call()) + assertEquals(Unit, A::bar.call(A())) + assertEquals(Unit, O::baz.call(O)) + + assertEquals(Unit, (::nullableUnit).call(true)) + assertEquals(null, (::nullableUnit).call(false)) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleConstructor.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleConstructor.kt new file mode 100644 index 00000000000..afcb2fd3794 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleConstructor.kt @@ -0,0 +1,6 @@ +class A(val result: String) + +fun box(): String { + val a = (::A).call("OK") + return a.result +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleMemberFunction.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleMemberFunction.kt new file mode 100644 index 00000000000..87bf9c0daff --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleMemberFunction.kt @@ -0,0 +1,16 @@ +class A { + fun foo(x: Int, y: Int) = x + y +} + +fun box(): String { + val x = (A::foo).call(A(), 42, 239) + if (x != 281) return "Fail: $x" + + try { + (A::foo).call() + return "Fail: no exception" + } + catch (e: Exception) {} + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleTopLevelFunctions.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleTopLevelFunctions.kt new file mode 100644 index 00000000000..2db97adddc3 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/simpleTopLevelFunctions.kt @@ -0,0 +1,27 @@ +fun String.foo(): Int = length() + +var state = "Fail" + +fun bar(result: String) { + state = result +} + +fun box(): String { + val f = (String::foo).call("abc") + if (f != 3) return "Fail: $f" + + try { + (String::foo).call() + return "Fail: IllegalArgumentException should have been thrown" + } + catch (e: IllegalArgumentException) {} + + try { + (String::foo).call(42) + return "Fail: IllegalArgumentException should have been thrown" + } + catch (e: IllegalArgumentException) {} + + (::bar).call("OK") + return state +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 816529baacd..a7f262c2903 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2767,6 +2767,81 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Call extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInCall() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/call"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("equalsHashCodeToString.kt") + public void testEqualsHashCodeToString() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/equalsHashCodeToString.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("exceptionHappened.kt") + public void testExceptionHappened() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/exceptionHappened.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("innerClassConstructor.kt") + public void testInnerClassConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/innerClassConstructor.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("platformStatic.kt") + public void testPlatformStatic() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/platformStatic.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("platformStaticInObjectIncorrectReceiver.kt") + public void testPlatformStaticInObjectIncorrectReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/platformStaticInObjectIncorrectReceiver.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("privateProperty.kt") + public void testPrivateProperty() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/privateProperty.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("propertyAccessors.kt") + public void testPropertyAccessors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/propertyAccessors.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("returnUnit.kt") + public void testReturnUnit() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/returnUnit.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleConstructor.kt") + public void testSimpleConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/simpleConstructor.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleMemberFunction.kt") + public void testSimpleMemberFunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/simpleMemberFunction.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simpleTopLevelFunctions.kt") + public void testSimpleTopLevelFunctions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/simpleTopLevelFunctions.kt"); + doTestWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/builtins/src/kotlin/reflect/KCallable.kt b/core/builtins/src/kotlin/reflect/KCallable.kt index 74a70de4641..61652623d2e 100644 --- a/core/builtins/src/kotlin/reflect/KCallable.kt +++ b/core/builtins/src/kotlin/reflect/KCallable.kt @@ -34,6 +34,15 @@ public interface KCallable { /** * Parameters required to make a call to this callable. + * If this callable requires a `this` instance or an extension receiver parameter, + * they come first in the list in that order. */ public val parameters: List + + /** + * Calls this callable with the specified arguments and returns the result. + * Throws an exception if the number of specified arguments is not equal to the size of [parameters], + * or if their types do not match the types of the parameters. + */ + public fun call(vararg args: Any?): R } diff --git a/core/builtins/src/kotlin/reflect/KParameter.kt b/core/builtins/src/kotlin/reflect/KParameter.kt index c5f60a6a473..dd5430ad580 100644 --- a/core/builtins/src/kotlin/reflect/KParameter.kt +++ b/core/builtins/src/kotlin/reflect/KParameter.kt @@ -36,7 +36,8 @@ public interface KParameter { public val name: String? /** - * Type of this parameter. + * Type of this parameter. For a `vararg` parameter, this is the type of the corresponding array, + * not the individual element. */ public val type: KType } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt new file mode 100644 index 00000000000..c45f2dc8d22 --- /dev/null +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.reflect.jvm.internal + +import java.lang.reflect.Constructor as ReflectConstructor +import java.lang.reflect.Method as ReflectMethod + +internal sealed class FunctionCaller { + abstract fun call(args: Array<*>): Any? + + class Constructor(val constructor: ReflectConstructor<*>) : FunctionCaller() { + override fun call(args: Array<*>): Any? { + return constructor.newInstance(*args) + } + } + + abstract class Method(val method: ReflectMethod) : FunctionCaller() { + private val isVoidMethod = method.returnType == Void.TYPE + + protected fun callMethod(instance: Any?, args: Array<*>): Any? { + val result = method.invoke(instance, *args) + + // If this is a Unit function, the method returns void, Method#invoke returns null, while we should return Unit + return if (isVoidMethod) Unit else result + } + } + + class StaticMethod(method: ReflectMethod) : Method(method) { + override fun call(args: Array<*>): Any? { + return 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()) + } + } + + 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.") + } + return callMethod(null, args.asList().subList(1, args.size()).toTypedArray()) + } + } +} 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 6bd32b6ab21..bec263df0a3 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.load.java.sources.JavaSourceElement import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaConstructor import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaMethod +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.deserialization.NameResolver @@ -29,6 +30,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf import java.lang.reflect.Constructor import java.lang.reflect.Method +import java.lang.reflect.Modifier import kotlin.jvm.internal.FunctionImpl import kotlin.reflect.* @@ -68,7 +70,7 @@ open class KFunctionImpl protected constructor( } internal val javaMethod: Method? by ReflectProperties.lazySoft { - if (name != "") { + if (!isConstructor) { val proto = protoData if (proto != null) { container.findMethodBySignature(proto.proto, proto.signature, proto.nameResolver, @@ -82,7 +84,7 @@ open class KFunctionImpl protected constructor( } internal val javaConstructor: Constructor<*>? by ReflectProperties.lazySoft { - if (name == "") { + if (isConstructor) { val proto = protoData if (proto != null) { return@lazySoft container.findConstructorBySignature( @@ -98,6 +100,22 @@ open class KFunctionImpl protected constructor( override val name: String get() = descriptor.getName().asString() + private val caller: FunctionCaller by ReflectProperties.lazySoft { + javaConstructor?.let { FunctionCaller.Constructor(it) } ?: + javaMethod?.let { method -> + when { + !Modifier.isStatic(method.modifiers) -> FunctionCaller.InstanceMethod(method) + descriptor.annotations.findAnnotation(PLATFORM_STATIC) != null -> FunctionCaller.PlatformStaticInObject(method) + else -> FunctionCaller.StaticMethod(method) + } + } ?: + throw KotlinReflectionInternalError("Call is not yet supported for this function: $descriptor") + } + + override fun call(vararg args: Any?): Any? = caller.call(args) + + private val isConstructor: Boolean get() = name == "" + override fun getArity(): Int { // TODO: test? return descriptor.getValueParameters().size() + @@ -113,4 +131,8 @@ 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 6818e08ad38..93f02c4c4df 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty0Impl.kt @@ -45,6 +45,11 @@ open class KProperty0Impl : DescriptorBasedProperty, KProperty0, KP } } + override fun call(vararg args: Any?): R { + require(args.isEmpty()) { "Property $name expects no arguments, but ${args.size()} were provided." } + return get() + } + class Getter(override val property: KProperty0Impl) : KPropertyImpl.Getter(), KProperty0.Getter { override fun invoke(): R = property.get() } @@ -70,6 +75,12 @@ open class KMutableProperty0Impl : KProperty0Impl, KMutableProperty0, K 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) + } } } 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 23fa200ee0b..ae2e7797749 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty1Impl.kt @@ -56,6 +56,12 @@ open class KProperty1Impl : DescriptorBasedProperty, KProperty1(override val property: KProperty1Impl) : KPropertyImpl.Getter(), KProperty1.Getter { override fun invoke(receiver: T): R = property.get(receiver) } @@ -94,6 +100,12 @@ open class KMutableProperty1Impl : KProperty1Impl, KMutableProperty1 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) + } } } 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 366965f4032..cb0a4558b41 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KProperty2Impl.kt @@ -46,6 +46,12 @@ open class KProperty2Impl : DescriptorBasedProperty, KProperty2< } } + @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) + } + class Getter(override val property: KProperty2Impl) : KPropertyImpl.Getter(), KProperty2.Getter { override fun invoke(receiver1: D, receiver2: E): R = property.get(receiver1, receiver2) } @@ -72,5 +78,11 @@ class KMutableProperty2Impl : KProperty2Impl, KMutableProperty 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 9e1d36c4a4a..30460c97e9d 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -45,6 +45,8 @@ interface KPropertyImpl : KProperty, KCallableImpl { // TODO: default getter created this way won't have any source information property.descriptor.getGetter() ?: DescriptorFactory.createDefaultGetter(property.descriptor) } + + override fun call(vararg args: Any?): R = property.call(*args) } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java index d4b3d495bd7..3e51a3b8609 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java @@ -20,6 +20,7 @@ import kotlin.jvm.KotlinReflectionNotSupportedError; import kotlin.reflect.KCallable; import kotlin.reflect.KDeclarationContainer; import kotlin.reflect.KParameter; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -68,6 +69,11 @@ public abstract class CallableReference implements KCallable { throw error(); } + @Override + public Object call(@NotNull Object... args) { + throw error(); + } + protected static Error error() { throw new KotlinReflectionNotSupportedError(); } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java index 64cee9ed4a0..eb7f2cd0f5e 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java @@ -18,6 +18,7 @@ package kotlin.jvm.internal; import kotlin.jvm.KotlinReflectionNotSupportedError; import kotlin.reflect.*; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -65,6 +66,11 @@ public class FunctionReference throw error(); } + @Override + public Object call(@NotNull Object... args) { + throw error(); + } + protected static Error error() { throw new KotlinReflectionNotSupportedError(); }