Desmond van der Meer
10/17/2025, 4:21 PMYoussef Shoaib [MOD]
10/17/2025, 4:26 PMFirStatusTransformerExtension.Youssef Shoaib [MOD]
10/17/2025, 4:27 PMDesmond van der Meer
10/17/2025, 4:29 PMDesmond van der Meer
10/17/2025, 4:30 PMFirStatusTransformerExtension indeed, now that I look at it. I just wonder how safe that is.Youssef Shoaib [MOD]
10/17/2025, 4:32 PMFirStatusTransformerExtension) add a variable at the beginning of the function.
Yeah its hacky. There's some vanilla Kotlin options you could consider though. Maybe context arguments might suit you better? You could also just have a "magical" function that the user calls:
@SomeAnnotation
fun myFun() {
val magic = magicFunCall()
}
and check that the call happens in the right place in a Fir checker, and replace the call in Ir to the parameterDesmond van der Meer
10/17/2025, 4:38 PMDesmond van der Meer
10/17/2025, 4:38 PMFirStatusTransformerExtension and see if any errors pop up 🙂Desmond van der Meer
10/17/2025, 4:40 PMYoussef Shoaib [MOD]
10/17/2025, 4:41 PMDesmond van der Meer
10/17/2025, 4:41 PMDesmond van der Meer
10/17/2025, 4:43 PMYoussef Shoaib [MOD]
10/17/2025, 4:44 PMinline fun <reified T> callsMagicFunOnTInternally() {
...
magic<T>()
}
gets transformed in Ir into:
inline fun <reified T> callsMagicFunOnTInternally(typeName: String) {
...
typeName
}
and then a call like:
callsMagicFunOnTInternally()
gets replaced in Ir with:
callsMagicFunOnTInternally("some.fq.name")
you don't even need to copy the function in fact. Simply add the new parameter.Desmond van der Meer
10/17/2025, 4:44 PMYoussef Shoaib [MOD]
10/17/2025, 4:45 PMdeepCopyWithSymbols.
Symbol resolution won't fail if the extra parameter is only added in Ir I believe. You might wanna take inspiration from the Compose plugin, which adds an extra parameter to every Composable, and I believe that's done in IR in their caseDesmond van der Meer
10/17/2025, 4:46 PMDesmond van der Meer
10/17/2025, 4:47 PMDesmond van der Meer
10/17/2025, 5:01 PMjava.lang.IllegalStateException: couldn't find inline method Ldev/tebi/shared/logging/LoggerKt;.logger2(Lkotlin/reflect/KClass;)Ldev/tebi/shared/logging/Logger
In this case the logger2(..) function is one defined in another module, which was transformed by the plugin. My plugin also transforms the call-site of this function in the original module. But stuff goes wrong during inlining.Desmond van der Meer
10/17/2025, 5:02 PMDesmond van der Meer
10/17/2025, 5:02 PMYoussef Shoaib [MOD]
10/17/2025, 5:03 PMlogger2 in the current module?Desmond van der Meer
10/17/2025, 5:03 PMDesmond van der Meer
10/17/2025, 5:03 PMDesmond van der Meer
10/17/2025, 5:08 PMorg.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Couldn't inline method call: CALL 'public final fun logger2 <T> (<this>: kotlin.reflect.KClass<T of dev.tebi.shared.logging.LoggerKt.logger2>): dev.tebi.shared.logging.Logger [inline] declared in dev.tebi.shared.logging.LoggerKt' type=dev.tebi.shared.logging.Logger origin=null
Method: null
File is unknown
The root cause java.lang.IllegalStateException was thrown at: org.jetbrains.kotlin.codegen.inline.SourceCompilerForInlineKt.getMethodNode(SourceCompilerForInline.kt:129)
at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCodegen.genInlineCall(IrInlineCodegen.kt:82)
at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCallGenerator.genCall(IrInlineCallGenerator.kt:36)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:589)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:134)
at org.jetbrains.kotlin.ir.expressions.IrCall.accept(IrCall.kt:24)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitVariable(ExpressionCodegen.kt:729)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitVariable(ExpressionCodegen.kt:134)
at org.jetbrains.kotlin.ir.declarations.IrVariable.accept(IrVariable.kt:36)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitStatementContainer(ExpressionCodegen.kt:530)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:535)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:134)
at org.jetbrains.kotlin.ir.expressions.IrBlockBody.accept(IrBlockBody.kt:20)
at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.generate(ExpressionCodegen.kt:232)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.doGenerate(FunctionCodegen.kt:129)
at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:50)
... 44 more
Caused by: java.lang.IllegalStateException: couldn't find inline method Ldev/tebi/shared/logging/LoggerKt;.logger2(Lkotlin/reflect/KClass;)Ldev/tebi/shared/logging/Logger;Desmond van der Meer
10/17/2025, 5:09 PMYoussef Shoaib [MOD]
10/17/2025, 5:09 PMDesmond van der Meer
10/17/2025, 5:10 PMDesmond van der Meer
10/17/2025, 5:10 PMYoussef Shoaib [MOD]
10/17/2025, 5:12 PMDesmond van der Meer
10/17/2025, 5:26 PMYoussef Shoaib [MOD]
10/17/2025, 5:28 PMDesmond van der Meer
10/17/2025, 5:29 PMIrElementTransformerVoidYoussef Shoaib [MOD]
10/17/2025, 5:30 PMDesmond van der Meer
10/17/2025, 5:35 PMDesmond van der Meer
10/17/2025, 5:35 PMDesmond van der Meer
10/17/2025, 8:09 PMYoussef Shoaib [MOD]
10/17/2025, 8:14 PMJust adding parameters for dependencies on the IR stage won't cut it anymore! The compilation fails during the step of creating IR in the first place while trying to deserialize a non-existent function with the original signature.
To mitigate this, Compose copies functions in Kotlin/JS instead of replacing themSo copying will be necessary for JS, exactly the opposite of what I expected
Desmond van der Meer
10/20/2025, 10:53 AM