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");