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
|
||||
}
|
||||
|
||||
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 {
|
||||
findFunctions(kotlinPackage, Name.identifier("toString")).first { function ->
|
||||
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");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationJvmHashCode.kt")
|
||||
public void testAnnotationJvmHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationToString.kt")
|
||||
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.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.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.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
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.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
@@ -183,18 +183,13 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
fun implementEqualsAndHashCode(annotationClass: IrClass, implClass: IrClass, originalProps: List<IrProperty>, childProps: List<IrProperty>) {
|
||||
val creator = MethodsFromAnyGeneratorForLowerings(context, implClass, ANNOTATION_IMPLEMENTATION)
|
||||
val generator =
|
||||
creator.LoweringDataClassMemberGenerator(
|
||||
nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(),
|
||||
typeForEquals = annotationClass.defaultType
|
||||
) { type, a, b ->
|
||||
generatedEquals(this, type, a, b)
|
||||
}
|
||||
val generator = AnnotationImplementationMemberGenerator(
|
||||
context, implClass,
|
||||
nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(),
|
||||
) { 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()
|
||||
generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, originalProps)
|
||||
|
||||
@@ -208,3 +203,49 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val
|
||||
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
|
||||
|
||||
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.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
@@ -38,71 +39,6 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla
|
||||
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 {
|
||||
fun IrFunction.isToString(): Boolean =
|
||||
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> =
|
||||
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 intTimesSymbol: IrSimpleFunctionSymbol
|
||||
abstract val intXorSymbol: IrSimpleFunctionSymbol
|
||||
|
||||
abstract val extensionToString: 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.buildSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
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.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
@@ -387,6 +384,11 @@ class IrBuiltInsOverDescriptors(
|
||||
KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, int)
|
||||
}.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 =
|
||||
builtIns.int.unsubstitutedMemberScope.findFirstFunction("plus") {
|
||||
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.mapTypeParameters
|
||||
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.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
@@ -160,7 +162,7 @@ abstract class DataClassMembersGenerator(
|
||||
+irResultVar
|
||||
|
||||
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))
|
||||
+irSet(irResultVar.symbol, irRhs)
|
||||
}
|
||||
@@ -175,30 +177,10 @@ abstract class DataClassMembersGenerator(
|
||||
context.irBuiltIns.intType,
|
||||
irGetProperty(irThis(), property),
|
||||
irInt(0),
|
||||
getHashCodeOf(property, irGetProperty(irThis(), property))
|
||||
getHashCodeOf(this, property, irGetProperty(irThis(), property))
|
||||
)
|
||||
else ->
|
||||
getHashCodeOf(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)
|
||||
getHashCodeOf(this, property, irGetProperty(irThis(), property))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
irPropertiesByDescriptor[property]
|
||||
?: throw AssertionError("Class: ${irClass.descriptor}: unexpected property descriptor: $property")
|
||||
|
||||
@@ -20,14 +20,14 @@ annotation class Foo(
|
||||
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 {
|
||||
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))
|
||||
if (foo1 != foo2) return "Failed equals ${foo1.toString()} ${foo2.toString()}"
|
||||
val barlike = BarLike(10, "bar", Float.NaN)
|
||||
if (barlike.hashCode() != foo1.bar.hashCode()) return "Failed HC1"
|
||||
if (barlike.hashCode() != foo2.bar.hashCode()) return "Failed HC2"
|
||||
val barlike = makeHC("i", 10) + makeHC("s", "bar") + makeHC("f", Float.NaN)
|
||||
if (barlike != foo1.bar.hashCode()) return "Failed HC1"
|
||||
if (barlike != foo2.bar.hashCode()) return "Failed HC2"
|
||||
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");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationJvmHashCode.kt")
|
||||
public void testAnnotationJvmHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/instances/annotationJvmHashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationToString.kt")
|
||||
public void testAnnotationToString() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user