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:
+67
-99
@@ -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<IrClass, IrClass> = 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<IrProperty>, List<IrProperty>> {
|
||||
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<IrProperty> {
|
||||
// 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<IrProperty>, childProps: List<IrProperty>) {
|
||||
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<IrProperty>) = 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!!)
|
||||
|
||||
+21
-12
@@ -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 {
|
||||
|
||||
+15
-19
@@ -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<IrType, IrSimpleFunctionSymbol> =
|
||||
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")
|
||||
}
|
||||
}
|
||||
+114
-23
@@ -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<IrFunction> {
|
||||
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<IrFunction> {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<PropertyDescriptor, IrProperty> =
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user