Compose Compiler는 Composable 함수 본문에 “그룹”을 삽입한다.
“그룹”은 Composable 함수의 현재 상태에 대한 모든 정보를 보존한다.
“그룹”은 Composable 함수의 호출 위치에 대한 정보를 갖는다.
다양한 유형의 그룹을 생성하기 위해 Compiler가 실행 가능한 블록에 아래 논리들을 적용한다.
교체 가능한 그룹 (Replaceable Groups)
e.g.1) Composable 람다식인 경우
fun composableLambda(
composer: Composer,
key: Int,
tracked: Boolean,
block: Any,
): ComposableLambda {
composer.startReplaceableGroup(key) // 교체 가능한 그룹 시작
// Composition을 관련성 있는 정보로 업데이트 하는 코드
val slot = composer.rememberedValue()
val result = if (slot == Composer.Empty) {
val value = ComposableLambdaImpl(key, tracked)
composer.updateRememberedValue(value)
value
} else {
slot as ComposableLambdaImpl
}
result.update(block)
composer.endReplaceableGroup() // 교체 가능한 그룹 닫음
return result
}
e.g.2) @NonRestartableComposable 마킹된 Composable 함수인 경우
// Before Compiler
@NonRestartableComposable
@Composable
fun Foo(x: Int) {
Wat()
}
// After Compiler
@NonRestartableComposable
@Composable
fun Foo(x: Int, $composer: Composer?, $changed: Int) {
$composer.startReplaceableGroup(<>)
Wat($composer, 0)
$composer.endReplaceableGroup()
}
Wat()이 Composable 함수라면 Compiler가 여기에도 “교체 가능한 그룹”을 삽입함e.g.3) 조건부 논리에 따른 Composable 함수 호출인 경우
if (condition) {
Text("Hello")
} else {
Text("World")
}
이동 가능한 그룹 (Movable Groups)
@Composable
fun TalksScreen(talks: List<Talk>) {
Column {
for (talk in talks) {
key(talk.id) { // Unique key
Talk(talk)
}
}
}
}
Talk은 고유한 정체성이 보장되고 “이동 가능한 그룹”이 생성된다.
e.g. Key 함수를 사용했을 때 “이동 가능한 그룹” 추가 예시
// Before Compiler
@Composable
fun Test(value: Int) {
key(value) {
Wrapper {
Leaf("Value ${'$'}value")
}
}
}
// After Compiler
@Composable
fun Test(value: Int, $composer: Composer?, $changed: Int) {
// ..
$composer.startMovableGroup(<>, value)
Wrapper(composableLambda($composer, <>, true) { $composer: Composer?, $changed: Int ->
Leaf("Value %value", $composer, 0)
}, $composer, 0b0110)
$composer.endMovableGroup()
// ..
}
재시작 가능한 그룹 (Restartable Groups)
endRestartGroup()은 nullable한 값을 반환함
@Composable
fun A(x: Int) {
f(x)
}
@Composable
fun A(x: Int, $composer: Composer<*>, $changed: Int) {
$composer.startRestartGroup()
// ..
f(x)
$composer.endRestartGroup()?.updateScope { next ->
A(x, next, $changed or 0b1)
}
}
endRestartGroup()이 null이 아닐 때 동일한 A()호출을 감쌈<aside> 💡
Compose Compiler는 Composable 함수 본문에 “그룹”을 삽입해 특정 상황에서 어떻게 Composable들이 동작하는지 Composition에 알린다.
</aside>
.klib 및 Kotlin/JS를 위한 지원이 Compose Compiler에 추가됨
기존 JVM과 동일하게 함수의 IR을 교체하는 것을 지양하고, 복사본을 생성함
$composable 접미사를 추가// Before Compiler
@Composable
fun Counter() {
..
}
// Transformed
@Decoy(..)
fun Counter() { // 시그니처 유지
illegalDecoyCallException("Counter")
}
@DecoyImplementation(..)
fun Counter$composable( // 시그니처 변경
$composer: Composer,
$changed: Int
) {
// .. transformed code
}
슬롯 테이블