From b85a475358163a30470e8ffa481909310f80644f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Sch=C3=A4fer?= Date: Thu, 22 Aug 2019 11:48:58 +0200 Subject: [PATCH] JVM IR: Generate equals-impl0 method for inline classes --- .../resolve/InlineClassDescriptorResolver.kt | 8 ++-- .../jvm/lower/JvmInlineClassLowering.kt | 19 ++++++++++ .../MemoizedInlineClassReplacements.kt | 37 +++++++++++++++++-- .../callSpecializedEqualsViaReflection.kt | 5 +-- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt index 903e9fb9407..0d4a0093b33 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/InlineClassDescriptorResolver.kt @@ -19,12 +19,12 @@ object InlineClassDescriptorResolver { @JvmField val UNBOX_METHOD_NAME = Name.identifier("unbox") - private val SPECIALIZED_EQUALS_NAME = Name.identifier("equals-impl0") + val SPECIALIZED_EQUALS_NAME = Name.identifier("equals-impl0") - private val BOXING_VALUE_PARAMETER_NAME = Name.identifier("v") + 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") + val SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME = Name.identifier("p1") + val SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME = Name.identifier("p2") @JvmStatic fun createBoxFunctionDescriptor(owner: ClassDescriptor): SimpleFunctionDescriptor? = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index 7585f18c8ec..6834d8785ab 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -79,6 +79,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F buildPrimaryInlineClassConstructor(declaration, irConstructor) buildBoxFunction(declaration) buildUnboxFunction(declaration) + buildSpecializedEqualsMethod(declaration) } return declaration @@ -371,4 +372,22 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F irClass.declarations += function } + + private fun buildSpecializedEqualsMethod(irClass: IrClass) { + val function = manager.getSpecializedEqualsMethod(irClass, context.irBuiltIns) + val left = function.valueParameters[0] + val right = function.valueParameters[1] + val type = left.type.unboxInlineClass() + + function.body = context.createIrBuilder(irClass.symbol).run { + irExprBody( + irEquals( + coerceInlineClasses(irGet(left), left.type, type), + coerceInlineClasses(irGet(right), right.type, type) + ) + ) + } + + irClass.declarations += function + } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt index 13a662d1544..5515a414d23 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt @@ -11,18 +11,23 @@ import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi.mangledNameFor +import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildFunWithDescriptorForInlining import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol import org.jetbrains.kotlin.ir.types.getClass +import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl +import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.explicitParameters import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver import org.jetbrains.kotlin.storage.LockBasedStorageManager class IrReplacementFunction( @@ -73,14 +78,14 @@ class MemoizedInlineClassReplacements { storageManager.createMemoizedFunction { irClass -> require(irClass.isInline) buildFun { - name = Name.identifier("box-impl") + name = Name.identifier(KotlinTypeMapper.BOX_JVM_METHOD_NAME) origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER returnType = irClass.defaultType }.apply { parent = irClass copyTypeParametersFrom(irClass) addValueParameter { - name = Name.identifier("v") + name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME type = InlineClassAbi.getUnderlyingType(irClass) } } @@ -94,7 +99,7 @@ class MemoizedInlineClassReplacements { storageManager.createMemoizedFunction { irClass -> require(irClass.isInline) buildFun { - name = Name.identifier("unbox-impl") + name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER returnType = InlineClassAbi.getUnderlyingType(irClass) }.apply { @@ -103,6 +108,32 @@ class MemoizedInlineClassReplacements { } } + private val specializedEqualsCache = storageManager.createCacheWithNotNullValues() + fun getSpecializedEqualsMethod(irClass: IrClass, irBuiltIns: IrBuiltIns): IrSimpleFunction { + require(irClass.isInline) + return specializedEqualsCache.computeIfAbsent(irClass) { + buildFun { + name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME + // TODO: Revisit this once we allow user defined equals methods in inline classes. + origin = JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD + returnType = irBuiltIns.booleanType + }.apply { + parent = irClass + // We ignore type arguments here, since there is no good way to go from type arguments to types in the IR anyway. + val typeArgument = + IrSimpleTypeImpl(null, irClass.symbol, false, List(irClass.typeParameters.size) { IrStarProjectionImpl }, listOf()) + addValueParameter { + name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME + type = typeArgument + } + addValueParameter { + name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME + type = typeArgument + } + } + } + } + private fun createMethodReplacement(function: IrFunction): IrReplacementFunction? { require(function.dispatchReceiverParameter != null && function is IrSimpleFunction) val overrides = function.overriddenSymbols.mapNotNull { diff --git a/compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt b/compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt index 9155ea7330f..c4b87c0bc68 100644 --- a/compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt +++ b/compiler/testData/codegen/box/inlineClasses/callSpecializedEqualsViaReflection.kt @@ -1,6 +1,5 @@ // WITH_REFLECT // FULL_JDK -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM import java.lang.reflect.InvocationTargetException @@ -16,11 +15,11 @@ fun box(): String { Simple::class.java.getDeclaredMethod(name, String::class.java, String::class.java) ?: return "$name not found" - try { + val result = try { specializedEquals.invoke(null, "a", "b") } catch (e: InvocationTargetException) { return if (e.targetException is NullPointerException) "OK" else "${e.targetException}" } - return "Reserved method invoked successfully" + return if (result == false) "OK" else "Fail" }