[K/N] Get rid of using base class default values for annotations
They can be not available in incremental compilation case ^KT-50211
This commit is contained in:
+3
-1
@@ -65,13 +65,15 @@ abstract class AnnotationImplementationTransformer(val context: BackendContext,
|
|||||||
return super.visitClassNew(declaration)
|
return super.visitClassNew(declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall) : IrConstructor
|
||||||
|
|
||||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||||
val constructedClass = expression.type.classOrNull?.owner ?: return super.visitConstructorCall(expression)
|
val constructedClass = expression.type.classOrNull?.owner ?: return super.visitConstructorCall(expression)
|
||||||
if (!constructedClass.isAnnotationClass) return super.visitConstructorCall(expression)
|
if (!constructedClass.isAnnotationClass) return super.visitConstructorCall(expression)
|
||||||
if (constructedClass.typeParameters.isNotEmpty()) return super.visitConstructorCall(expression) // Not supported yet
|
if (constructedClass.typeParameters.isNotEmpty()) return super.visitConstructorCall(expression) // Not supported yet
|
||||||
|
|
||||||
val implClass = implementations.getOrPut(constructedClass) { createAnnotationImplementation(constructedClass) }
|
val implClass = implementations.getOrPut(constructedClass) { createAnnotationImplementation(constructedClass) }
|
||||||
val ctor = implClass.constructors.single()
|
val ctor = chooseConstructor(implClass, expression)
|
||||||
val newCall = IrConstructorCallImpl.fromSymbolOwner(
|
val newCall = IrConstructorCallImpl.fromSymbolOwner(
|
||||||
expression.startOffset,
|
expression.startOffset,
|
||||||
expression.endOffset,
|
expression.endOffset,
|
||||||
|
|||||||
+3
@@ -36,6 +36,9 @@ class JsAnnotationImplementationTransformer(val jsContext: JsIrBackendContext) :
|
|||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall): IrConstructor =
|
||||||
|
compilationException("Should not be called", implClass)
|
||||||
|
|
||||||
override fun visitClassNew(declaration: IrClass): IrStatement {
|
override fun visitClassNew(declaration: IrClass): IrStatement {
|
||||||
if (declaration.isAnnotationClass) {
|
if (declaration.isAnnotationClass) {
|
||||||
implementGeneratedFunctions(declaration, declaration)
|
implementGeneratedFunctions(declaration, declaration)
|
||||||
|
|||||||
+4
@@ -41,6 +41,10 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
|
|||||||
private val inInlineFunctionScope: Boolean
|
private val inInlineFunctionScope: Boolean
|
||||||
get() = allScopes.any { it.irElement.safeAs<IrDeclaration>()?.isInPublicInlineScope == true }
|
get() = allScopes.any { it.irElement.safeAs<IrDeclaration>()?.isInPublicInlineScope == true }
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
override fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall) =
|
||||||
|
implClass.constructors.single()
|
||||||
|
|
||||||
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||||
val constructedClass = expression.type.classOrNull
|
val constructedClass = expression.type.classOrNull
|
||||||
if (constructedClass?.owner?.isAnnotationClass == true && inInlineFunctionScope) {
|
if (constructedClass?.owner?.isAnnotationClass == true && inInlineFunctionScope) {
|
||||||
|
|||||||
+47
-16
@@ -5,18 +5,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.lower
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.AnnotationImplementationTransformer
|
import org.jetbrains.kotlin.backend.common.lower.AnnotationImplementationTransformer
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.isFinalClass
|
import org.jetbrains.kotlin.backend.konan.ir.isFinalClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
|
||||||
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.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.putArgument
|
||||||
|
|
||||||
|
|
||||||
internal class NativeAnnotationImplementationTransformer(context: Context, irFile: IrFile) :
|
internal class NativeAnnotationImplementationTransformer(context: Context, irFile: IrFile) :
|
||||||
@@ -35,31 +36,61 @@ internal class NativeAnnotationImplementationTransformer(context: Context, irFil
|
|||||||
parent = irFile!!
|
parent = irFile!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When annotation is defined in another module, default values can be not available
|
||||||
|
* during incremental compilation.
|
||||||
|
*
|
||||||
|
* In that case we need to delegate evaluating defaults to original class constructor.
|
||||||
|
* The simplest way to do that - generate a constructor for each set of arguments used for
|
||||||
|
* instantiating annotations, hope there shouldn't be too many of them in each module.
|
||||||
|
*/
|
||||||
|
override fun chooseConstructor(implClass: IrClass, expression: IrConstructorCall) : IrConstructor {
|
||||||
|
val existingValueArguments = (0 until expression.valueArgumentsCount)
|
||||||
|
.filter { expression.getValueArgument(it) != null }
|
||||||
|
.map { expression.symbol.owner.valueParameters[it].name }
|
||||||
|
.toSet()
|
||||||
|
return implClass.constructors.singleOrNull { cons ->
|
||||||
|
cons.valueParameters.map { it.name }.toSet() == existingValueArguments
|
||||||
|
} ?: implClass.addConstructor {
|
||||||
|
startOffset = SYNTHETIC_OFFSET
|
||||||
|
endOffset = SYNTHETIC_OFFSET
|
||||||
|
visibility = DescriptorVisibilities.PUBLIC
|
||||||
|
}.apply {
|
||||||
|
expression.symbol.owner.valueParameters
|
||||||
|
.filter { it.name in existingValueArguments }
|
||||||
|
.forEach { parameter -> addValueParameter(parameter.name.asString(), parameter.type) }
|
||||||
|
createConstructorBody(this, expression.symbol.owner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun implementAnnotationPropertiesAndConstructor(implClass: IrClass, annotationClass: IrClass, generatedConstructor: IrConstructor) {
|
override fun implementAnnotationPropertiesAndConstructor(implClass: IrClass, annotationClass: IrClass, generatedConstructor: IrConstructor) {
|
||||||
require(!annotationClass.isFinalClass) { "Annotation class ${annotationClass.kotlinFqName} shouldn't be final" }
|
require(!annotationClass.isFinalClass) { "Annotation class ${annotationClass.kotlinFqName} shouldn't be final" }
|
||||||
val properties = annotationClass.getAnnotationProperties()
|
val properties = annotationClass.getAnnotationProperties()
|
||||||
properties.forEach { property ->
|
properties.forEach { property ->
|
||||||
val propType = property.getter!!.returnType
|
generatedConstructor.addValueParameter(property.name.asString(), property.getter!!.returnType)
|
||||||
val propName = property.name
|
|
||||||
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() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
createConstructorBody(generatedConstructor, annotationClass.constructors.single())
|
||||||
|
}
|
||||||
|
|
||||||
generatedConstructor.body = context.irFactory.createBlockBody(
|
private fun createConstructorBody(constructor: IrConstructor, delegate: IrConstructor) {
|
||||||
|
/**
|
||||||
|
* We need to delegate to base constructor, instead of calling primary with default values
|
||||||
|
* as default values can be not available. {@see chooseConstructor} for details
|
||||||
|
*/
|
||||||
|
constructor.body = context.irFactory.createBlockBody(
|
||||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, listOf(
|
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, listOf(
|
||||||
IrDelegatingConstructorCallImpl(
|
IrDelegatingConstructorCallImpl(
|
||||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.irBuiltIns.unitType, annotationClass.constructors.single().symbol,
|
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, context.irBuiltIns.unitType, delegate.symbol,
|
||||||
typeArgumentsCount = 0, valueArgumentsCount = generatedConstructor.valueParameters.size
|
typeArgumentsCount = 0, valueArgumentsCount = delegate.valueParameters.size
|
||||||
).apply {
|
).apply {
|
||||||
generatedConstructor.valueParameters.forEach {
|
constructor.valueParameters.forEach { param ->
|
||||||
putValueArgument(it.index, IrGetValueImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, it.symbol))
|
putArgument(delegate.valueParameters.single { it.name == param.name },
|
||||||
|
IrGetValueImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, param.symbol))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val forbidDirectFieldAccessInMethods = true
|
override val forbidDirectFieldAccessInMethods = true
|
||||||
|
|||||||
Reference in New Issue
Block a user