Implement proper contract for generated java.lang.Annotation.hashCode()
#KT-48606 Fixed
This commit is contained in:
@@ -455,6 +455,11 @@ class IrBuiltInsOverFir(
|
|||||||
it.owner.name == OperatorNameConventions.TIMES && it.owner.valueParameters[0].type == intType
|
it.owner.name == OperatorNameConventions.TIMES && it.owner.valueParameters[0].type == intType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val intXorSymbol: IrSimpleFunctionSymbol
|
||||||
|
get() = intClass.functions.single {
|
||||||
|
it.owner.name == OperatorNameConventions.XOR && it.owner.valueParameters[0].type == intType
|
||||||
|
}
|
||||||
|
|
||||||
override val extensionToString: IrSimpleFunctionSymbol by lazy {
|
override val extensionToString: IrSimpleFunctionSymbol by lazy {
|
||||||
findFunctions(kotlinPackage, Name.identifier("toString")).first { function ->
|
findFunctions(kotlinPackage, Name.identifier("toString")).first { function ->
|
||||||
function.owner.extensionReceiverParameter?.let { receiver -> receiver.type == anyNType } ?: false
|
function.owner.extensionReceiverParameter?.let { receiver -> receiver.type == anyNType } ?: false
|
||||||
|
|||||||
+6
@@ -419,6 +419,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt");
|
runTest("compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("annotationJvmHashCode.kt")
|
||||||
|
public void testAnnotationJvmHashCode() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("annotationToString.kt")
|
@TestMetadata("annotationToString.kt")
|
||||||
public void testAnnotationToString() throws Exception {
|
public void testAnnotationToString() throws Exception {
|
||||||
|
|||||||
+55
-14
@@ -13,19 +13,19 @@ import org.jetbrains.kotlin.backend.common.ir.copyTo
|
|||||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.isArray
|
|
||||||
import org.jetbrains.kotlin.ir.types.isKClass
|
import org.jetbrains.kotlin.ir.types.isKClass
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
@@ -183,18 +183,13 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val
|
|||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
fun implementEqualsAndHashCode(annotationClass: IrClass, implClass: IrClass, originalProps: List<IrProperty>, childProps: List<IrProperty>) {
|
fun implementEqualsAndHashCode(annotationClass: IrClass, implClass: IrClass, originalProps: List<IrProperty>, childProps: List<IrProperty>) {
|
||||||
val creator = MethodsFromAnyGeneratorForLowerings(context, implClass, ANNOTATION_IMPLEMENTATION)
|
val creator = MethodsFromAnyGeneratorForLowerings(context, implClass, ANNOTATION_IMPLEMENTATION)
|
||||||
val generator =
|
val generator = AnnotationImplementationMemberGenerator(
|
||||||
creator.LoweringDataClassMemberGenerator(
|
context, implClass,
|
||||||
nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(),
|
nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(),
|
||||||
typeForEquals = annotationClass.defaultType
|
) { type, a, b ->
|
||||||
) { type, a, b ->
|
generatedEquals(this, type, a, b)
|
||||||
generatedEquals(this, type, a, b)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Manual implementation of equals is required for two reasons:
|
|
||||||
// 1. `other` should be casted to interface instead of implementation
|
|
||||||
// 2. Properties should be retrieved using getters without accessing backing fields
|
|
||||||
// (DataClassMembersGenerator typically tries to access fields)
|
|
||||||
val eqFun = creator.createEqualsMethodDeclaration()
|
val eqFun = creator.createEqualsMethodDeclaration()
|
||||||
generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, originalProps)
|
generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, originalProps)
|
||||||
|
|
||||||
@@ -208,3 +203,49 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val
|
|||||||
open fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) {}
|
open fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AnnotationImplementationMemberGenerator(
|
||||||
|
backendContext: BackendContext,
|
||||||
|
irClass: IrClass,
|
||||||
|
val nameForToString: String,
|
||||||
|
val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression,
|
||||||
|
) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION) {
|
||||||
|
|
||||||
|
override fun IrClass.classNameForToString(): String = nameForToString
|
||||||
|
|
||||||
|
// From https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Annotation.html#equals-java.lang.Object-
|
||||||
|
// ---
|
||||||
|
// The hash code of an annotation is the sum of the hash codes of its members (including those with default values), as defined below:
|
||||||
|
// The hash code of an annotation member is (127 times the hash code of the member-name as computed by String.hashCode()) XOR the hash code of the member-value
|
||||||
|
override fun IrBuilderWithScope.shiftResultOfHashCode(irResultVar: IrVariable): IrExpression = irGet(irResultVar) // no default (* 31)
|
||||||
|
|
||||||
|
override fun getHashCodeOf(builder: IrBuilderWithScope, property: IrProperty, irValue: IrExpression): IrExpression = with(builder) {
|
||||||
|
val propertyValueHashCode = getHashCodeOf(property.backingField!!.type, irValue)
|
||||||
|
val propertyNameHashCode = getHashCodeOf(backendContext.irBuiltIns.stringType, irString(property.name.toString()))
|
||||||
|
val multiplied = irCallOp(context.irBuiltIns.intTimesSymbol, context.irBuiltIns.intType, propertyNameHashCode, irInt(127))
|
||||||
|
return irCallOp(context.irBuiltIns.intXorSymbol, context.irBuiltIns.intType, multiplied, propertyValueHashCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manual implementation of equals is required for two reasons:
|
||||||
|
// 1. `other` should be casted to interface instead of implementation
|
||||||
|
// 2. Properties should be retrieved using getters without accessing backing fields
|
||||||
|
// (DataClassMembersGenerator typically tries to access fields)
|
||||||
|
fun generateEqualsUsingGetters(equalsFun: IrSimpleFunction, typeForEquals: IrType, properties: List<IrProperty>) = equalsFun.apply {
|
||||||
|
body = backendContext.createIrBuilder(symbol).irBlockBody {
|
||||||
|
val irType = typeForEquals
|
||||||
|
fun irOther() = irGet(valueParameters[0])
|
||||||
|
fun irThis() = irGet(dispatchReceiverParameter!!)
|
||||||
|
fun IrProperty.get(receiver: IrExpression) = irCall(getter!!).apply {
|
||||||
|
dispatchReceiver = receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
+irIfThenReturnFalse(irNotIs(irOther(), irType))
|
||||||
|
val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast")
|
||||||
|
for (property in properties) {
|
||||||
|
val arg1 = property.get(irThis())
|
||||||
|
val arg2 = property.get(irGet(irType, otherWithCast.symbol))
|
||||||
|
+irIfThenReturnFalse(irNot(selectEquals(property.getter?.returnType ?: property.backingField!!.type, arg1, arg2)))
|
||||||
|
}
|
||||||
|
+irReturnTrue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+50
-67
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.common.lower
|
package org.jetbrains.kotlin.backend.common.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isHashCode
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
@@ -38,71 +39,6 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla
|
|||||||
addValueParameter("other", context.irBuiltIns.anyNType)
|
addValueParameter("other", context.irBuiltIns.anyNType)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class LoweringDataClassMemberGenerator(
|
|
||||||
val nameForToString: String,
|
|
||||||
val typeForEquals: IrType,
|
|
||||||
val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression,
|
|
||||||
) :
|
|
||||||
DataClassMembersGenerator(
|
|
||||||
IrGeneratorContextBase(context.irBuiltIns),
|
|
||||||
context.ir.symbols.externalSymbolTable,
|
|
||||||
irClass,
|
|
||||||
origin
|
|
||||||
) {
|
|
||||||
|
|
||||||
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction {
|
|
||||||
error("Descriptor API shouldn't be used in lowerings")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) {
|
|
||||||
// no-op — irFunction from lowering should already have necessary parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? {
|
|
||||||
error("Descriptor API shouldn't be used in lowerings")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType {
|
|
||||||
error("Descriptor API shouldn't be used in lowerings")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo {
|
|
||||||
val symbol = if (type.isArray() || type.isPrimitiveArray()) {
|
|
||||||
context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
|
|
||||||
} else {
|
|
||||||
type.classOrNull?.functions?.singleOrNull { it.owner.isHashCode() } ?:
|
|
||||||
context.irBuiltIns.anyClass.functions.single { it.owner.name.asString() == "hashCode" }
|
|
||||||
}
|
|
||||||
return object : HashCodeFunctionInfo {
|
|
||||||
override val symbol: IrSimpleFunctionSymbol = symbol
|
|
||||||
|
|
||||||
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun IrClass.classNameForToString(): String = nameForToString
|
|
||||||
|
|
||||||
fun generateEqualsUsingGetters(equalsFun: IrSimpleFunction, typeForEquals: IrType, properties: List<IrProperty>) = equalsFun.apply {
|
|
||||||
body = this@MethodsFromAnyGeneratorForLowerings.context.createIrBuilder(symbol).irBlockBody {
|
|
||||||
val irType = typeForEquals
|
|
||||||
fun irOther() = irGet(valueParameters[0])
|
|
||||||
fun irThis() = irGet(dispatchReceiverParameter!!)
|
|
||||||
fun IrProperty.get(receiver: IrExpression) = irCall(getter!!).apply {
|
|
||||||
dispatchReceiver = receiver
|
|
||||||
}
|
|
||||||
|
|
||||||
+irIfThenReturnFalse(irNotIs(irOther(), irType))
|
|
||||||
val otherWithCast = irTemporary(irAs(irOther(), irType), "other_with_cast")
|
|
||||||
for (property in properties) {
|
|
||||||
val arg1 = property.get(irThis())
|
|
||||||
val arg2 = property.get(irGet(irType, otherWithCast.symbol))
|
|
||||||
+irIfThenReturnFalse(irNot(selectEquals(property.getter?.returnType ?: property.backingField!!.type, arg1, arg2)))
|
|
||||||
}
|
|
||||||
+irReturnTrue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun IrFunction.isToString(): Boolean =
|
fun IrFunction.isToString(): Boolean =
|
||||||
name.asString() == "toString" && extensionReceiverParameter == null && valueParameters.isEmpty()
|
name.asString() == "toString" && extensionReceiverParameter == null && valueParameters.isEmpty()
|
||||||
@@ -118,6 +54,53 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla
|
|||||||
|
|
||||||
fun IrClass.collectOverridenSymbols(predicate: (IrFunction) -> Boolean): List<IrSimpleFunctionSymbol> =
|
fun IrClass.collectOverridenSymbols(predicate: (IrFunction) -> Boolean): List<IrSimpleFunctionSymbol> =
|
||||||
superTypes.mapNotNull { it.getClass()?.functions?.singleOrNull(predicate)?.symbol }
|
superTypes.mapNotNull { it.getClass()?.functions?.singleOrNull(predicate)?.symbol }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class LoweringDataClassMemberGenerator(
|
||||||
|
val backendContext: BackendContext,
|
||||||
|
irClass: IrClass,
|
||||||
|
origin: IrDeclarationOrigin
|
||||||
|
) :
|
||||||
|
DataClassMembersGenerator(
|
||||||
|
IrGeneratorContextBase(backendContext.irBuiltIns),
|
||||||
|
backendContext.ir.symbols.externalSymbolTable,
|
||||||
|
irClass,
|
||||||
|
origin
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction {
|
||||||
|
error("Descriptor API shouldn't be used in lowerings")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generateSyntheticFunctionParameterDeclarations(irFunction: IrFunction) {
|
||||||
|
// no-op — irFunction from lowering should already have necessary parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getProperty(parameter: ValueParameterDescriptor?, irValueParameter: IrValueParameter?): IrProperty? {
|
||||||
|
error("Descriptor API shouldn't be used in lowerings")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun transform(typeParameterDescriptor: TypeParameterDescriptor): IrType {
|
||||||
|
error("Descriptor API shouldn't be used in lowerings")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHashCodeFunctionInfo(type: IrType): HashCodeFunctionInfo {
|
||||||
|
val symbol = if (type.isArray() || type.isPrimitiveArray()) {
|
||||||
|
context.irBuiltIns.dataClassArrayMemberHashCodeSymbol
|
||||||
|
} else {
|
||||||
|
type.classOrNull?.functions?.singleOrNull { it.owner.isHashCode() }
|
||||||
|
?: context.irBuiltIns.anyClass.functions.single { it.owner.name.asString() == "hashCode" }
|
||||||
|
}
|
||||||
|
return object : HashCodeFunctionInfo {
|
||||||
|
override val symbol: IrSimpleFunctionSymbol = symbol
|
||||||
|
|
||||||
|
override fun commitSubstituted(irMemberAccessExpression: IrMemberAccessExpression<*>) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ abstract class IrBuiltIns {
|
|||||||
|
|
||||||
abstract val intPlusSymbol: IrSimpleFunctionSymbol
|
abstract val intPlusSymbol: IrSimpleFunctionSymbol
|
||||||
abstract val intTimesSymbol: IrSimpleFunctionSymbol
|
abstract val intTimesSymbol: IrSimpleFunctionSymbol
|
||||||
|
abstract val intXorSymbol: IrSimpleFunctionSymbol
|
||||||
|
|
||||||
abstract val extensionToString: IrSimpleFunctionSymbol
|
abstract val extensionToString: IrSimpleFunctionSymbol
|
||||||
abstract val stringPlus: IrSimpleFunctionSymbol
|
abstract val stringPlus: IrSimpleFunctionSymbol
|
||||||
|
|||||||
+6
-4
@@ -32,10 +32,7 @@ import org.jetbrains.kotlin.ir.types.*
|
|||||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeBuilder
|
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeBuilder
|
||||||
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
||||||
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
|
||||||
import org.jetbrains.kotlin.ir.util.functions
|
|
||||||
import org.jetbrains.kotlin.ir.util.referenceClassifier
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
@@ -387,6 +384,11 @@ class IrBuiltInsOverDescriptors(
|
|||||||
KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int)
|
KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int)
|
||||||
}.let { symbolTable.referenceSimpleFunction(it) }
|
}.let { symbolTable.referenceSimpleFunction(it) }
|
||||||
|
|
||||||
|
override val intXorSymbol: IrSimpleFunctionSymbol =
|
||||||
|
builtIns.int.unsubstitutedMemberScope.findFirstFunction("xor") {
|
||||||
|
KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int)
|
||||||
|
}.let { symbolTable.referenceSimpleFunction(it) }
|
||||||
|
|
||||||
override val intPlusSymbol: IrSimpleFunctionSymbol =
|
override val intPlusSymbol: IrSimpleFunctionSymbol =
|
||||||
builtIns.int.unsubstitutedMemberScope.findFirstFunction("plus") {
|
builtIns.int.unsubstitutedMemberScope.findFirstFunction("plus") {
|
||||||
KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int)
|
KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int)
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
|||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.mapTypeParameters
|
import org.jetbrains.kotlin.ir.expressions.mapTypeParameters
|
||||||
import org.jetbrains.kotlin.ir.expressions.mapValueParameters
|
import org.jetbrains.kotlin.ir.expressions.mapValueParameters
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||||
@@ -160,7 +162,7 @@ abstract class DataClassMembersGenerator(
|
|||||||
+irResultVar
|
+irResultVar
|
||||||
|
|
||||||
for (property in properties.drop(1)) {
|
for (property in properties.drop(1)) {
|
||||||
val shiftedResult = irCallOp(context.irBuiltIns.intTimesSymbol, irIntType, irGet(irResultVar), irInt(31))
|
val shiftedResult = shiftResultOfHashCode(irResultVar)
|
||||||
val irRhs = irCallOp(context.irBuiltIns.intPlusSymbol, irIntType, shiftedResult, getHashCodeOfProperty(property))
|
val irRhs = irCallOp(context.irBuiltIns.intPlusSymbol, irIntType, shiftedResult, getHashCodeOfProperty(property))
|
||||||
+irSet(irResultVar.symbol, irRhs)
|
+irSet(irResultVar.symbol, irRhs)
|
||||||
}
|
}
|
||||||
@@ -175,30 +177,10 @@ abstract class DataClassMembersGenerator(
|
|||||||
context.irBuiltIns.intType,
|
context.irBuiltIns.intType,
|
||||||
irGetProperty(irThis(), property),
|
irGetProperty(irThis(), property),
|
||||||
irInt(0),
|
irInt(0),
|
||||||
getHashCodeOf(property, irGetProperty(irThis(), property))
|
getHashCodeOf(this, property, irGetProperty(irThis(), property))
|
||||||
)
|
)
|
||||||
else ->
|
else ->
|
||||||
getHashCodeOf(property, irGetProperty(irThis(), property))
|
getHashCodeOf(this, property, irGetProperty(irThis(), property))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getHashCodeOf(property: IrProperty, irValue: IrExpression): IrExpression {
|
|
||||||
val hashCodeFunctionInfo = getHashCodeFunctionInfo(property.backingField!!.type)
|
|
||||||
val hashCodeFunctionSymbol = hashCodeFunctionInfo.symbol
|
|
||||||
|
|
||||||
val hasDispatchReceiver = hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null
|
|
||||||
return irCall(
|
|
||||||
hashCodeFunctionSymbol,
|
|
||||||
context.irBuiltIns.intType,
|
|
||||||
valueArgumentsCount = if (hasDispatchReceiver) 0 else 1,
|
|
||||||
typeArgumentsCount = 0
|
|
||||||
).apply {
|
|
||||||
if (hasDispatchReceiver) {
|
|
||||||
dispatchReceiver = irValue
|
|
||||||
} else {
|
|
||||||
putValueArgument(0, irValue)
|
|
||||||
}
|
|
||||||
hashCodeFunctionInfo.commitSubstituted(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +212,32 @@ abstract class DataClassMembersGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun IrBuilderWithScope.shiftResultOfHashCode(irResultVar: IrVariable): IrExpression =
|
||||||
|
irCallOp(context.irBuiltIns.intTimesSymbol, context.irBuiltIns.intType, irGet(irResultVar), irInt(31))
|
||||||
|
|
||||||
|
protected open fun getHashCodeOf(builder: IrBuilderWithScope, property: IrProperty, irValue: IrExpression) =
|
||||||
|
builder.getHashCodeOf(property.backingField!!.type, irValue)
|
||||||
|
|
||||||
|
protected fun IrBuilderWithScope.getHashCodeOf(type: IrType, irValue: IrExpression): IrExpression {
|
||||||
|
val hashCodeFunctionInfo = getHashCodeFunctionInfo(type)
|
||||||
|
val hashCodeFunctionSymbol = hashCodeFunctionInfo.symbol
|
||||||
|
val hasDispatchReceiver = hashCodeFunctionSymbol.descriptor.dispatchReceiverParameter != null
|
||||||
|
return irCall(
|
||||||
|
hashCodeFunctionSymbol,
|
||||||
|
context.irBuiltIns.intType,
|
||||||
|
valueArgumentsCount = if (hasDispatchReceiver) 0 else 1,
|
||||||
|
typeArgumentsCount = 0
|
||||||
|
).apply {
|
||||||
|
if (hasDispatchReceiver) {
|
||||||
|
dispatchReceiver = irValue
|
||||||
|
} else {
|
||||||
|
putValueArgument(0, irValue)
|
||||||
|
}
|
||||||
|
hashCodeFunctionInfo.commitSubstituted(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getIrProperty(property: PropertyDescriptor): IrProperty =
|
fun getIrProperty(property: PropertyDescriptor): IrProperty =
|
||||||
irPropertiesByDescriptor[property]
|
irPropertiesByDescriptor[property]
|
||||||
?: throw AssertionError("Class: ${irClass.descriptor}: unexpected property descriptor: $property")
|
?: throw AssertionError("Class: ${irClass.descriptor}: unexpected property descriptor: $property")
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ annotation class Foo(
|
|||||||
val bar: Bar
|
val bar: Bar
|
||||||
)
|
)
|
||||||
|
|
||||||
data class BarLike(val i:Int, val s: String, val f: Float)
|
fun makeHC(name: String, value: Any) = (127 * name.hashCode()) xor value.hashCode()
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val foo1 = Foo(42, "foo", arrayOf("a", "b"), intArrayOf(1,2), Bar::class, Bar(10, "bar", Float.NaN))
|
val foo1 = Foo(42, "foo", arrayOf("a", "b"), intArrayOf(1,2), Bar::class, Bar(10, "bar", Float.NaN))
|
||||||
val foo2 = Foo(42, "foo", arrayOf("a", "b"), intArrayOf(1,2), Bar::class, Bar(10, "bar", Float.NaN))
|
val foo2 = Foo(42, "foo", arrayOf("a", "b"), intArrayOf(1,2), Bar::class, Bar(10, "bar", Float.NaN))
|
||||||
if (foo1 != foo2) return "Failed equals ${foo1.toString()} ${foo2.toString()}"
|
if (foo1 != foo2) return "Failed equals ${foo1.toString()} ${foo2.toString()}"
|
||||||
val barlike = BarLike(10, "bar", Float.NaN)
|
val barlike = makeHC("i", 10) + makeHC("s", "bar") + makeHC("f", Float.NaN)
|
||||||
if (barlike.hashCode() != foo1.bar.hashCode()) return "Failed HC1"
|
if (barlike != foo1.bar.hashCode()) return "Failed HC1"
|
||||||
if (barlike.hashCode() != foo2.bar.hashCode()) return "Failed HC2"
|
if (barlike != foo2.bar.hashCode()) return "Failed HC2"
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// !LANGUAGE: +InstantiationOfAnnotationClasses
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue as assert
|
||||||
|
|
||||||
|
annotation class ZeroArg()
|
||||||
|
|
||||||
|
annotation class OneArg(val arg: String)
|
||||||
|
|
||||||
|
annotation class ManyArg(val i: Int, val o: OneArg, val z: Boolean, val k: KClass<*>, val e: IntArray)
|
||||||
|
|
||||||
|
@ZeroArg
|
||||||
|
@OneArg("a")
|
||||||
|
@ManyArg(42, OneArg("b"), true, OneArg::class, intArrayOf(1, 2, 3))
|
||||||
|
class Target
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val reflectiveZero = Target::class.java.getAnnotation(ZeroArg::class.java)
|
||||||
|
val reflectiveOne = Target::class.java.getAnnotation(OneArg::class.java)
|
||||||
|
val reflectiveMany = Target::class.java.getAnnotation(ManyArg::class.java)
|
||||||
|
|
||||||
|
val createdZero = ZeroArg()
|
||||||
|
val createdOne = OneArg("a")
|
||||||
|
val createdMany = ManyArg(42, OneArg("b"), true, OneArg::class, intArrayOf(1, 2, 3))
|
||||||
|
|
||||||
|
assertEquals(reflectiveZero.hashCode(), createdZero.hashCode(), "zero")
|
||||||
|
assertEquals(reflectiveOne.hashCode(), createdOne.hashCode(), "one")
|
||||||
|
assertEquals(reflectiveMany.hashCode(), createdMany.hashCode(), "many")
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+6
@@ -419,6 +419,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt");
|
runTest("compiler/testData/codegen/box/annotations/instances/annotationInstancesEmptyDefault.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("annotationJvmHashCode.kt")
|
||||||
|
public void testAnnotationJvmHashCode() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("annotationToString.kt")
|
@TestMetadata("annotationToString.kt")
|
||||||
public void testAnnotationToString() throws Exception {
|
public void testAnnotationToString() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user