[Compose] Compose K1 compatibility IR linkage workaround
When used from K2, the already published K1 Compose library may have
a cross-module `IrGetField` access to a "stability" field that became
private in K2. This causes linkage errors (either on the partial
linkage level, or later on the Native binaries linkage level).
This workaround is applied during the partial linkage phase. It replaces
such field access by an invocation of the appropriate "stability" getter
function.
diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt
index ff4792d..9023c81 100644
--- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt
+++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/linkage/partial/PartiallyLinkedIrTreePatcher.kt
@@ -14,24 +14,29 @@
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
+import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.Companion.PARTIAL_LINKAGE_RUNTIME_ERROR
+import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
+import org.jetbrains.kotlin.ir.expressions.impl.fromSymbolOwner
import org.jetbrains.kotlin.ir.linkage.partial.*
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase.*
import org.jetbrains.kotlin.ir.overrides.isEffectivelyPrivate
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
-import org.jetbrains.kotlin.ir.types.*
+import org.jetbrains.kotlin.ir.types.IrSimpleType
+import org.jetbrains.kotlin.ir.types.IrType
+import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.*
-import org.jetbrains.kotlin.ir.util.isSubtypeOfClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
import org.jetbrains.kotlin.utils.compact
@@ -508,6 +513,46 @@
checkReferencedDeclaration(symbol)
}
+ override fun visitGetField(expression: IrGetField): IrExpression {
+ var stabilityFieldGetterCall: IrCall? = null
+
+ val newExpression = expression.maybeThrowLinkageError {
+ val partialLinkageCase = checkReferencedDeclaration(symbol)
+
+ if (partialLinkageCase !is ExpressionHasInaccessibleDeclaration)
+ return@maybeThrowLinkageError partialLinkageCase
+
+ val getInaccessibleFieldExpression = partialLinkageCase.expression as? IrGetField
+ val maybeComposeStabilityField = getInaccessibleFieldExpression?.symbol?.takeIf { it.isBound }?.owner
+ ?: return@maybeThrowLinkageError partialLinkageCase
+
+ val fieldName = maybeComposeStabilityField.name.asString()
+ if (!fieldName.endsWith(COMPOSE_STABILITY_FIELD_SUFFIX))
+ return@maybeThrowLinkageError partialLinkageCase
+
+ val composeStabilityGetterName =
+ Name.identifier(fieldName.removeSuffix(COMPOSE_STABILITY_FIELD_SUFFIX) + COMPOSE_STABILITY_GETTER_SUFFIX)
+
+ val composeStabilityGetter = (maybeComposeStabilityField.parent as? IrDeclarationContainer)?.let { container ->
+ container.findDeclaration<IrSimpleFunction> { it.name == composeStabilityGetterName }
+ } ?: return@maybeThrowLinkageError partialLinkageCase
+
+ stabilityFieldGetterCall = IrCallImpl.fromSymbolOwner(
+ UNDEFINED_OFFSET,
+ UNDEFINED_OFFSET,
+ getInaccessibleFieldExpression.type,
+ composeStabilityGetter.symbol
+ )
+
+ null
+ }
+
+ return if (newExpression === expression && stabilityFieldGetterCall != null)
+ stabilityFieldGetterCall
+ else
+ newExpression
+ }
+
override fun visitClassReference(expression: IrClassReference) = expression.maybeThrowLinkageError {
checkReferencedDeclaration(symbol) ?: checkExpressionType(classType)
}
@@ -1216,5 +1261,8 @@
}
private val REPLACE_WITH_CONSTRUCTOR_EXPRESSION_FIELD_FQN = FqName("kotlin.ReplaceWith.<init>.expression")
+
+ private const val COMPOSE_STABILITY_FIELD_SUFFIX = $$"$stable"
+ private const val COMPOSE_STABILITY_GETTER_SUFFIX = $$"$stableprop_getter"
}
}