Fix a bug preventing the usage of default values in compose/js
Expressions for default values can use other parameters.
```fun Foo(a: String, b: String = a) {...}```
In such cases we need to ensure that default values expressions
use parameters of the new function (new/copied value parameters).
The change is only for kotlin/js targets (DecoysTransformer is k/js specific)
Test: tested locally in compose-jb fork
Change-Id: I4d174f0d335902911b284c0c1b3bd5f0a485ccf9
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt
index 8038409..b6b129c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt
@@ -43,6 +43,10 @@
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.declarations.IrValueParameter
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
+import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
@@ -53,6 +57,8 @@
import org.jetbrains.kotlin.ir.util.hasDefaultValue
import org.jetbrains.kotlin.ir.util.isLocal
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingTrace
@@ -218,9 +224,43 @@
newFunction.addDecoyImplementationAnnotation(newName.asString(), original.getSignatureId())
+ newFunction.valueParameters.forEach {
+ it.defaultValue?.transformDefaultValue(
+ originalFunction = original,
+ newFunction = newFunction
+ )
+ }
+
return newFunction
}
+ /**
+ * Expressions for default values can use other parameters.
+ * In such cases we need to ensure that default values expressions use parameters of the new
+ * function (new/copied value parameters).
+ *
+ * Example:
+ * fun Foo(a: String, b: String = a) {...}
+ */
+ private fun IrExpressionBody.transformDefaultValue(
+ originalFunction: IrFunction,
+ newFunction: IrFunction
+ ) {
+ transformChildrenVoid(object : IrElementTransformerVoid() {
+ override fun visitGetValue(expression: IrGetValue): IrExpression {
+ val original = super.visitGetValue(expression)
+ val valueParameter =
+ (expression.symbol.owner as? IrValueParameter) ?: return original
+
+ val parameterIndex = valueParameter.index
+ if (parameterIndex < 0 || valueParameter.parent != originalFunction) {
+ return super.visitGetValue(expression)
+ }
+ return irGet(newFunction.valueParameters[parameterIndex])
+ }
+ })
+ }
+
private fun IrFunction.stubBody() {
body = DeclarationIrBuilder(context, symbol).irBlockBody {
+ irReturn(