diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt index 72daf291fe5..30d2aa70029 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AnnotationImplementationTransformer.kt @@ -8,25 +8,19 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext -import org.jetbrains.kotlin.backend.common.deepCopyWithVariables -import org.jetbrains.kotlin.backend.common.ir.copyTo +import org.jetbrains.kotlin.backend.common.ir.addFakeOverrides 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.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.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.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isKClass +import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name @@ -46,7 +40,7 @@ class AnnotationImplementationLowering( } } -open class AnnotationImplementationTransformer(val context: BackendContext, val irFile: IrFile?) : IrElementTransformerVoidWithContext() { +abstract class AnnotationImplementationTransformer(val context: BackendContext, val irFile: IrFile?) : IrElementTransformerVoidWithContext() { internal val implementations: MutableMap = mutableMapOf() override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { @@ -67,11 +61,15 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val return newCall } + open fun IrClass.platformSetup() {} + private fun createAnnotationImplementation(annotationClass: IrClass): IrClass { val localDeclarationParent = currentClass?.scope?.getLocalDeclarationParent() as? IrClass val parentFqName = annotationClass.fqNameWhenAvailable!!.asString().replace('.', '_') val wrapperName = Name.identifier("annotationImpl\$$parentFqName$0") val subclass = context.irFactory.buildClass { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET name = wrapperName origin = ANNOTATION_IMPLEMENTATION // It can be seen from inline functions and multiple classes within one file @@ -79,90 +77,29 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val // since declaration is synthetic anyway visibility = DescriptorVisibilities.INTERNAL }.apply { - parent = localDeclarationParent ?: irFile ?: error("irFile in transformer should be specified when creating synthetic implementation") + parent = localDeclarationParent ?: irFile + ?: error("irFile in transformer should be specified when creating synthetic implementation") createImplicitParameterDeclarationWithWrappedDescriptor() superTypes = listOf(annotationClass.defaultType) + platformSetup() } val ctor = subclass.addConstructor { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET visibility = DescriptorVisibilities.PUBLIC } - val (originalProps, implementationProps) = implementAnnotationProperties(subclass, annotationClass, ctor) - implementEqualsAndHashCode(annotationClass, subclass, originalProps, implementationProps) + implementAnnotationPropertiesAndConstructor(subclass, annotationClass, ctor) + implementGeneratedFunctions(annotationClass, subclass) implementPlatformSpecificParts(annotationClass, subclass) return subclass } - fun implementAnnotationProperties(implClass: IrClass, annotationClass: IrClass, generatedConstructor: IrConstructor): Pair, List> { - val ctorBody = context.irFactory.createBlockBody( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf( - IrDelegatingConstructorCallImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.unitType, context.irBuiltIns.anyClass.constructors.single(), - typeArgumentsCount = 0, valueArgumentsCount = 0 - ) - ) - ) - - generatedConstructor.body = ctorBody - - val properties = annotationClass.getAnnotationProperties() - - return properties to properties.map { property -> - - val propType = property.getter!!.returnType - val propName = property.name - val field = context.irFactory.buildField { - name = propName - type = propType - origin = ANNOTATION_IMPLEMENTATION - isFinal = true - visibility = DescriptorVisibilities.PRIVATE - }.also { it.parent = implClass } - - val parameter = generatedConstructor.addValueParameter(propName.asString(), propType) - // VALUE_FROM_PARAMETER - val originalParameter = ((property.backingField?.initializer?.expression as? IrGetValue)?.symbol?.owner as? IrValueParameter) - if (originalParameter?.defaultValue != null) { - parameter.defaultValue = originalParameter.defaultValue!!.deepCopyWithVariables().also { it.transformChildrenVoid() } - } - - ctorBody.statements += IrSetFieldImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.symbol, - IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, implClass.thisReceiver!!.symbol), - IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter.symbol), - context.irBuiltIns.unitType, - ) - - val prop = implClass.addProperty { - name = propName - isVar = false - origin = ANNOTATION_IMPLEMENTATION - }.apply { - field.correspondingPropertySymbol = this.symbol - backingField = field - parent = implClass - } - - prop.addGetter { - name = propName // Annotation value getter should be named 'x', not 'getX' - returnType = propType.kClassToJClassIfNeeded() // On JVM, annotation store j.l.Class even if declared with KClass - origin = ANNOTATION_IMPLEMENTATION - visibility = DescriptorVisibilities.PUBLIC - modality = Modality.FINAL - }.apply { - correspondingPropertySymbol = prop.symbol - dispatchReceiverParameter = implClass.thisReceiver!!.copyTo(this) - body = context.createIrBuilder(symbol).irBlockBody { - var value: IrExpression = irGetField(irGet(dispatchReceiverParameter!!), field) - if (propType.isKClass()) value = this.kClassExprToJClassIfNeeded(value) - +irReturn(value) - } - } - - prop - } - - } + abstract fun implementAnnotationPropertiesAndConstructor( + implClass: IrClass, + annotationClass: IrClass, + generatedConstructor: IrConstructor + ) fun IrClass.getAnnotationProperties(): List { // For some weird reason, annotations defined in other IrFiles, do not have IrProperties in declarations. @@ -173,31 +110,61 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val .mapNotNull { it.correspondingPropertySymbol?.owner } } - open fun IrType.kClassToJClassIfNeeded(): IrType = this - open fun IrBuilderWithScope.kClassExprToJClassIfNeeded(irExpression: IrExpression): IrExpression = irExpression - open fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression = - irBuilder.irEquals(arg1, arg2) + abstract fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol - @Suppress("UNUSED_VARIABLE") - fun implementEqualsAndHashCode(annotationClass: IrClass, implClass: IrClass, originalProps: List, childProps: List) { + fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression = + if (type.isArray() || type.isPrimitiveArray()) { + val requiredSymbol = getArrayContentEqualsSymbol(type) + irBuilder.irCall( + requiredSymbol + ).apply { + if (requiredSymbol.owner.extensionReceiverParameter != null) { + extensionReceiver = arg1 + putValueArgument(0, arg2) + } else { + putValueArgument(0, arg1) + putValueArgument(1, arg2) + } + } + } else + irBuilder.irEquals(arg1, arg2) + + open val forbidDirectFieldAccessInMethods = false + + open fun generateFunctionBodies( + annotationClass: IrClass, + implClass: IrClass, + eqFun: IrSimpleFunction, + hcFun: IrSimpleFunction, + toStringFun: IrSimpleFunction, + generator: AnnotationImplementationMemberGenerator + ) { + val properties = annotationClass.getAnnotationProperties() + generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, properties) + generator.generateHashCodeMethod(hcFun, properties) + generator.generateToStringMethod(toStringFun, properties) + } + + fun implementGeneratedFunctions(annotationClass: IrClass, implClass: IrClass) { val creator = MethodsFromAnyGeneratorForLowerings(context, implClass, ANNOTATION_IMPLEMENTATION) + val eqFun = creator.createEqualsMethodDeclaration() + val hcFun = creator.createHashCodeMethodDeclaration() + val toStringFun = creator.createToStringMethodDeclaration() + if (annotationClass != implClass) { + implClass.addFakeOverrides(context.typeSystem) + } + val generator = AnnotationImplementationMemberGenerator( context, implClass, nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(), + forbidDirectFieldAccess = forbidDirectFieldAccessInMethods ) { type, a, b -> generatedEquals(this, type, a, b) } - val eqFun = creator.createEqualsMethodDeclaration() - generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, originalProps) - - val hcFun = creator.createHashCodeMethodDeclaration() - generator.generateHashCodeMethod(hcFun, childProps) - - val toStringFun = creator.createToStringMethodDeclaration() - generator.generateToStringMethod(toStringFun, childProps) + generateFunctionBodies(annotationClass, implClass, eqFun, hcFun, toStringFun, generator) } open fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) {} @@ -207,8 +174,9 @@ class AnnotationImplementationMemberGenerator( backendContext: BackendContext, irClass: IrClass, val nameForToString: String, + forbidDirectFieldAccess: Boolean, val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression, -) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION) { +) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION, forbidDirectFieldAccess) { override fun IrClass.classNameForToString(): String = nameForToString @@ -230,7 +198,7 @@ class AnnotationImplementationMemberGenerator( // 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) = equalsFun.apply { - body = backendContext.createIrBuilder(symbol).irBlockBody { + body = backendContext.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { val irType = typeForEquals fun irOther() = irGet(valueParameters[0]) fun irThis() = irGet(dispatchReceiverParameter!!) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt index 4ea853d74ab..839ed894be0 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt @@ -22,22 +22,29 @@ import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator +import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.isPrimitiveArray class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irClass: IrClass, val origin: IrDeclarationOrigin) { - fun createToStringMethodDeclaration(): IrSimpleFunction = irClass.addFunction("toString", context.irBuiltIns.stringType).apply { - overriddenSymbols = irClass.collectOverridenSymbols { it.isToString() } - } + private fun IrClass.addSyntheticFunction(name: String, returnType: IrType) = + addFunction(name, returnType, startOffset = SYNTHETIC_OFFSET, endOffset = SYNTHETIC_OFFSET) - fun createHashCodeMethodDeclaration(): IrSimpleFunction = irClass.addFunction("hashCode", context.irBuiltIns.intType).apply { - overriddenSymbols = irClass.collectOverridenSymbols { it.isHashCode() } - } + fun createToStringMethodDeclaration(): IrSimpleFunction = + irClass.addSyntheticFunction("toString", context.irBuiltIns.stringType).apply { + overriddenSymbols = irClass.collectOverridenSymbols { it.isToString() } + } - fun createEqualsMethodDeclaration(): IrSimpleFunction = irClass.addFunction("equals", context.irBuiltIns.booleanType).apply { - overriddenSymbols = irClass.collectOverridenSymbols { it.isEquals(context) } - addValueParameter("other", context.irBuiltIns.anyNType) - } + fun createHashCodeMethodDeclaration(): IrSimpleFunction = + irClass.addSyntheticFunction("hashCode", context.irBuiltIns.intType).apply { + overriddenSymbols = irClass.collectOverridenSymbols { it.isHashCode() } + } + + fun createEqualsMethodDeclaration(): IrSimpleFunction = + irClass.addSyntheticFunction("equals", context.irBuiltIns.booleanType).apply { + overriddenSymbols = irClass.collectOverridenSymbols { it.isEquals(context) } + addValueParameter("other", context.irBuiltIns.anyNType) + } companion object { fun IrFunction.isToString(): Boolean = @@ -60,13 +67,15 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla open class LoweringDataClassMemberGenerator( val backendContext: BackendContext, irClass: IrClass, - origin: IrDeclarationOrigin + origin: IrDeclarationOrigin, + forbidDirectFieldAccess: Boolean = false ) : DataClassMembersGenerator( IrGeneratorContextBase(backendContext.irBuiltIns), backendContext.ir.symbols.externalSymbolTable, irClass, - origin + origin, + forbidDirectFieldAccess ) { override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsAnnotationImplementationLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsAnnotationImplementationLowering.kt index 6de48a75d2d..17483e8c8ea 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsAnnotationImplementationLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsAnnotationImplementationLowering.kt @@ -11,9 +11,11 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.builders.IrBlockBodyBuilder import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isArray @@ -38,9 +40,8 @@ class JsAnnotationImplementationTransformer(val jsContext: JsIrBackendContext) : override fun visitClassNew(declaration: IrClass): IrClass { if (!declaration.isAnnotationClass) return declaration - val properties = declaration.getAnnotationProperties() context.irFactory.stageController.unrestrictDeclarationListsAccess { - implementEqualsAndHashCode(declaration, declaration, properties, properties) + implementGeneratedFunctions(declaration, declaration) } return declaration } @@ -48,22 +49,17 @@ class JsAnnotationImplementationTransformer(val jsContext: JsIrBackendContext) : private val arraysContentEquals: Map = requireNotNull(jsContext.ir.symbols.arraysContentEquals) { "contentEquals symbols should be defined in JS IR context" } - override fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression { - return if (type.isArray() || type.isPrimitiveArray()) { - val requiredSymbol = - if (type.isPrimitiveArray()) - arraysContentEquals[type] - else - arraysContentEquals.entries.singleOrNull { (k, _) -> k.isArray() }?.value - if (requiredSymbol == null) { - error("Can't find an Arrays.contentEquals method for array type ${type.render()}") - } - irBuilder.irCall( - requiredSymbol - ).apply { - extensionReceiver = arg1 - putValueArgument(0, arg2) - } - } else super.generatedEquals(irBuilder, type, arg1, arg2) + override fun getArrayContentEqualsSymbol(type: IrType) = + when { + type.isPrimitiveArray() -> arraysContentEquals[type] + else -> arraysContentEquals.entries.singleOrNull { (k, _) -> k.isArray() }?.value + } ?: error("Can't find an Arrays.contentEquals method for array type ${type.render()}") + + override fun implementAnnotationPropertiesAndConstructor( + implClass: IrClass, + annotationClass: IrClass, + generatedConstructor: IrConstructor + ) { + throw IllegalStateException("Should not be called") } } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt index 16dfbe87b30..f010998dba0 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt @@ -5,23 +5,28 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION -import org.jetbrains.kotlin.backend.common.lower.AnnotationImplementationLowering -import org.jetbrains.kotlin.backend.common.lower.AnnotationImplementationTransformer +import org.jetbrains.kotlin.backend.common.deepCopyWithVariables +import org.jetbrains.kotlin.backend.common.ir.copyTo +import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.addFunction -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclaration -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.builders.declarations.* +import org.jetbrains.kotlin.ir.declarations.* 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.IrDelegatingConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -48,7 +53,7 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, return super.visitConstructorCall(expression) } - override fun IrType.kClassToJClassIfNeeded(): IrType = when { + private fun IrType.kClassToJClassIfNeeded(): IrType = when { this.isKClass() -> jvmContext.ir.symbols.javaLangClass.starProjectedType this.isKClassArray() -> jvmContext.irBuiltIns.arrayClass.typeWith( jvmContext.ir.symbols.javaLangClass.starProjectedType @@ -71,20 +76,13 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, } } - override fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression { - return if (type.isArray() || type.isPrimitiveArray()) { - val targetType = if (type.isPrimitiveArray()) type else jvmContext.ir.symbols.arrayOfAnyNType - val requiredSymbol = jvmContext.ir.symbols.arraysClass.owner.findDeclaration { - it.name.asString() == "equals" && it.valueParameters.size == 2 && it.valueParameters.first().type == targetType - } - requireNotNull(requiredSymbol) { "Can't find Arrays.equals method for type ${targetType.render()}" } - irBuilder.irCall( - requiredSymbol.symbol - ).apply { - putValueArgument(0, arg1) - putValueArgument(1, arg2) - } - } else super.generatedEquals(irBuilder, type, arg1, arg2) + override fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol { + val targetType = if (type.isPrimitiveArray()) type else jvmContext.ir.symbols.arrayOfAnyNType + val requiredSymbol = jvmContext.ir.symbols.arraysClass.owner.findDeclaration { + it.name.asString() == "equals" && it.valueParameters.size == 2 && it.valueParameters.first().type == targetType + } + requireNotNull(requiredSymbol) { "Can't find Arrays.equals method for type ${targetType.render()}" } + return requiredSymbol.symbol } override fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) { @@ -107,4 +105,97 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, } } } + + override fun implementAnnotationPropertiesAndConstructor( + implClass: IrClass, + annotationClass: IrClass, + generatedConstructor: IrConstructor + ) { + val ctorBody = context.irFactory.createBlockBody( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, listOf( + IrDelegatingConstructorCallImpl( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.irBuiltIns.unitType, context.irBuiltIns.anyClass.constructors.single(), + typeArgumentsCount = 0, valueArgumentsCount = 0 + ) + ) + ) + + generatedConstructor.body = ctorBody + + annotationClass.getAnnotationProperties().forEach { property -> + val propType = property.getter!!.returnType + val propName = property.name + val field = context.irFactory.buildField { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET + name = propName + type = propType + origin = ANNOTATION_IMPLEMENTATION + isFinal = true + visibility = DescriptorVisibilities.PRIVATE + }.also { it.parent = implClass } + + val parameter = generatedConstructor.addValueParameter(propName.asString(), propType) + // VALUE_FROM_PARAMETER + val originalParameter = ((property.backingField?.initializer?.expression as? IrGetValue)?.symbol?.owner as? IrValueParameter) + if (originalParameter?.defaultValue != null) { + parameter.defaultValue = originalParameter.defaultValue!!.deepCopyWithVariables().also { it.transformChildrenVoid() } + } + + ctorBody.statements += IrSetFieldImpl( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, field.symbol, + IrGetValueImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, implClass.thisReceiver!!.symbol), + IrGetValueImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, parameter.symbol), + context.irBuiltIns.unitType, + ) + + val prop = implClass.addProperty { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET + name = propName + isVar = false + origin = ANNOTATION_IMPLEMENTATION + }.apply { + field.correspondingPropertySymbol = this.symbol + backingField = field + parent = implClass + overriddenSymbols = listOf(property.symbol) + } + + prop.addGetter { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET + name = propName // Annotation value getter should be named 'x', not 'getX' + returnType = propType.kClassToJClassIfNeeded() // On JVM, annotation store j.l.Class even if declared with KClass + origin = ANNOTATION_IMPLEMENTATION + visibility = DescriptorVisibilities.PUBLIC + modality = Modality.FINAL + }.apply { + correspondingPropertySymbol = prop.symbol + dispatchReceiverParameter = implClass.thisReceiver!!.copyTo(this) + body = context.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + var value: IrExpression = irGetField(irGet(dispatchReceiverParameter!!), field) + if (propType.isKClass()) value = this.kClassExprToJClassIfNeeded(value) + +irReturn(value) + } + overriddenSymbols = listOf(property.getter!!.symbol) + } + } + } + + override fun generateFunctionBodies( + annotationClass: IrClass, + implClass: IrClass, + eqFun: IrSimpleFunction, + hcFun: IrSimpleFunction, + toStringFun: IrSimpleFunction, + generator: AnnotationImplementationMemberGenerator + ) { + val properties = annotationClass.getAnnotationProperties() + val implProperties = implClass.getAnnotationProperties() + generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, properties) + generator.generateHashCodeMethod(hcFun, implProperties) + generator.generateToStringMethod(toStringFun, implProperties) + } + } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt index 8950efe87aa..9dab16706f6 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.ir.util import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl @@ -36,7 +35,8 @@ abstract class DataClassMembersGenerator( val context: IrGeneratorContext, val symbolTable: ReferenceSymbolTable, val irClass: IrClass, - val origin: IrDeclarationOrigin + val origin: IrDeclarationOrigin, + val forbidDirectFieldAccess: Boolean = false ) { private val irPropertiesByDescriptor: Map = irClass.properties.associateBy { it.descriptor } @@ -49,8 +49,8 @@ abstract class DataClassMembersGenerator( } private inner class MemberFunctionBuilder( - startOffset: Int = UNDEFINED_OFFSET, - endOffset: Int = UNDEFINED_OFFSET, + startOffset: Int = SYNTHETIC_OFFSET, + endOffset: Int = SYNTHETIC_OFFSET, val irFunction: IrFunction ) : IrBlockBodyBuilder(context, Scope(irFunction.symbol), startOffset, endOffset) { inline fun addToClass(builder: MemberFunctionBuilder.(IrFunction) -> Unit): IrFunction { @@ -89,7 +89,7 @@ abstract class DataClassMembersGenerator( // data classes and corresponding properties can be non-final. // We should use getters for such properties (see KT-41284). val backingField = property.backingField - return if (property.modality == Modality.FINAL && backingField != null) { + return if (!forbidDirectFieldAccess && property.modality == Modality.FINAL && backingField != null) { irGetField(receiver, backingField) } else { irCall(property.getter!!).apply {