JVM IR: Generate equals-impl0 method for inline classes

This commit is contained in:
Steven Schäfer
2019-08-22 11:48:58 +02:00
committed by Alexander Udalov
parent e4456127e4
commit b85a475358
4 changed files with 59 additions and 10 deletions
@@ -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? =
@@ -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
}
}
@@ -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<IrClass, IrSimpleFunction>()
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 {
@@ -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"
}