Refactor AnnotationImplementationLowering

- Replaced UNDEFINED_OFFSET with SYNTHETIC_OFFSET, it's required by
  Native backend codegen
- Fixed missing overridden symbols
- Enforce adding fakeoverrides for members not overridden by backend
- Support more points for platform customisation
This commit is contained in:
Pavel Kunyavskiy
2021-09-13 10:32:37 +03:00
committed by Space
parent d9424fa092
commit 773c82ae48
5 changed files with 222 additions and 158 deletions
@@ -8,25 +8,19 @@ 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.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.ir.addFakeOverrides
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.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.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression 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.symbols.IrFunctionSymbol
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.IrType
import org.jetbrains.kotlin.ir.types.classOrNull 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.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name 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<IrClass, IrClass> = mutableMapOf() internal val implementations: MutableMap<IrClass, IrClass> = mutableMapOf()
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
@@ -67,11 +61,15 @@ open class AnnotationImplementationTransformer(val context: BackendContext, val
return newCall return newCall
} }
open fun IrClass.platformSetup() {}
private fun createAnnotationImplementation(annotationClass: IrClass): IrClass { private fun createAnnotationImplementation(annotationClass: IrClass): IrClass {
val localDeclarationParent = currentClass?.scope?.getLocalDeclarationParent() as? IrClass val localDeclarationParent = currentClass?.scope?.getLocalDeclarationParent() as? IrClass
val parentFqName = annotationClass.fqNameWhenAvailable!!.asString().replace('.', '_') val parentFqName = annotationClass.fqNameWhenAvailable!!.asString().replace('.', '_')
val wrapperName = Name.identifier("annotationImpl\$$parentFqName$0") val wrapperName = Name.identifier("annotationImpl\$$parentFqName$0")
val subclass = context.irFactory.buildClass { val subclass = context.irFactory.buildClass {
startOffset = SYNTHETIC_OFFSET
endOffset = SYNTHETIC_OFFSET
name = wrapperName name = wrapperName
origin = ANNOTATION_IMPLEMENTATION origin = ANNOTATION_IMPLEMENTATION
// It can be seen from inline functions and multiple classes within one file // 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 // since declaration is synthetic anyway
visibility = DescriptorVisibilities.INTERNAL visibility = DescriptorVisibilities.INTERNAL
}.apply { }.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() createImplicitParameterDeclarationWithWrappedDescriptor()
superTypes = listOf(annotationClass.defaultType) superTypes = listOf(annotationClass.defaultType)
platformSetup()
} }
val ctor = subclass.addConstructor { val ctor = subclass.addConstructor {
startOffset = SYNTHETIC_OFFSET
endOffset = SYNTHETIC_OFFSET
visibility = DescriptorVisibilities.PUBLIC visibility = DescriptorVisibilities.PUBLIC
} }
val (originalProps, implementationProps) = implementAnnotationProperties(subclass, annotationClass, ctor) implementAnnotationPropertiesAndConstructor(subclass, annotationClass, ctor)
implementEqualsAndHashCode(annotationClass, subclass, originalProps, implementationProps) implementGeneratedFunctions(annotationClass, subclass)
implementPlatformSpecificParts(annotationClass, subclass) implementPlatformSpecificParts(annotationClass, subclass)
return subclass return subclass
} }
fun implementAnnotationProperties(implClass: IrClass, annotationClass: IrClass, generatedConstructor: IrConstructor): Pair<List<IrProperty>, List<IrProperty>> { abstract fun implementAnnotationPropertiesAndConstructor(
val ctorBody = context.irFactory.createBlockBody( implClass: IrClass,
UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf( annotationClass: IrClass,
IrDelegatingConstructorCallImpl( generatedConstructor: IrConstructor
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
}
}
fun IrClass.getAnnotationProperties(): List<IrProperty> { fun IrClass.getAnnotationProperties(): List<IrProperty> {
// For some weird reason, annotations defined in other IrFiles, do not have IrProperties in declarations. // 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 } .mapNotNull { it.correspondingPropertySymbol?.owner }
} }
open fun IrType.kClassToJClassIfNeeded(): IrType = this
open fun IrBuilderWithScope.kClassExprToJClassIfNeeded(irExpression: IrExpression): IrExpression = irExpression open fun IrBuilderWithScope.kClassExprToJClassIfNeeded(irExpression: IrExpression): IrExpression = irExpression
open fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression = abstract fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol
irBuilder.irEquals(arg1, arg2)
@Suppress("UNUSED_VARIABLE") fun generatedEquals(irBuilder: IrBlockBodyBuilder, type: IrType, arg1: IrExpression, arg2: IrExpression): IrExpression =
fun implementEqualsAndHashCode(annotationClass: IrClass, implClass: IrClass, originalProps: List<IrProperty>, childProps: List<IrProperty>) { 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 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( val generator = AnnotationImplementationMemberGenerator(
context, implClass, context, implClass,
nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(), nameForToString = "@" + annotationClass.fqNameWhenAvailable!!.asString(),
forbidDirectFieldAccess = forbidDirectFieldAccessInMethods
) { type, a, b -> ) { type, a, b ->
generatedEquals(this, type, a, b) generatedEquals(this, type, a, b)
} }
val eqFun = creator.createEqualsMethodDeclaration() generateFunctionBodies(annotationClass, implClass, eqFun, hcFun, toStringFun, generator)
generator.generateEqualsUsingGetters(eqFun, annotationClass.defaultType, originalProps)
val hcFun = creator.createHashCodeMethodDeclaration()
generator.generateHashCodeMethod(hcFun, childProps)
val toStringFun = creator.createToStringMethodDeclaration()
generator.generateToStringMethod(toStringFun, childProps)
} }
open fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) {} open fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) {}
@@ -207,8 +174,9 @@ class AnnotationImplementationMemberGenerator(
backendContext: BackendContext, backendContext: BackendContext,
irClass: IrClass, irClass: IrClass,
val nameForToString: String, val nameForToString: String,
forbidDirectFieldAccess: Boolean,
val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression, val selectEquals: IrBlockBodyBuilder.(IrType, IrExpression, IrExpression) -> IrExpression,
) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION) { ) : LoweringDataClassMemberGenerator(backendContext, irClass, ANNOTATION_IMPLEMENTATION, forbidDirectFieldAccess) {
override fun IrClass.classNameForToString(): String = nameForToString override fun IrClass.classNameForToString(): String = nameForToString
@@ -230,7 +198,7 @@ class AnnotationImplementationMemberGenerator(
// 2. Properties should be retrieved using getters without accessing backing fields // 2. Properties should be retrieved using getters without accessing backing fields
// (DataClassMembersGenerator typically tries to access fields) // (DataClassMembersGenerator typically tries to access fields)
fun generateEqualsUsingGetters(equalsFun: IrSimpleFunction, typeForEquals: IrType, properties: List<IrProperty>) = equalsFun.apply { fun generateEqualsUsingGetters(equalsFun: IrSimpleFunction, typeForEquals: IrType, properties: List<IrProperty>) = equalsFun.apply {
body = backendContext.createIrBuilder(symbol).irBlockBody { body = backendContext.createIrBuilder(symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody {
val irType = typeForEquals val irType = typeForEquals
fun irOther() = irGet(valueParameters[0]) fun irOther() = irGet(valueParameters[0])
fun irThis() = irGet(dispatchReceiverParameter!!) fun irThis() = irGet(dispatchReceiverParameter!!)
@@ -22,22 +22,29 @@ import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator 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.functions
import org.jetbrains.kotlin.ir.util.isPrimitiveArray import org.jetbrains.kotlin.ir.util.isPrimitiveArray
class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irClass: IrClass, val origin: IrDeclarationOrigin) { class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irClass: IrClass, val origin: IrDeclarationOrigin) {
fun createToStringMethodDeclaration(): IrSimpleFunction = irClass.addFunction("toString", context.irBuiltIns.stringType).apply { private fun IrClass.addSyntheticFunction(name: String, returnType: IrType) =
overriddenSymbols = irClass.collectOverridenSymbols { it.isToString() } addFunction(name, returnType, startOffset = SYNTHETIC_OFFSET, endOffset = SYNTHETIC_OFFSET)
}
fun createHashCodeMethodDeclaration(): IrSimpleFunction = irClass.addFunction("hashCode", context.irBuiltIns.intType).apply { fun createToStringMethodDeclaration(): IrSimpleFunction =
overriddenSymbols = irClass.collectOverridenSymbols { it.isHashCode() } irClass.addSyntheticFunction("toString", context.irBuiltIns.stringType).apply {
} overriddenSymbols = irClass.collectOverridenSymbols { it.isToString() }
}
fun createEqualsMethodDeclaration(): IrSimpleFunction = irClass.addFunction("equals", context.irBuiltIns.booleanType).apply { fun createHashCodeMethodDeclaration(): IrSimpleFunction =
overriddenSymbols = irClass.collectOverridenSymbols { it.isEquals(context) } irClass.addSyntheticFunction("hashCode", context.irBuiltIns.intType).apply {
addValueParameter("other", context.irBuiltIns.anyNType) 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 { companion object {
fun IrFunction.isToString(): Boolean = fun IrFunction.isToString(): Boolean =
@@ -60,13 +67,15 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla
open class LoweringDataClassMemberGenerator( open class LoweringDataClassMemberGenerator(
val backendContext: BackendContext, val backendContext: BackendContext,
irClass: IrClass, irClass: IrClass,
origin: IrDeclarationOrigin origin: IrDeclarationOrigin,
forbidDirectFieldAccess: Boolean = false
) : ) :
DataClassMembersGenerator( DataClassMembersGenerator(
IrGeneratorContextBase(backendContext.irBuiltIns), IrGeneratorContextBase(backendContext.irBuiltIns),
backendContext.ir.symbols.externalSymbolTable, backendContext.ir.symbols.externalSymbolTable,
irClass, irClass,
origin origin,
forbidDirectFieldAccess
) { ) {
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction { override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction {
@@ -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.IrBlockBodyBuilder
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.IrClass 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.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression 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.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.types.isArray
@@ -38,9 +40,8 @@ class JsAnnotationImplementationTransformer(val jsContext: JsIrBackendContext) :
override fun visitClassNew(declaration: IrClass): IrClass { override fun visitClassNew(declaration: IrClass): IrClass {
if (!declaration.isAnnotationClass) return declaration if (!declaration.isAnnotationClass) return declaration
val properties = declaration.getAnnotationProperties()
context.irFactory.stageController.unrestrictDeclarationListsAccess { context.irFactory.stageController.unrestrictDeclarationListsAccess {
implementEqualsAndHashCode(declaration, declaration, properties, properties) implementGeneratedFunctions(declaration, declaration)
} }
return declaration return declaration
} }
@@ -48,22 +49,17 @@ class JsAnnotationImplementationTransformer(val jsContext: JsIrBackendContext) :
private val arraysContentEquals: Map<IrType, IrSimpleFunctionSymbol> = private val arraysContentEquals: Map<IrType, IrSimpleFunctionSymbol> =
requireNotNull(jsContext.ir.symbols.arraysContentEquals) { "contentEquals symbols should be defined in JS IR context" } 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 { override fun getArrayContentEqualsSymbol(type: IrType) =
return if (type.isArray() || type.isPrimitiveArray()) { when {
val requiredSymbol = type.isPrimitiveArray() -> arraysContentEquals[type]
if (type.isPrimitiveArray()) else -> arraysContentEquals.entries.singleOrNull { (k, _) -> k.isArray() }?.value
arraysContentEquals[type] } ?: error("Can't find an Arrays.contentEquals method for array type ${type.render()}")
else
arraysContentEquals.entries.singleOrNull { (k, _) -> k.isArray() }?.value override fun implementAnnotationPropertiesAndConstructor(
if (requiredSymbol == null) { implClass: IrClass,
error("Can't find an Arrays.contentEquals method for array type ${type.render()}") annotationClass: IrClass,
} generatedConstructor: IrConstructor
irBuilder.irCall( ) {
requiredSymbol throw IllegalStateException("Should not be called")
).apply {
extensionReceiver = arg1
putValueArgument(0, arg2)
}
} else super.generatedEquals(irBuilder, type, arg1, arg2)
} }
} }
@@ -5,23 +5,28 @@
package org.jetbrains.kotlin.backend.jvm.lower package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
import org.jetbrains.kotlin.backend.common.lower.AnnotationImplementationLowering import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.lower.AnnotationImplementationTransformer import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope
import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference 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.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.*
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.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression 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.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -48,7 +53,7 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
return super.visitConstructorCall(expression) return super.visitConstructorCall(expression)
} }
override fun IrType.kClassToJClassIfNeeded(): IrType = when { private fun IrType.kClassToJClassIfNeeded(): IrType = when {
this.isKClass() -> jvmContext.ir.symbols.javaLangClass.starProjectedType this.isKClass() -> jvmContext.ir.symbols.javaLangClass.starProjectedType
this.isKClassArray() -> jvmContext.irBuiltIns.arrayClass.typeWith( this.isKClassArray() -> jvmContext.irBuiltIns.arrayClass.typeWith(
jvmContext.ir.symbols.javaLangClass.starProjectedType 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 { override fun getArrayContentEqualsSymbol(type: IrType): IrFunctionSymbol {
return if (type.isArray() || type.isPrimitiveArray()) { val targetType = if (type.isPrimitiveArray()) type else jvmContext.ir.symbols.arrayOfAnyNType
val targetType = if (type.isPrimitiveArray()) type else jvmContext.ir.symbols.arrayOfAnyNType val requiredSymbol = jvmContext.ir.symbols.arraysClass.owner.findDeclaration<IrFunction> {
val requiredSymbol = jvmContext.ir.symbols.arraysClass.owner.findDeclaration<IrFunction> { it.name.asString() == "equals" && it.valueParameters.size == 2 && it.valueParameters.first().type == targetType
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()}" }
requireNotNull(requiredSymbol) { "Can't find Arrays.equals method for type ${targetType.render()}" } return requiredSymbol.symbol
irBuilder.irCall(
requiredSymbol.symbol
).apply {
putValueArgument(0, arg1)
putValueArgument(1, arg2)
}
} else super.generatedEquals(irBuilder, type, arg1, arg2)
} }
override fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) { 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)
}
} }
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
@@ -36,7 +35,8 @@ abstract class DataClassMembersGenerator(
val context: IrGeneratorContext, val context: IrGeneratorContext,
val symbolTable: ReferenceSymbolTable, val symbolTable: ReferenceSymbolTable,
val irClass: IrClass, val irClass: IrClass,
val origin: IrDeclarationOrigin val origin: IrDeclarationOrigin,
val forbidDirectFieldAccess: Boolean = false
) { ) {
private val irPropertiesByDescriptor: Map<PropertyDescriptor, IrProperty> = private val irPropertiesByDescriptor: Map<PropertyDescriptor, IrProperty> =
irClass.properties.associateBy { it.descriptor } irClass.properties.associateBy { it.descriptor }
@@ -49,8 +49,8 @@ abstract class DataClassMembersGenerator(
} }
private inner class MemberFunctionBuilder( private inner class MemberFunctionBuilder(
startOffset: Int = UNDEFINED_OFFSET, startOffset: Int = SYNTHETIC_OFFSET,
endOffset: Int = UNDEFINED_OFFSET, endOffset: Int = SYNTHETIC_OFFSET,
val irFunction: IrFunction val irFunction: IrFunction
) : IrBlockBodyBuilder(context, Scope(irFunction.symbol), startOffset, endOffset) { ) : IrBlockBodyBuilder(context, Scope(irFunction.symbol), startOffset, endOffset) {
inline fun addToClass(builder: MemberFunctionBuilder.(IrFunction) -> Unit): IrFunction { inline fun addToClass(builder: MemberFunctionBuilder.(IrFunction) -> Unit): IrFunction {
@@ -89,7 +89,7 @@ abstract class DataClassMembersGenerator(
// data classes and corresponding properties can be non-final. // data classes and corresponding properties can be non-final.
// We should use getters for such properties (see KT-41284). // We should use getters for such properties (see KT-41284).
val backingField = property.backingField val backingField = property.backingField
return if (property.modality == Modality.FINAL && backingField != null) { return if (!forbidDirectFieldAccess && property.modality == Modality.FINAL && backingField != null) {
irGetField(receiver, backingField) irGetField(receiver, backingField)
} else { } else {
irCall(property.getter!!).apply { irCall(property.getter!!).apply {