Ji Sungbin
03/13/2023, 7:10 AMIrValueParameter? I’ve tried IrValueParameter#defaultValue but it always returns null.MerlinTHS
03/13/2023, 12:27 PMMerlinTHS
03/13/2023, 12:32 PMvisitSimpleFunction of IrElementVisitorVoid in an IrGenerationExtension but it just worked.Ji Sungbin
03/13/2023, 12:41 PMJi Sungbin
03/15/2023, 6:48 AM@Composable
public fun Success(text: String = "SUCCESS") {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Text(text = text)
}
}
My IrElementVisitorVoid implementation looks like this.
internal class SugarIrVisitor(
private val logger: Logger,
private val addSugarIrData: (data: SugarIrData) -> Unit,
) : IrElementVisitorVoid {
override fun visitModuleFragment(declaration: IrModuleFragment) {
declaration.files.forEach { file ->
file.accept(this, null)
}
}
override fun visitFile(declaration: IrFile) {
declaration.declarations.forEach { item ->
item.accept(this, null)
}
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
if (declaration.name.asString() == "Success") {
val defaultValue = declaration.valueParameters.first().defaultValue!!
logger.warn("Success function was copied")
addSugarIrData(SugarIrData(defaultValue = defaultValue))
}
}
}
But I get an NPE at val defaultValue = declaration.valueParameters.first().defaultValue!!.
Where did I go wrong in my IrElementVisitorVoid implementation?Ji Sungbin
03/15/2023, 7:16 AMIrExpressionBody but an IrExpression? Then it makes sense that an NPE occurred (IrValueParameter#defaultValue is of type IrExpressionBody).MerlinTHS
03/15/2023, 10:09 AMdefaultValue is of type IrExpressionBody. You can find the actual expression you’re passing into it ( which in this case is of type IrConst ) in the expression property of the defaultValue. Can you make sure, that the accessed parameter is actually one you declared in the original signature and not something the Compose Compiler Plugin generates in its lowerings like $composer or $changed? Something like:
fun List<IrValueParameter>.filterGenerated() =
filterNot { it.name.asString().startsWith("$") }Ji Sungbin
03/15/2023, 10:20 AMoverride fun visitSimpleFunction(declaration: IrSimpleFunction) {
if (declaration.name.asString() == "Success") {
declaration.valueParameters.forEach { param ->
logger.warn("name: ${param.name.asString()}, defaultValue: ${param.defaultValue}")
}
}
}
@Composable
public fun Success(text: String = "SUCCESS", really: Boolean = true) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Text(text = text + really.toString())
}
}Ji Sungbin
03/15/2023, 10:23 AMMerlinTHS
03/15/2023, 10:34 AMJi Sungbin
03/15/2023, 10:38 AMMerlinTHS
03/15/2023, 10:42 AMMerlinTHS
03/15/2023, 10:48 AMDefault Arguments
* =================
*
* Composable functions need to have the default expressions executed inside of the group of the
* function. In order to accomplish this, composable functions handle default arguments
* themselves, instead of using the default handling of kotlin. This is also a win because we can
* handle the default arguments without generating an additional function since we do not need to
* worry about callers from java. Generally speaking though, compose handles default arguments
* similarly to kotlin in that we generate a $default bitmask parameter which maps each parameter
* index to a bit on the int. A value of "1" for a given parameter index indicated that that
* value was *not* provided at the callsite, and the default expression should be used instead.
*
* @Composable fun A(x: Int = 0) {
* f(x)
* }
*
* gets transformed into
*
* @Composable fun A(x: Int, $default: Int) {
* val x = if ($default and 0b1 != 0) 0 else x
* f(x)
* }
*
* Note: This transform requires [ComposerParamTransformer] to also be run in order to work
* properly.
*Ji Sungbin
03/15/2023, 10:49 AMAnother possible approach would be visiting the `defaultValue`s before Compose lowers them.I want to try that way. Can I adjust the order of application of the Compiler Plugin? (Maybe FIR? (First Intermediate Representation)… actually I don’t know what FIR is. 😅)
MerlinTHS
03/15/2023, 10:49 AMJi Sungbin
03/15/2023, 10:52 AMJi Sungbin
03/15/2023, 10:53 AMMerlinTHS
03/15/2023, 10:59 AMJi Sungbin
03/15/2023, 11:03 AMMerlinTHS
03/15/2023, 11:11 AMIrGenerationExtension ) with one of the predefined loading orders ( FIRST ).
public final class LoadingOrder {
public static final LoadingOrder ANY = new LoadingOrder();
public static final LoadingOrder FIRST = new LoadingOrder("first");
public static final LoadingOrder LAST = new LoadingOrder("last");
...
}
To do that, you have to use the old and deprecated ComponentRegistrar. It allows you to access the extensionArea of MockProject ( which I think was the reason to deprecate it ) where you can specify the order when registering an extension.
class YourComponentRegistrar : ComponentRegistrar {
override val supportsK2: Boolean
get() = false // Except you're using FIR
override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
) {
project.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName)
.registerExtension(YourIrGenerationExtension(), LoadingOrder.FIRST, project)
}
}Ji Sungbin
03/15/2023, 11:20 AMJi Sungbin
03/15/2023, 11:29 AM