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