From 6d0a403ead3e6f895c2cb19328ea4049ba86588e Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 19 Oct 2018 15:45:01 +0300 Subject: [PATCH] KT-26765: Support 'call' for constructors with inline class parameters --- .../codegen/PropertyReferenceCodegen.kt | 6 ++++ .../constructorWithInlineClassParameters.kt | 27 +++++++++++++++ ... => functionsWithInlineClassParameters.kt} | 10 ------ .../codegen/BlackBoxCodegenTestGenerated.java | 11 ++++-- .../LightAnalysisModeTestGenerated.java | 11 ++++-- .../ir/IrBlackBoxCodegenTestGenerated.java | 11 ++++-- .../reflect/jvm/internal/KFunctionImpl.kt | 20 ++++++++--- .../reflect/jvm/internal/calls/CallerImpl.kt | 34 +++++++++++++++++++ .../internal/calls/InlineClassAwareCaller.kt | 28 +++++++++++---- .../IrJsCodegenBoxTestGenerated.java | 11 ++++-- .../semantics/JsCodegenBoxTestGenerated.java | 11 ++++-- 11 files changed, 145 insertions(+), 35 deletions(-) create mode 100644 compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt rename compiler/testData/codegen/box/reflection/call/inlineClasses/{functionsAndConstructors.kt => functionsWithInlineClassParameters.kt} (72%) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt index f51e6c6df5b..c5346462094 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.jvm.AsmTypes.* import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.resolve.jvm.shouldHideConstructorDueToInlineClassTypeValueParameters import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.util.OperatorNameConventions @@ -241,6 +242,11 @@ class PropertyReferenceCodegen( } val accessor = when (callable) { + is ClassConstructorDescriptor -> + if (shouldHideConstructorDueToInlineClassTypeValueParameters(callable)) + AccessorForConstructorDescriptor(callable, callable.containingDeclaration, null, AccessorKind.NORMAL) + else + callable is FunctionDescriptor -> callable is VariableDescriptorWithAccessors -> callable.getter ?: DescriptorFactory.createDefaultGetter(callable as PropertyDescriptor, Annotations.EMPTY).apply { diff --git a/compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt b/compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt new file mode 100644 index 00000000000..5c18d5709a8 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt @@ -0,0 +1,27 @@ +// IGNORE_BACKEND: JS_IR, JS, NATIVE, JVM_IR +// WITH_REFLECT +import kotlin.test.assertEquals + +inline class Z(val x: Int) + +class Outer(val z1: Z) { + inner class Inner(val z2: Z) { + val test = "$z1 $z2" + } +} + +inline class InlineOuter(val z1: Z) { + inner class Inner(val z2: Z) { + val test = "$z1 $z2" + } +} + +fun box(): String { + assertEquals(Z(1), ::Outer.call(Z(1)).z1) + assertEquals("Z(x=1) Z(x=2)", Outer::Inner.call(Outer(Z(1)), Z(2)).test) + assertEquals("Z(x=1) Z(x=3)", Outer(Z(1))::Inner.call(Z(3)).test) + assertEquals("Z(x=1) Z(x=2)", InlineOuter::Inner.call(InlineOuter(Z(1)), Z(2)).test) + assertEquals("Z(x=1) Z(x=3)", InlineOuter(Z(1))::Inner.call(Z(3)).test) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt b/compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt similarity index 72% rename from compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt rename to compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt index cf9338b71a4..34e6d45c69a 100644 --- a/compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt +++ b/compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt @@ -13,14 +13,6 @@ class C { fun topLevel(x: String, y: S): S = S(x) + y -/* TODO: support constructors with inline class types in the signature (KT-26765) -class D { - inner class Inner(x: S, y: S) { - val result = x + y - } -} -*/ - fun S.extension(y: S): S = this + y fun S.extension2(): String = value @@ -28,12 +20,10 @@ fun S.extension2(): String = value fun box(): String { assertEquals(S("ab"), C::member.call(C(), S("a"), "b")) assertEquals(S("cd"), ::topLevel.call("c", S("d"))) - // assertEquals(S("ef"), D::Inner.call(D(), S("e"), S("f")).result) assertEquals(S("gh"), S::extension.call(S("g"), S("h"))) assertEquals("_", S::extension2.call(S("_"))) assertEquals(S("ij"), C()::member.call(S("i"), "j")) - // assertEquals(S("kl"), D()::Inner.call(S("k"), S("l")).result) assertEquals(S("mn"), S("m")::extension.call(S("n"))) assertEquals("_", S("_")::extension2.call()) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 5f015374db6..82adc7ac1fe 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -19283,14 +19283,19 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("constructorWithInlineClassParameters.kt") + public void testConstructorWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt"); + } + @TestMetadata("fieldAccessors.kt") public void testFieldAccessors() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/fieldAccessors.kt"); } - @TestMetadata("functionsAndConstructors.kt") - public void testFunctionsAndConstructors() throws Exception { - runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt"); + @TestMetadata("functionsWithInlineClassParameters.kt") + public void testFunctionsWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt"); } @TestMetadata("inlineClassConstructor.kt") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 8e686dd9ac2..7aa2b93a4f4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -19283,14 +19283,19 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("constructorWithInlineClassParameters.kt") + public void testConstructorWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt"); + } + @TestMetadata("fieldAccessors.kt") public void testFieldAccessors() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/fieldAccessors.kt"); } - @TestMetadata("functionsAndConstructors.kt") - public void testFunctionsAndConstructors() throws Exception { - runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt"); + @TestMetadata("functionsWithInlineClassParameters.kt") + public void testFunctionsWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt"); } @TestMetadata("inlineClassConstructor.kt") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 7692210ddcb..156b2e707f6 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -19288,14 +19288,19 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); } + @TestMetadata("constructorWithInlineClassParameters.kt") + public void testConstructorWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt"); + } + @TestMetadata("fieldAccessors.kt") public void testFieldAccessors() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/fieldAccessors.kt"); } - @TestMetadata("functionsAndConstructors.kt") - public void testFunctionsAndConstructors() throws Exception { - runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt"); + @TestMetadata("functionsWithInlineClassParameters.kt") + public void testFunctionsWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt"); } @TestMetadata("inlineClassConstructor.kt") 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 451b0bdd05f..b68e49ab609 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt @@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.resolve.jvm.shouldHideConstructorDueToInlineClassTypeValueParameters import java.lang.reflect.Constructor import java.lang.reflect.Member import java.lang.reflect.Method @@ -76,7 +77,7 @@ internal class KFunctionImpl private constructor( when (member) { is Constructor<*> -> - createConstructorCaller(member) + createConstructorCaller(member, descriptor) is Method -> when { !Modifier.isStatic(member.modifiers) -> createInstanceMethodCaller(member) @@ -112,7 +113,7 @@ internal class KFunctionImpl private constructor( when (member) { is Constructor<*> -> - createConstructorCaller(member) + createConstructorCaller(member, descriptor) is Method -> when { // Note that static $default methods for @JvmStatic functions are generated differently in objects and companion objects. // In objects, $default's signature does _not_ contain the additional object instance parameter, @@ -140,8 +141,19 @@ internal class KFunctionImpl private constructor( private fun createInstanceMethodCaller(member: Method) = if (isBound) CallerImpl.Method.BoundInstance(member, boundReceiver) else CallerImpl.Method.Instance(member) - private fun createConstructorCaller(member: Constructor<*>) = - if (isBound) CallerImpl.BoundConstructor(member, boundReceiver) else CallerImpl.Constructor(member) + private fun createConstructorCaller(member: Constructor<*>, descriptor: FunctionDescriptor): CallerImpl> { + return if (shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor)) { + if (isBound) + CallerImpl.AccessorForHiddenBoundConstructor(member, boundReceiver) + else + CallerImpl.AccessorForHiddenConstructor(member) + } else { + if (isBound) + CallerImpl.BoundConstructor(member, boundReceiver) + else + CallerImpl.Constructor(member) + } + } override val arity: Int get() = caller.arity diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt index 6865b62aaaa..aef634aa324 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt @@ -55,6 +55,32 @@ internal sealed class CallerImpl( } } + class AccessorForHiddenConstructor( + constructor: ReflectConstructor<*> + ) : CallerImpl>( + constructor, constructor.declaringClass, null, + constructor.genericParameterTypes.dropLast() + ) { + override fun call(args: Array<*>): Any? { + checkArguments(args) + return member.newInstance(*args, null) + } + } + + class AccessorForHiddenBoundConstructor( + constructor: ReflectConstructor<*>, + private val boundReceiver: Any? + ) : CallerImpl>( + constructor, constructor.declaringClass, + null, + constructor.genericParameterTypes.dropFirstAndLast() + ), BoundCaller { + override fun call(args: Array<*>): Any? { + checkArguments(args) + return member.newInstance(boundReceiver, *args, null) + } + } + sealed class Method( method: ReflectMethod, requiresInstance: Boolean = !Modifier.isStatic(method.modifiers), @@ -211,5 +237,13 @@ internal sealed class CallerImpl( @Suppress("UNCHECKED_CAST") inline fun Array.dropFirst(): Array = if (size <= 1) emptyArray() else copyOfRange(1, size) as Array + + @Suppress("UNCHECKED_CAST") + inline fun Array.dropLast(): Array = + if (size <= 1) emptyArray() else copyOfRange(0, size - 1) as Array + + @Suppress("UNCHECKED_CAST") + inline fun Array.dropFirstAndLast(): Array = + if (size <= 2) emptyArray() else copyOfRange(1, size - 1) as Array } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InlineClassAwareCaller.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InlineClassAwareCaller.kt index 6971c6d54e8..4ac4b7fb8e2 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InlineClassAwareCaller.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InlineClassAwareCaller.kt @@ -52,6 +52,9 @@ internal class InlineClassAwareCaller( -1 } + descriptor is ConstructorDescriptor -> + if (caller is BoundCaller) -1 else 0 + descriptor.dispatchReceiverParameter != null && caller !is BoundCaller -> { // If we have an unbound reference to the inline class member, // its receiver (which is passed as argument 0) should also be unboxed. @@ -70,7 +73,12 @@ internal class InlineClassAwareCaller( val extensionReceiverType = descriptor.extensionReceiverParameter?.type if (extensionReceiverType != null) { kotlinParameterTypes.add(extensionReceiverType) - } else if (descriptor !is ConstructorDescriptor) { + } else if (descriptor is ConstructorDescriptor) { + val constructedClass = descriptor.constructedClass + if (constructedClass.isInner) { + kotlinParameterTypes.add((constructedClass.containingDeclaration as ClassDescriptor).defaultType) + } + } else { val containingDeclaration = descriptor.containingDeclaration if (containingDeclaration is ClassDescriptor && containingDeclaration.isInline) { kotlinParameterTypes.add(containingDeclaration.defaultType) @@ -162,13 +170,21 @@ internal fun KotlinType.toInlineClass(): Class<*>? { return null } -private val CallableMemberDescriptor.expectedReceiverType - get() = - extensionReceiverParameter?.type - ?: (containingDeclaration as? ClassDescriptor)?.defaultType.takeIf { dispatchReceiverParameter != null } +private val CallableMemberDescriptor.expectedReceiverType: KotlinType? + get() { + val extensionReceiver = extensionReceiverParameter + val dispatchReceiver = dispatchReceiverParameter + return when { + extensionReceiver != null -> extensionReceiver.type + dispatchReceiver == null -> null + this is ConstructorDescriptor -> dispatchReceiver.type + else -> (containingDeclaration as? ClassDescriptor)?.defaultType + } + } internal fun Any?.coerceToExpectedReceiverType(descriptor: CallableMemberDescriptor): Any? { - val unboxMethod = descriptor.expectedReceiverType?.toInlineClass()?.getUnboxMethod(descriptor) ?: return this + val expectedReceiverType = descriptor.expectedReceiverType + val unboxMethod = expectedReceiverType?.toInlineClass()?.getUnboxMethod(descriptor) ?: return this return unboxMethod.invoke(this) } \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index 9b840255409..99953b86e81 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -16813,14 +16813,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); } + @TestMetadata("constructorWithInlineClassParameters.kt") + public void testConstructorWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt"); + } + @TestMetadata("fieldAccessors.kt") public void testFieldAccessors() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/fieldAccessors.kt"); } - @TestMetadata("functionsAndConstructors.kt") - public void testFunctionsAndConstructors() throws Exception { - runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt"); + @TestMetadata("functionsWithInlineClassParameters.kt") + public void testFunctionsWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt"); } @TestMetadata("inlineClassConstructor.kt") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 13eb7345913..378e0a035dd 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -17858,14 +17858,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("constructorWithInlineClassParameters.kt") + public void testConstructorWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/constructorWithInlineClassParameters.kt"); + } + @TestMetadata("fieldAccessors.kt") public void testFieldAccessors() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/fieldAccessors.kt"); } - @TestMetadata("functionsAndConstructors.kt") - public void testFunctionsAndConstructors() throws Exception { - runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsAndConstructors.kt"); + @TestMetadata("functionsWithInlineClassParameters.kt") + public void testFunctionsWithInlineClassParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/functionsWithInlineClassParameters.kt"); } @TestMetadata("inlineClassConstructor.kt")