diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ErasedInlineClassBodyCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ErasedInlineClassBodyCodegen.kt index c76158f461e..75162a7523e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ErasedInlineClassBodyCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ErasedInlineClassBodyCodegen.kt @@ -77,6 +77,7 @@ class ErasedInlineClassBodyCodegen( generateUnboxMethod() generateFunctionsFromAny() + generateSpecializedEqualsStub() } private fun generateFunctionsFromAny() { @@ -116,6 +117,31 @@ class ErasedInlineClassBodyCodegen( ) } + private fun generateSpecializedEqualsStub() { + val specializedEqualsDescriptor = InlineClassDescriptorResolver.createSpecializedEqualsDescriptor(descriptor) ?: return + + functionCodegen.generateMethod( + Synthetic(null, specializedEqualsDescriptor), specializedEqualsDescriptor, object : FunctionGenerationStrategy.CodegenBased(state) { + override fun mapMethodSignature( + functionDescriptor: FunctionDescriptor, + typeMapper: KotlinTypeMapper, + contextKind: OwnerKind, + hasSpecialBridge: Boolean + ): JvmMethodGenericSignature { + // we shouldn't use default mapping here to avoid adding parameter that relates to carrier type + return typeMapper.mapSignatureForSpecializedEqualsOfInlineClass(functionDescriptor) + } + + + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { + val iv = codegen.v + iv.aconst(null) + iv.athrow() + } + } + ) + } + override fun generateKotlinMetadataAnnotation() { writeSyntheticClassMetadata(v, state) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index 6a78395dafa..4b1952f8b2a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -1162,6 +1162,11 @@ public class KotlinTypeMapper { return mapSignature(f, OwnerKind.IMPLEMENTATION, true); } + @NotNull + public JvmMethodGenericSignature mapSignatureForSpecializedEqualsOfInlineClass(@NotNull FunctionDescriptor f) { + return mapSignature(f, OwnerKind.IMPLEMENTATION, true); + } + @NotNull public JvmMethodSignature mapSignatureSkipGeneric(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind) { return mapSignature(f, kind, true); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt index ee497c0b092..ff802a73624 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns object InlineClassDescriptorResolver { @JvmField @@ -18,8 +19,13 @@ object InlineClassDescriptorResolver { @JvmField val UNBOX_METHOD_NAME = Name.identifier("unbox") + private val SPECIALIZED_EQUALS_NAME = Name.identifier("equals--impl") + private val BOXING_VALUE_PARAMETER_NAME = Name.identifier("v") + private val SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME = Name.identifier("p1") + private val SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME = Name.identifier("p2") + fun createBoxFunctionDescriptor(owner: ClassDescriptor): SimpleFunctionDescriptor? { return createConversionFunctionDescriptor(true, owner) } @@ -27,6 +33,30 @@ object InlineClassDescriptorResolver { fun createUnboxFunctionDescriptor(owner: ClassDescriptor): SimpleFunctionDescriptor? = createConversionFunctionDescriptor(false, owner) + fun createSpecializedEqualsDescriptor(owner: ClassDescriptor): SimpleFunctionDescriptor? { + val inlinedValue = owner.underlyingRepresentation() ?: return null + + val functionDescriptor = SimpleFunctionDescriptorImpl.create( + owner, + Annotations.EMPTY, + SPECIALIZED_EQUALS_NAME, + CallableMemberDescriptor.Kind.SYNTHESIZED, + SourceElement.NO_SOURCE + ) + + functionDescriptor.initialize( + null, + null, + emptyList(), + createValueParametersForSpecializedEquals(functionDescriptor, inlinedValue), + owner.builtIns.booleanType, + Modality.FINAL, + Visibilities.PUBLIC + ) + + return functionDescriptor + } + private fun createConversionFunctionDescriptor( isBoxMethod: Boolean, owner: ClassDescriptor @@ -57,13 +87,32 @@ object InlineClassDescriptorResolver { private fun createValueParameterForBoxing( functionDescriptor: FunctionDescriptor, inlinedValue: ValueParameterDescriptor + ): ValueParameterDescriptorImpl { + return createValueParameter(functionDescriptor, inlinedValue, BOXING_VALUE_PARAMETER_NAME, 0) + } + + private fun createValueParametersForSpecializedEquals( + functionDescriptor: FunctionDescriptor, + inlinedValue: ValueParameterDescriptor + ): List { + return listOf( + createValueParameter(functionDescriptor, inlinedValue, SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME, 0), + createValueParameter(functionDescriptor, inlinedValue, SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME, 1) + ) + } + + private fun createValueParameter( + functionDescriptor: FunctionDescriptor, + inlinedValue: ValueParameterDescriptor, + name: Name, + index: Int ): ValueParameterDescriptorImpl { return ValueParameterDescriptorImpl( functionDescriptor, null, - 0, + index, Annotations.EMPTY, - BOXING_VALUE_PARAMETER_NAME, + name, inlinedValue.type, false, false, false, null, SourceElement.NO_SOURCE ) diff --git a/compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt b/compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt new file mode 100644 index 00000000000..d3d6f926855 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt @@ -0,0 +1,25 @@ +// WITH_REFLECT +// FULL_JDK +// IGNORE_BACKEND: JVM_IR, JS_IR, JS, NATIVE + +import java.lang.reflect.InvocationTargetException + +inline class Simple(val x: String) { + fun somethingWeird() {} +} + +fun box(): String { + var s = "" + val name = "equals--impl" + val specializedEquals = + Class.forName("Simple\$Erased").getDeclaredMethod("equals--impl", String::class.java, String::class.java) + ?: return "$name not found" + + try { + specializedEquals.invoke(null, "a", "b") + } catch (e: InvocationTargetException) { + return if (e.targetException is NullPointerException) "OK" else "${e.targetException}" + } + + return "Reserved method invoked successfully" +} diff --git a/compiler/testData/codegen/bytecodeListing/inlineClasses/companionObjectInsideInlineClass.txt b/compiler/testData/codegen/bytecodeListing/inlineClasses/companionObjectInsideInlineClass.txt index 175a3671a5f..7e812b1ba33 100644 --- a/compiler/testData/codegen/bytecodeListing/inlineClasses/companionObjectInsideInlineClass.txt +++ b/compiler/testData/codegen/bytecodeListing/inlineClasses/companionObjectInsideInlineClass.txt @@ -12,6 +12,7 @@ static class Foo$Erased { public final static @org.jetbrains.annotations.NotNull method box(p0: int): Foo public static method constructor(p0: int): int public static method equals(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals--impl(p0: int, p1: int): boolean public static method hashCode(p0: int): int public final static method inInlineClass(p0: int): void public static @org.jetbrains.annotations.NotNull method toString(p0: int): java.lang.String diff --git a/compiler/testData/codegen/bytecodeListing/inlineClasses/computablePropertiesInsideInlineClass.txt b/compiler/testData/codegen/bytecodeListing/inlineClasses/computablePropertiesInsideInlineClass.txt index da7a8149850..f1833637ed3 100644 --- a/compiler/testData/codegen/bytecodeListing/inlineClasses/computablePropertiesInsideInlineClass.txt +++ b/compiler/testData/codegen/bytecodeListing/inlineClasses/computablePropertiesInsideInlineClass.txt @@ -4,6 +4,7 @@ static class Foo$Erased { public final static @org.jetbrains.annotations.NotNull method box(p0: int): Foo public static method constructor(p0: int): int public static method equals(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals--impl(p0: int, p1: int): boolean public final static method getAsThis(p0: int): int public final static method getProp(p0: int): int public static method hashCode(p0: int): int diff --git a/compiler/testData/codegen/bytecodeListing/inlineClasses/inlineClassConstructors.txt b/compiler/testData/codegen/bytecodeListing/inlineClasses/inlineClassConstructors.txt index 9e4bfc8e9c6..fcbdfffea44 100644 --- a/compiler/testData/codegen/bytecodeListing/inlineClasses/inlineClassConstructors.txt +++ b/compiler/testData/codegen/bytecodeListing/inlineClasses/inlineClassConstructors.txt @@ -5,6 +5,7 @@ static class Foo$Erased { public static method constructor(p0: int): int public static method constructor(p0: long): int public static method equals(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals--impl(p0: int, p1: int): boolean public static method hashCode(p0: int): int public static @org.jetbrains.annotations.NotNull method toString(p0: int): java.lang.String } diff --git a/compiler/testData/codegen/bytecodeListing/inlineClasses/noBridgesForErasedInlineClass.txt b/compiler/testData/codegen/bytecodeListing/inlineClasses/noBridgesForErasedInlineClass.txt index dc7e18b445e..c9148d51fa3 100644 --- a/compiler/testData/codegen/bytecodeListing/inlineClasses/noBridgesForErasedInlineClass.txt +++ b/compiler/testData/codegen/bytecodeListing/inlineClasses/noBridgesForErasedInlineClass.txt @@ -9,6 +9,7 @@ static class Foo$Erased { public final static @org.jetbrains.annotations.NotNull method box(p0: long): Foo public static method constructor(p0: long): long public static method equals(p0: long, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals--impl(p0: long, p1: long): boolean public static method foo-1e4ch6lh(p0: long, p1: long): void public static method hashCode(p0: long): int public static @org.jetbrains.annotations.NotNull method toString(p0: long): java.lang.String diff --git a/compiler/testData/codegen/bytecodeListing/inlineClasses/shapeOfInlineClassWithPrimitive.txt b/compiler/testData/codegen/bytecodeListing/inlineClasses/shapeOfInlineClassWithPrimitive.txt index dee5e27c9a4..8b59c7512fd 100644 --- a/compiler/testData/codegen/bytecodeListing/inlineClasses/shapeOfInlineClassWithPrimitive.txt +++ b/compiler/testData/codegen/bytecodeListing/inlineClasses/shapeOfInlineClassWithPrimitive.txt @@ -5,6 +5,7 @@ static class Foo$Erased { public static method constructor(p0: long): long public final static method empty(p0: long): void public static method equals(p0: long, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals--impl(p0: long, p1: long): boolean public final static method extension(p0: long, @org.jetbrains.annotations.NotNull p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.String): void public static method hashCode(p0: long): int public final static method param(p0: long, p1: double): void diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 42eb324ac4d..8838e298589 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -11501,6 +11501,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/inlineClasses/callComputablePropertyInsideInlineClass.kt"); } + @TestMetadata("callSpecializedEqualsViaReflection.kt") + public void testCallSpecializedEqualsViaReflection() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt"); + } + @TestMetadata("callSpeciallyOverriddenPropertyOfInlineClass.kt") public void testCallSpeciallyOverriddenPropertyOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/callSpeciallyOverriddenPropertyOfInlineClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 5e7c904bb89..2cb897b0453 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -11501,6 +11501,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/inlineClasses/callComputablePropertyInsideInlineClass.kt"); } + @TestMetadata("callSpecializedEqualsViaReflection.kt") + public void testCallSpecializedEqualsViaReflection() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt"); + } + @TestMetadata("callSpeciallyOverriddenPropertyOfInlineClass.kt") public void testCallSpeciallyOverriddenPropertyOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/callSpeciallyOverriddenPropertyOfInlineClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 6c4c139b851..102fbe3fb1b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -11501,6 +11501,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/inlineClasses/callComputablePropertyInsideInlineClass.kt"); } + @TestMetadata("callSpecializedEqualsViaReflection.kt") + public void testCallSpecializedEqualsViaReflection() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt"); + } + @TestMetadata("callSpeciallyOverriddenPropertyOfInlineClass.kt") public void testCallSpeciallyOverriddenPropertyOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/callSpeciallyOverriddenPropertyOfInlineClass.kt"); 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 880de373cce..25453f30740 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 @@ -10046,6 +10046,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/inlineClasses/callComputablePropertyInsideInlineClass.kt"); } + @TestMetadata("callSpecializedEqualsViaReflection.kt") + public void testCallSpecializedEqualsViaReflection() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt"); + } + @TestMetadata("callableReferencesWithInlineClasses.kt") public void testCallableReferencesWithInlineClasses() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/callableReferencesWithInlineClasses.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 d5e78ce1ae7..087277992c1 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 @@ -11111,6 +11111,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/inlineClasses/callComputablePropertyInsideInlineClass.kt"); } + @TestMetadata("callSpecializedEqualsViaReflection.kt") + public void testCallSpecializedEqualsViaReflection() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt"); + } + @TestMetadata("callableReferencesWithInlineClasses.kt") public void testCallableReferencesWithInlineClasses() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/callableReferencesWithInlineClasses.kt");