From 99d8f2eb0c508cfa62c977630e46554c654a091a Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Thu, 8 Nov 2018 16:27:32 +0300 Subject: [PATCH] Support 'call' for primary value of an inline class Getter of a primary value of an inline class belongs to the box class. Its arguments should not be unboxed when the method is called. However, its result might require boxing if it's an inline class value. When we have an internal primary value, there's no getter method. In fact, we can use box/unbox methods for inline class directly (don't forget to box the result, it may be an inline class type value). #KT-26748 --- .../codegen/PropertyReferenceCodegen.kt | 2 +- .../codegen/context/CodegenContextUtil.kt | 10 ++- .../internalPrimaryValOfInlineClass.kt | 39 ++++++++++ .../inlineClasses/primaryValOfInlineClass.kt | 39 ++++++++++ .../mapping/inlineClassPrimaryVal.kt | 39 ++++++++++ .../mapping/types/inlineClassPrimaryVal.kt | 60 ++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 20 ++++++ .../LightAnalysisModeTestGenerated.java | 20 ++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 20 ++++++ .../kotlin/resolve/inlineClassesUtils.kt | 3 + .../reflect/jvm/internal/KPropertyImpl.kt | 18 ++++- .../internal/calls/InlineClassAwareCaller.kt | 71 +++++++++++-------- .../InternalUnderlyingValOfInlineClass.kt | 44 ++++++++++++ .../IrJsCodegenBoxTestGenerated.java | 10 +++ .../semantics/JsCodegenBoxTestGenerated.java | 10 +++ 15 files changed, 369 insertions(+), 36 deletions(-) create mode 100644 compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt create mode 100644 compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt create mode 100644 compiler/testData/codegen/box/reflection/mapping/inlineClassPrimaryVal.kt create mode 100644 compiler/testData/codegen/box/reflection/mapping/types/inlineClassPrimaryVal.kt create mode 100644 core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InternalUnderlyingValOfInlineClass.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt index c5346462094..4d1e64efdde 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt @@ -256,7 +256,7 @@ class PropertyReferenceCodegen( } val declaration = DescriptorUtils.unwrapFakeOverride(accessor).original val method = - if (callable.containingDeclaration.isInlineClass()) + if (callable.containingDeclaration.isInlineClass() && !declaration.isGetterOfUnderlyingPropertyOfInlineClass()) state.typeMapper.mapSignatureForInlineErasedClassSkipGeneric(declaration).asmMethod else state.typeMapper.mapAsmMethod(declaration) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContextUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContextUtil.kt index 49996c7614a..c9821ab0a7a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContextUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContextUtil.kt @@ -17,9 +17,11 @@ package org.jetbrains.kotlin.codegen.context import org.jetbrains.kotlin.codegen.OwnerKind +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.resolve.isGetterOfUnderlyingPropertyOfInlineClass import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.org.objectweb.asm.Type @@ -34,13 +36,19 @@ object CodegenContextUtil { @JvmStatic fun isImplementationOwner(owner: CodegenContext<*>, descriptor: DeclarationDescriptor): Boolean { - if (descriptor.containingDeclaration?.isInlineClass() == true) { + if (descriptor is CallableDescriptor && descriptor.containingDeclaration.isInlineClass()) { val isInErasedMethod = owner.contextKind == OwnerKind.ERASED_INLINE_CLASS + + if (descriptor.isGetterOfUnderlyingPropertyOfInlineClass()) { + return !isInErasedMethod + } + when (descriptor) { is FunctionDescriptor -> return isInErasedMethod is PropertyDescriptor -> return !isInErasedMethod } } + return owner !is MultifileClassFacadeContext } } diff --git a/compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt b/compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt new file mode 100644 index 00000000000..00b0bf9974b --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt @@ -0,0 +1,39 @@ +// IGNORE_BACKEND: JS_IR, JS, NATIVE, JVM_IR +// WITH_REFLECT + +import kotlin.test.assertEquals + +inline class Z(internal val x: Int) +inline class Z2(internal val x: Z) + +inline class L(internal val x: Long) +inline class L2(internal val x: L) + +inline class A(internal val x: Any?) +inline class A2(internal val x: A) + +fun box(): String { + assertEquals(42, Z::x.call(Z(42))) + assertEquals(42, Z(42)::x.call()) + + assertEquals(1234L, L::x.call(L(1234L))) + assertEquals(1234L, L(1234L)::x.call()) + + assertEquals("abc", A::x.call(A("abc"))) + assertEquals("abc", A("abc")::x.call()) + assertEquals(null, A::x.call(A(null))) + assertEquals(null, A(null)::x.call()) + + assertEquals(Z(42), Z2::x.call(Z2(Z(42)))) + assertEquals(Z(42), Z2(Z(42))::x.call()) + + assertEquals(L(1234L), L2::x.call(L2(L(1234L)))) + assertEquals(L(1234L), L2(L(1234L))::x.call()) + + assertEquals(A("abc"), A2::x.call(A2(A("abc")))) + assertEquals(A("abc"), A2(A("abc"))::x.call()) + assertEquals(A(null), A2::x.call(A2(A(null)))) + assertEquals(A(null), A2(A(null))::x.call()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt b/compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt new file mode 100644 index 00000000000..a82b83c3dae --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt @@ -0,0 +1,39 @@ +// IGNORE_BACKEND: JS_IR, JS, NATIVE, JVM_IR +// WITH_REFLECT + +import kotlin.test.assertEquals + +inline class Z(val x: Int) +inline class Z2(val x: Z) + +inline class L(val x: Long) +inline class L2(val x: L) + +inline class A(val x: Any?) +inline class A2(val x: A) + +fun box(): String { + assertEquals(42, Z::x.call(Z(42))) + assertEquals(42, Z(42)::x.call()) + + assertEquals(1234L, L::x.call(L(1234L))) + assertEquals(1234L, L(1234L)::x.call()) + + assertEquals("abc", A::x.call(A("abc"))) + assertEquals("abc", A("abc")::x.call()) + assertEquals(null, A::x.call(A(null))) + assertEquals(null, A(null)::x.call()) + + assertEquals(Z(42), Z2::x.call(Z2(Z(42)))) + assertEquals(Z(42), Z2(Z(42))::x.call()) + + assertEquals(L(1234L), L2::x.call(L2(L(1234L)))) + assertEquals(L(1234L), L2(L(1234L))::x.call()) + + assertEquals(A("abc"), A2::x.call(A2(A("abc")))) + assertEquals(A("abc"), A2(A("abc"))::x.call()) + assertEquals(A(null), A2::x.call(A2(A(null)))) + assertEquals(A(null), A2(A(null))::x.call()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/reflection/mapping/inlineClassPrimaryVal.kt b/compiler/testData/codegen/box/reflection/mapping/inlineClassPrimaryVal.kt new file mode 100644 index 00000000000..4bd08bd2dec --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/inlineClassPrimaryVal.kt @@ -0,0 +1,39 @@ +// IGNORE_BACKEND: JVM_IR, JS_IR, JS, NATIVE +// WITH_REFLECT + +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals + +inline class Z1(val publicX: Int) { + companion object { + val publicXRef = Z1::publicX + val publicXBoundRef = Z1(42)::publicX + } +} + +inline class Z2(internal val internalX: Int) { + companion object { + val internalXRef = Z2::internalX + val internalXBoundRef = Z2(42)::internalX + } +} + +inline class Z3(private val privateX: Int) { + companion object { + val privateXRef = Z3::privateX + val privateXBoundRef = Z3(42)::privateX + } +} + +fun box(): String { + assertEquals("getPublicX", Z1.publicXRef.javaGetter!!.name) + assertEquals("getPublicX", Z1.publicXBoundRef.javaGetter!!.name) + + assertEquals(null, Z2.internalXRef.javaGetter) + assertEquals(null, Z2.internalXBoundRef.javaGetter) + + assertEquals(null, Z3.privateXRef.javaGetter) + assertEquals(null, Z3.privateXBoundRef.javaGetter) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/reflection/mapping/types/inlineClassPrimaryVal.kt b/compiler/testData/codegen/box/reflection/mapping/types/inlineClassPrimaryVal.kt new file mode 100644 index 00000000000..2f732970b1a --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/types/inlineClassPrimaryVal.kt @@ -0,0 +1,60 @@ +// IGNORE_BACKEND: JVM_IR, JS_IR, JS, NATIVE +// WITH_REFLECT + +import kotlin.reflect.KCallable +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals + +inline class Z1(val publicX: Int) { + companion object { + val publicXRef = Z1::publicX + val publicXBoundRef = Z1(42)::publicX + } +} + +inline class Z2(internal val internalX: Int) { + companion object { + val internalXRef = Z2::internalX + val internalXBoundRef = Z2(42)::internalX + } +} + +inline class Z3(private val privateX: Int) { + companion object { + val privateXRef = Z3::privateX + val privateXBoundRef = Z3(42)::privateX + } +} + +inline class ZZ(val x: Z1) + +fun KCallable<*>.getJavaTypesOfParams() = parameters.map { it.type.javaType }.toString() +fun KCallable<*>.getJavaTypeOfResult() = returnType.javaType.toString() + +fun box(): String { + assertEquals("[class Z1]", Z1.publicXRef.getJavaTypesOfParams()) + assertEquals("int", Z1.publicXRef.getJavaTypeOfResult()) + + assertEquals("[]", Z1.publicXBoundRef.getJavaTypesOfParams()) + assertEquals("int", Z1.publicXBoundRef.getJavaTypeOfResult()) + + assertEquals("[class Z2]", Z2.internalXRef.getJavaTypesOfParams()) + assertEquals("int", Z2.internalXRef.getJavaTypeOfResult()) + + assertEquals("[]", Z2.internalXBoundRef.getJavaTypesOfParams()) + assertEquals("int", Z2.internalXBoundRef.getJavaTypeOfResult()) + + assertEquals("[class Z3]", Z3.privateXRef.getJavaTypesOfParams()) + assertEquals("int", Z3.privateXRef.getJavaTypeOfResult()) + + assertEquals("[]", Z3.privateXBoundRef.getJavaTypesOfParams()) + assertEquals("int", Z3.privateXBoundRef.getJavaTypeOfResult()) + + + assertEquals("[class ZZ]", ZZ::x.getJavaTypesOfParams()) + + // KT-28170 + assertEquals("int", ZZ::x.getJavaTypeOfResult()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index d27b12d4f34..21ad4313965 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -19313,6 +19313,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/inlineClassConstructor.kt"); } + @TestMetadata("internalPrimaryValOfInlineClass.kt") + public void testInternalPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt"); + } + @TestMetadata("jvmStaticFieldInObject.kt") public void testJvmStaticFieldInObject() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/jvmStaticFieldInObject.kt"); @@ -19343,6 +19348,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/overridingVarOfInlineClass.kt"); } + @TestMetadata("primaryValOfInlineClass.kt") + public void testPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt"); + } + @TestMetadata("properties.kt") public void testProperties() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/properties.kt"); @@ -20150,6 +20160,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/mapping/functions.kt"); } + @TestMetadata("inlineClassPrimaryVal.kt") + public void testInlineClassPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/inlineClassPrimaryVal.kt"); + } + @TestMetadata("inlineReifiedFun.kt") public void testInlineReifiedFun() throws Exception { runTest("compiler/testData/codegen/box/reflection/mapping/inlineReifiedFun.kt"); @@ -20293,6 +20308,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/mapping/types/inlineClassInSignature.kt"); } + @TestMetadata("inlineClassPrimaryVal.kt") + public void testInlineClassPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/inlineClassPrimaryVal.kt"); + } + @TestMetadata("innerGenericTypeArgument.kt") public void testInnerGenericTypeArgument() throws Exception { runTest("compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 0f80acf7041..08ca5adf5a0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -19313,6 +19313,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/inlineClassConstructor.kt"); } + @TestMetadata("internalPrimaryValOfInlineClass.kt") + public void testInternalPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt"); + } + @TestMetadata("jvmStaticFieldInObject.kt") public void testJvmStaticFieldInObject() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/jvmStaticFieldInObject.kt"); @@ -19343,6 +19348,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/overridingVarOfInlineClass.kt"); } + @TestMetadata("primaryValOfInlineClass.kt") + public void testPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt"); + } + @TestMetadata("properties.kt") public void testProperties() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/properties.kt"); @@ -20150,6 +20160,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/mapping/functions.kt"); } + @TestMetadata("inlineClassPrimaryVal.kt") + public void testInlineClassPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/inlineClassPrimaryVal.kt"); + } + @TestMetadata("inlineReifiedFun.kt") public void testInlineReifiedFun() throws Exception { runTest("compiler/testData/codegen/box/reflection/mapping/inlineReifiedFun.kt"); @@ -20293,6 +20308,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/mapping/types/inlineClassInSignature.kt"); } + @TestMetadata("inlineClassPrimaryVal.kt") + public void testInlineClassPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/inlineClassPrimaryVal.kt"); + } + @TestMetadata("innerGenericTypeArgument.kt") public void testInnerGenericTypeArgument() throws Exception { runTest("compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 7899b09bdd5..475553062e0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -19318,6 +19318,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/inlineClassConstructor.kt"); } + @TestMetadata("internalPrimaryValOfInlineClass.kt") + public void testInternalPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt"); + } + @TestMetadata("jvmStaticFieldInObject.kt") public void testJvmStaticFieldInObject() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/jvmStaticFieldInObject.kt"); @@ -19348,6 +19353,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/overridingVarOfInlineClass.kt"); } + @TestMetadata("primaryValOfInlineClass.kt") + public void testPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt"); + } + @TestMetadata("properties.kt") public void testProperties() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/properties.kt"); @@ -20155,6 +20165,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/mapping/functions.kt"); } + @TestMetadata("inlineClassPrimaryVal.kt") + public void testInlineClassPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/inlineClassPrimaryVal.kt"); + } + @TestMetadata("inlineReifiedFun.kt") public void testInlineReifiedFun() throws Exception { runTest("compiler/testData/codegen/box/reflection/mapping/inlineReifiedFun.kt"); @@ -20298,6 +20313,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/mapping/types/inlineClassInSignature.kt"); } + @TestMetadata("inlineClassPrimaryVal.kt") + public void testInlineClassPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/inlineClassPrimaryVal.kt"); + } + @TestMetadata("innerGenericTypeArgument.kt") public void testInnerGenericTypeArgument() throws Exception { runTest("compiler/testData/codegen/box/reflection/mapping/types/innerGenericTypeArgument.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index 1b13bc05c8e..166e4d2b7dd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt @@ -58,6 +58,9 @@ fun KotlinType.isNullableUnderlyingType(): Boolean { return TypeUtils.isNullableType(underlyingType) } +fun CallableDescriptor.isGetterOfUnderlyingPropertyOfInlineClass() = + this is PropertyGetterDescriptor && correspondingProperty.isUnderlyingPropertyOfInlineClass() + fun VariableDescriptor.isUnderlyingPropertyOfInlineClass(): Boolean { val containingDeclaration = this.containingDeclaration if (!containingDeclaration.isInlineClass()) return false 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 9ddd9cd030f..fb93355d3b3 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.isUnderlyingPropertyOfInlineClass import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor import org.jetbrains.kotlin.types.TypeUtils import java.lang.reflect.Field @@ -226,9 +227,20 @@ private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Bool } when { - accessor == null -> computeFieldCaller( - property.javaField ?: throw KotlinReflectionInternalError("No accessors or field is found for property $property") - ) + accessor == null -> { + if (property.descriptor.isUnderlyingPropertyOfInlineClass() && + property.descriptor.visibility == Visibilities.INTERNAL + ) { + val unboxMethod = property.descriptor.containingDeclaration.toInlineClass()?.getUnboxMethod(property.descriptor) + ?: throw KotlinReflectionInternalError("Underlying property of inline class $property should have a field") + if (isBound) InternalUnderlyingValOfInlineClass.Bound(unboxMethod, boundReceiver) + else InternalUnderlyingValOfInlineClass.Unbound(unboxMethod) + } else { + val javaField = property.javaField + ?: throw KotlinReflectionInternalError("No accessors or field is found for property $property") + computeFieldCaller(javaField) + } + } !Modifier.isStatic(accessor.modifiers) -> if (isBound) CallerImpl.Method.BoundInstance(accessor, boundReceiver) else CallerImpl.Method.Instance(accessor) 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 4ac4b7fb8e2..de6958f5085 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 @@ -5,14 +5,13 @@ package kotlin.reflect.jvm.internal.calls -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.isGetterOfUnderlyingPropertyOfInlineClass import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.resolve.isInlineClassType +import org.jetbrains.kotlin.resolve.isUnderlyingPropertyOfInlineClass import org.jetbrains.kotlin.types.KotlinType import java.lang.reflect.Member import java.lang.reflect.Method @@ -24,9 +23,9 @@ import kotlin.reflect.jvm.internal.toJavaClass * A caller that is used whenever the declaration has inline classes in its parameter types or return type. * Each argument of an inline class type is unboxed, and the return value (if it's of an inline class type) is boxed. */ -internal class InlineClassAwareCaller( +internal class InlineClassAwareCaller( private val descriptor: CallableMemberDescriptor, - private val caller: CallerImpl, + private val caller: Caller, private val isDefault: Boolean ) : Caller { override val member: M @@ -45,6 +44,15 @@ internal class InlineClassAwareCaller( } private val data: BoxUnboxData by lazy(LazyThreadSafetyMode.PUBLICATION) { + val box = descriptor.returnType!!.toInlineClass()?.getBoxMethod(descriptor) + + if (descriptor.isGetterOfUnderlyingPropertyOfInlineClass()) { + // Getter of the underlying val of an inline class is always called on a boxed receiver, + // no argument boxing/unboxing is required. + // However, its result might require boxing if it is an inline class type. + return@lazy BoxUnboxData(IntRange.EMPTY, emptyArray(), box) + } + val shift = when { caller is CallerImpl.Method.BoundStatic -> { // Bound reference to a static method is only possible for a top level extension function/property, @@ -107,8 +115,6 @@ internal class InlineClassAwareCaller( } else null } - val box = descriptor.returnType!!.toInlineClass()?.getBoxMethod() - BoxUnboxData(argumentRange, unbox, box) } @@ -130,20 +136,15 @@ internal class InlineClassAwareCaller( return box?.invoke(null, result) ?: result } - - private fun Class<*>.getBoxMethod(): Method = try { - getDeclaredMethod("box" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS, getUnboxMethod(descriptor).returnType) - } catch (e: NoSuchMethodException) { - throw KotlinReflectionInternalError("No box method found in inline class: $this (calling $descriptor)") - } } -internal fun CallerImpl.createInlineClassAwareCallerIfNeeded( +internal fun Caller.createInlineClassAwareCallerIfNeeded( descriptor: CallableMemberDescriptor, isDefault: Boolean = false ): Caller { - val needsInlineAwareCaller = - descriptor.valueParameters.any { it.type.isInlineClassType() } || + val needsInlineAwareCaller: Boolean = + descriptor.isGetterOfUnderlyingPropertyOfInlineClass() || + descriptor.valueParameters.any { it.type.isInlineClassType() } || descriptor.returnType?.isInlineClassType() == true || this !is BoundCaller && descriptor.hasInlineClassReceiver() @@ -153,22 +154,28 @@ internal fun CallerImpl.createInlineClassAwareCallerIfNeeded( private fun CallableMemberDescriptor.hasInlineClassReceiver() = expectedReceiverType?.isInlineClassType() == true -internal fun Class<*>.getUnboxMethod(descriptor: CallableMemberDescriptor): Method = try { - getDeclaredMethod("unbox" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS) -} catch (e: NoSuchMethodException) { - throw KotlinReflectionInternalError("No unbox method found in inline class: $this (calling $descriptor)") -} - -internal fun KotlinType.toInlineClass(): Class<*>? { - val descriptor = constructor.declarationDescriptor - if (descriptor is ClassDescriptor && descriptor.isInline) { - return descriptor.toJavaClass() ?: throw KotlinReflectionInternalError( - "Class object for the class ${descriptor.name} cannot be found (classId=${descriptor.classId})" - ) +internal fun Class<*>.getUnboxMethod(descriptor: CallableMemberDescriptor): Method = + try { + getDeclaredMethod("unbox" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS) + } catch (e: NoSuchMethodException) { + throw KotlinReflectionInternalError("No unbox method found in inline class: $this (calling $descriptor)") } - return null -} +internal fun Class<*>.getBoxMethod(descriptor: CallableMemberDescriptor): Method = + try { + getDeclaredMethod("box" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS, getUnboxMethod(descriptor).returnType) + } catch (e: NoSuchMethodException) { + throw KotlinReflectionInternalError("No box method found in inline class: $this (calling $descriptor)") + } + +internal fun KotlinType.toInlineClass(): Class<*>? = + constructor.declarationDescriptor.toInlineClass() + +internal fun DeclarationDescriptor?.toInlineClass(): Class<*>? = + if (this is ClassDescriptor && isInline) + toJavaClass() ?: throw KotlinReflectionInternalError("Class object for the class $name cannot be found (classId=$classId)") + else + null private val CallableMemberDescriptor.expectedReceiverType: KotlinType? get() { @@ -183,6 +190,8 @@ private val CallableMemberDescriptor.expectedReceiverType: KotlinType? } internal fun Any?.coerceToExpectedReceiverType(descriptor: CallableMemberDescriptor): Any? { + if (descriptor is PropertyDescriptor && descriptor.isUnderlyingPropertyOfInlineClass()) return this + val expectedReceiverType = descriptor.expectedReceiverType val unboxMethod = expectedReceiverType?.toInlineClass()?.getUnboxMethod(descriptor) ?: return this diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InternalUnderlyingValOfInlineClass.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InternalUnderlyingValOfInlineClass.kt new file mode 100644 index 00000000000..05ac0527507 --- /dev/null +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/InternalUnderlyingValOfInlineClass.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.reflect.jvm.internal.calls + +import java.lang.reflect.Type +import kotlin.reflect.jvm.internal.calls.CallerImpl.Companion.dropFirst +import java.lang.reflect.Method as ReflectMethod + +internal sealed class InternalUnderlyingValOfInlineClass( + private val unboxMethod: ReflectMethod, + final override val parameterTypes: List +) : Caller { + + final override val member: ReflectMethod? get() = null + + final override val returnType: Type = + unboxMethod.returnType + + protected fun callMethod(instance: Any?, args: Array<*>): Any? { + return unboxMethod.invoke(instance, *args) + } + + class Unbound( + unboxMethod: ReflectMethod + ) : InternalUnderlyingValOfInlineClass(unboxMethod, listOf(unboxMethod.declaringClass)) { + override fun call(args: Array<*>): Any? { + checkArguments(args) + return callMethod(args[0], args.dropFirst()) + } + } + + class Bound( + unboxMethod: ReflectMethod, + private val boundReceiver: Any? + ) : InternalUnderlyingValOfInlineClass(unboxMethod, emptyList()), BoundCaller { + override fun call(args: Array<*>): Any? { + checkArguments(args) + return callMethod(boundReceiver, args) + } + } +} 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 8bfd2815a9c..52004b81b1a 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 @@ -16843,6 +16843,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/inlineClassConstructor.kt"); } + @TestMetadata("internalPrimaryValOfInlineClass.kt") + public void testInternalPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt"); + } + @TestMetadata("jvmStaticFieldInObject.kt") public void testJvmStaticFieldInObject() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/jvmStaticFieldInObject.kt"); @@ -16873,6 +16878,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/overridingVarOfInlineClass.kt"); } + @TestMetadata("primaryValOfInlineClass.kt") + public void testPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt"); + } + @TestMetadata("properties.kt") public void testProperties() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/properties.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 72026922cc8..93a57fbb763 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 @@ -17888,6 +17888,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/inlineClassConstructor.kt"); } + @TestMetadata("internalPrimaryValOfInlineClass.kt") + public void testInternalPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/internalPrimaryValOfInlineClass.kt"); + } + @TestMetadata("jvmStaticFieldInObject.kt") public void testJvmStaticFieldInObject() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/jvmStaticFieldInObject.kt"); @@ -17918,6 +17923,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/overridingVarOfInlineClass.kt"); } + @TestMetadata("primaryValOfInlineClass.kt") + public void testPrimaryValOfInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/primaryValOfInlineClass.kt"); + } + @TestMetadata("properties.kt") public void testProperties() throws Exception { runTest("compiler/testData/codegen/box/reflection/call/inlineClasses/properties.kt");