Jetpack ComposeのSlotTableへの変更の適応のコードリーディング

https://qiita.com/takahirom/items/8e978eeb6d85bf48a330

の続きです。

今度まとめた記事を書く予定なので、この記事は読まなくていいです。

前回までで、recomposeの実装を読んでいくことができました。
SlotTableに反映していくところつまり、4を呼んでいきます。


1: recordComposerModificationsLocked() を呼ぶ。Recomposer.snapshotInvalidationsを見て、IdentityScopeMapを使って変更点に対しての影響を受けるスコープを取得し、Recomposer.compositionInvalidationsやComposition.invalidationsというフィールドに変更を入れる。
2: compositionInvalidationsをtoRecomposeに入れる。
3: toRecomposeに対して、performRecompose(composition, modifiedValues)、(内部でdoCompose())することでtoApplyを作成。SlotTableを見て、ここで木を見ていき、木への変更をrecordしていく。
4: recordされたtoApplyに対してapplyChanges()をすることで、SlotTableの書き換えが走る。 (予想。コード未読)

runRecomposeAndApplyChanges()で今までアプリ側の関数を呼び出したりして、SlotTableへの変更をchangesというプロパティに入れていきました。
このchangeが入っているのが、toApplyになります。このapplyChanges()を呼んで、SlotTableへの変更を行っていくところを見ていきましょう。

```
*Recomposer.runRecomposeAndApplyChanges()* ← Now reading
```


toApplyは size = 1 でperformRecompose()で呼び出した結果が入っている。
一個だけ入っているが、この中に8個SlotTableに反映すべきchagnesが入っている。

```kotlin
                    if (toApply.isNotEmpty()) {
                        changeCount++ // 0から1に。

                        // Perform apply changes
                        try {
                            toApply.fastForEach { composition ->
                                composition.applyChanges()
                            }
                        } finally {
                            toApply.clear()
                        }
                    }

                    synchronized(stateLock) {
                        deriveStateLocked()
                    }
```

# CompositionImpl.applyChanges()

```
Recomposer.runRecomposeAndApplyChanges()
└── *CompositionImpl.applyChanges()* ← Now reading
```


```kotlin
    override fun applyChanges() {
        synchronized(lock) {
// abandonSetは何も入っていない。
// RememberEventDispatcherはComposable関数側にあるLaunchedEffectなどの実装を呼び出すための仕組みのように見えます。
            val manager = RememberEventDispatcher(abandonSet)
            try {
// Applyerに変更が始まることを通知する。ここでは何もしない。
                applier.onBeginChanges()

                // Apply all changes
// openして終わったらcloseする。
                slotTable.write { slots ->
                    val applier = applier
                    changes.fastForEach { change ->
                        change(applier, slots, manager)
                    }
                    changes.clear()
                }

                applier.onEndChanges()

                // Side effects run after lifecycle observers so that any remembered objects
                // that implement RememberObserver receive onRemembered before a side effect
                // that captured it and operates on it can run.
                manager.dispatchRememberObservers()
                manager.dispatchSideEffects()

                if (pendingInvalidScopes) {
                    pendingInvalidScopes = false
                    observations.removeValueIf { scope -> !scope.valid }
                    derivedStates.removeValueIf { derivedValue -> derivedValue !in observations }
                }
            } finally {
                manager.dispatchAbandons()
            }
            drainPendingModificationsLocked()
        }
    }
```


```kotlin
    inline fun <T> write(block: (writer: SlotWriter) -> T): T = openWriter()
        .let { writer ->
            try {
                block(writer)
            } finally {
                writer.close()
            }
        
```

このchangesを一つずつ行っていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/9a8440fd-44d5-5daa-f54b-7fe47689fb08.png)

内容は前回の記事から。
まずはこのadvancebyになるのかな？ :eyes: 

```
0 = record { _, slots, _ -> slots.advanceBy(distance) }
10 進める

1 = private val startRootGroup: Change = { _, slots, _ -> slots.ensureStarted(0) }
ensureStartedGroupが始まったので、いろんなWriterのcurrentGroupなどを更新をかけるもの。で、引数が0なのでrootでやる。

2 = recordSlotTableOperation { _, slots, _ -> slots.ensureStarted(anchor) }
ensureStartedGroupが始まったので、いろんなWriterのcurrentGroupなどを更新をかけるもの。で、引数がanchorなのでcurrent groupでやる

3 = private val removeCurrentGroupInstance: Change = { _, slots, rememberManager ->
slots.removeCurrentGroup(rememberManager)
}

4 = { _, slots, _ -> slots.endGroup() }

5 = { applier, _, _ -> applier.remove(removeIndex, count) }

6 = { _, slots, _ -> slots.advanceBy(distance) }// (distance = 3)
7 = { _, slots, _ -> slots.endGroup() }
```

## 現状整理

これからテーブルに変更を加えていくので、現状を記録しておきます。

```
slotTable:
androidx.compose.runtime.SlotTable@5935541
Group(0) key=100, nodes=2, size=16, slots=[0: {}]
 Group(1) key=1000, nodes=2, size=15
  Group(2) key=200, nodes=2, size=14 objectKey=OpaqueKey(key=provider)
   Group(3) key=-985533281, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@4fb4ae6, androidx.compose.runtime.internal.ComposableLambdaImpl@3b52827]
    Group(4) key=-337788286, nodes=2, size=12 aux=C(Content), slots=[5: androidx.compose.runtime.RecomposeScopeImpl@b882ad4]
     Group(5) key=-3687241, nodes=0, size=1 aux=C(remember):Composables.kt#9igjgp, slots=[7: MutableState(value=false)@167707773]
     Group(6) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[9: MutableState(value=false)@167707773, Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>, java.lang.Object>]
     Group(7) key=1036442245, nodes=0, size=2 aux=C(LaunchedEffect)P(1)336@14101L58:Effects.kt#9igjgp
      Group(8) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[13: kotlin.Unit, androidx.compose.runtime.LaunchedEffectImpl@8d3f428]
     Group(9) key=-337788139, nodes=1, size=4
      Group(10) key=1815931685, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@a4ed0be]
       Group(11) key=1546164276, nodes=1, size=2 aux=C(ReusableComposeNode):Composables.kt#9igjgp
        Group(12) key=125, nodes=0, size=1 node=Node1(name=node1), slots=[18: node1]
     Group(13) key=1815931958, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@81cf51f]
      Group(14) key=1546164276, nodes=1, size=2 aux=C(ReusableComposeNode):Composables.kt#9igjgp
       Group(15) key=125, nodes=0, size=1 node=Node2(name=node2), slots=[22: node2]
```

```kotlin
"SlotWriter.toString():"+toString()+
        "\ngroupsAsString():\n"+groupsAsString()
```

```
SlotWriter.toString():SlotWriter(current = 0 end=16 size = 16 gap=16-32)
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^9: key=1815931685, nodes=1, dataAnchor=15, parentAnchor=9)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=16, parentAnchor=10)
Group(  12#1^11: key=125, nodes=0, dataAnchor=17, parentAnchor=11)
Group(  13#3^4: key=1815931958, nodes=1, dataAnchor=19, parentAnchor=4)
Group(  14#2^13: key=1546164276, nodes=1, dataAnchor=20, parentAnchor=13)
Group(  15#1^14: key=125, nodes=0, dataAnchor=21, parentAnchor=14)
```


## デバッガーでchangeごとの動きを見てみる

ちょっと普通に見ていくとけっこう大変なので、どのあたりを読むべきかのあたりをつけるために、デバッガーで動きを見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/05e6043c-fad1-aa69-9d0f-c5499634a807.png)
デバッガーで以下のコードが実行されるようにします。

```kotlin
val slotWriter: SlotWriter = slots
val nodeCountStackString = ""
for (index in 0 until slotWriter.nodeCountStack.size) {
    nodeCountStackString += slotWriter.nodeCountStack.peek(index).toString() + ","
}
val startStackString = ""
for (index in 0 until slotWriter.startStack.size) {
    startStackString += slotWriter.startStack.peek(index).toString() + ","
}
val endStackString = ""
for (index in 0 until slotWriter.endStack.size) {
    endStackString += slotWriter.endStack.peek(index).toString() + ","
}
"before${changes.indexOf(change)}" +
        "\nSlotWriter.toString():" + slotWriter.toString() +
        "\nnodeCountStack.size:" + slotWriter.nodeCountStack.size + " nodeCountStack:" + nodeCountStackString +
        "\nstartStack.size:" + slotWriter.startStack.size + " startStack:" + startStackString +
        "\nendStack.size:" + slotWriter.endStack.size + " endStack:" + endStackString +
        "\nnodeCount:${slotWriter.nodeCount}" +
        "\ngroupsAsString():\n" + slotWriter.groupsAsString()
```


```
before0
SlotWriter.toString():SlotWriter(current = 0 end=16 size = 16 gap=16-32)
nodeCountStack.size:0 nodeCountStack:
startStack.size:0 startStack:
endStack.size:0 endStack:
nodeCount:0
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^9: key=1815931685, nodes=1, dataAnchor=15, parentAnchor=9)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=16, parentAnchor=10)
Group(  12#1^11: key=125, nodes=0, dataAnchor=17, parentAnchor=11)
Group(  13#3^4: key=1815931958, nodes=1, dataAnchor=19, parentAnchor=4)
Group(  14#2^13: key=1546164276, nodes=1, dataAnchor=20, parentAnchor=13)
Group(  15#1^14: key=125, nodes=0, dataAnchor=21, parentAnchor=14)

0 = record { _, slots, _ -> slots.advanceBy(distance) }
10 進める

before1
SlotWriter.toString():SlotWriter(current = 10 end=16 size = 16 gap=16-32)
nodeCountStack.size:0 nodeCountStack:
startStack.size:0 startStack:
endStack.size:0 endStack:
nodeCount:0
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^9: key=1815931685, nodes=1, dataAnchor=15, parentAnchor=9)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=16, parentAnchor=10)
Group(  12#1^11: key=125, nodes=0, dataAnchor=17, parentAnchor=11)
Group(  13#3^4: key=1815931958, nodes=1, dataAnchor=19, parentAnchor=4)
Group(  14#2^13: key=1546164276, nodes=1, dataAnchor=20, parentAnchor=13)
Group(  15#1^14: key=125, nodes=0, dataAnchor=21, parentAnchor=14)



1 = private val startRootGroup: Change = { _, slots, _ -> slots.ensureStarted(0) }
ensureStartedGroupが始まったので、いろんなWriterのcurrentGroupなどを更新をかけるもの。で、引数が0なのでrootでやる。

before2
SlotWriter.toString():SlotWriter(current = 10 end=16 size = 16 gap=16-32)
nodeCountStack.size:1 nodeCountStack:0,
startStack.size:1 startStack:-1,
endStack.size:1 endStack:0,
nodeCount:2
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^9: key=1815931685, nodes=1, dataAnchor=15, parentAnchor=9)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=16, parentAnchor=10)
Group(  12#1^11: key=125, nodes=0, dataAnchor=17, parentAnchor=11)
Group(  13#3^4: key=1815931958, nodes=1, dataAnchor=19, parentAnchor=4)
Group(  14#2^13: key=1546164276, nodes=1, dataAnchor=20, parentAnchor=13)
Group(  15#1^14: key=125, nodes=0, dataAnchor=21, parentAnchor=14)


2 = recordSlotTableOperation { _, slots, _ -> slots.ensureStarted(anchor) }
ensureStartedGroupが始まったので、いろんなWriterのcurrentGroupなどを更新をかけるもの。で、引数がanchorなのでcurrent groupでやる


before3
SlotWriter.toString():SlotWriter(current = 10 end=13 size = 16 gap=16-32)
nodeCountStack.size:2 nodeCountStack:0,2,
startStack.size:2 startStack:-1,0,
endStack.size:2 endStack:0,0,
nodeCount:1
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^9: key=1815931685, nodes=1, dataAnchor=15, parentAnchor=9)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=16, parentAnchor=10)
Group(  12#1^11: key=125, nodes=0, dataAnchor=17, parentAnchor=11)
Group(  13#3^4: key=1815931958, nodes=1, dataAnchor=19, parentAnchor=4)
Group(  14#2^13: key=1546164276, nodes=1, dataAnchor=20, parentAnchor=13)
Group(  15#1^14: key=125, nodes=0, dataAnchor=21, parentAnchor=14)

3 = private val removeCurrentGroupInstance: Change = { _, slots, rememberManager ->
slots.removeCurrentGroup(rememberManager)
}


before4
SlotWriter.toString():SlotWriter(current = 10 end=10 size = 13 gap=10-29)
nodeCountStack.size:2 nodeCountStack:0,2,
startStack.size:2 startStack:-1,0,
endStack.size:2 endStack:0,0,
nodeCount:0
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^4: key=1815931958, nodes=1, dataAnchor=-5, parentAnchor=4)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=-4, parentAnchor=-5)
Group(  12#1^11: key=125, nodes=0, dataAnchor=-3, parentAnchor=-4)

4 = { _, slots, _ -> slots.endGroup() }

before5
SlotWriter.toString():SlotWriter(current = 10 end=13 size = 13 gap=10-29)
nodeCountStack.size:1 nodeCountStack:0,
startStack.size:1 startStack:-1,
endStack.size:1 endStack:0,
nodeCount:1
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#12^0: key=1000, nodes=1, dataAnchor=1, parentAnchor=0)
Group(   2#11^1: key=200, nodes=1, dataAnchor=1, parentAnchor=1)
Group(   3#10^2: key=-985533281, nodes=1, dataAnchor=2, parentAnchor=2)
Group(   4#9^3: key=-337788286, nodes=1, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#1^4: key=-337788139, nodes=0, dataAnchor=15, parentAnchor=4)
Group(  10#3^4: key=1815931958, nodes=1, dataAnchor=-5, parentAnchor=4)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=-4, parentAnchor=-5)
Group(  12#1^11: key=125, nodes=0, dataAnchor=-3, parentAnchor=-4)

5 = { applier, _, _ -> applier.remove(removeIndex, count) }

before6
SlotWriter.toString():SlotWriter(current = 10 end=13 size = 13 gap=10-29)
nodeCountStack.size:1 nodeCountStack:0,
startStack.size:1 startStack:-1,
endStack.size:1 endStack:0,
nodeCount:1
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#12^0: key=1000, nodes=1, dataAnchor=1, parentAnchor=0)
Group(   2#11^1: key=200, nodes=1, dataAnchor=1, parentAnchor=1)
Group(   3#10^2: key=-985533281, nodes=1, dataAnchor=2, parentAnchor=2)
Group(   4#9^3: key=-337788286, nodes=1, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#1^4: key=-337788139, nodes=0, dataAnchor=15, parentAnchor=4)
Group(  10#3^4: key=1815931958, nodes=1, dataAnchor=-5, parentAnchor=4)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=-4, parentAnchor=-5)
Group(  12#1^11: key=125, nodes=0, dataAnchor=-3, parentAnchor=-4)

6 = { _, slots, _ -> slots.advanceBy(distance) }// (distance = 3)

before7
SlotWriter.toString():SlotWriter(current = 13 end=13 size = 13 gap=10-29)
nodeCountStack.size:1 nodeCountStack:0,
startStack.size:1 startStack:-1,
endStack.size:1 endStack:0,
nodeCount:1
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#12^0: key=1000, nodes=1, dataAnchor=1, parentAnchor=0)
Group(   2#11^1: key=200, nodes=1, dataAnchor=1, parentAnchor=1)
Group(   3#10^2: key=-985533281, nodes=1, dataAnchor=2, parentAnchor=2)
Group(   4#9^3: key=-337788286, nodes=1, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#1^4: key=-337788139, nodes=0, dataAnchor=15, parentAnchor=4)
Group(  10#3^4: key=1815931958, nodes=1, dataAnchor=-5, parentAnchor=4)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=-4, parentAnchor=-5)
Group(  12#1^11: key=125, nodes=0, dataAnchor=-3, parentAnchor=-4)

7 = { _, slots, _ -> slots.endGroup() }

after end
SlotWriter.toString():SlotWriter(current = 13 end=13 size = 13 gap=10-29)
nodeCountStack.size:0 nodeCountStack:
startStack.size:0 startStack:
endStack.size:0 endStack:
nodeCount:-1
groupsAsString():
Group(   0#13^-1: key=100, nodes=1, dataAnchor=0, parentAnchor=-1)
Group(   1#12^0: key=1000, nodes=1, dataAnchor=1, parentAnchor=0)
Group(   2#11^1: key=200, nodes=1, dataAnchor=1, parentAnchor=1)
Group(   3#10^2: key=-985533281, nodes=1, dataAnchor=2, parentAnchor=2)
Group(   4#9^3: key=-337788286, nodes=1, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#1^4: key=-337788139, nodes=0, dataAnchor=15, parentAnchor=4)
Group(  10#3^4: key=1815931958, nodes=1, dataAnchor=-5, parentAnchor=4)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=-4, parentAnchor=-5)
Group(  12#1^11: key=125, nodes=0, dataAnchor=-3, parentAnchor=-4)
```


予想通りではありますが、

```
3 = private val removeCurrentGroupInstance: Change = { _, slots, rememberManager ->
slots.removeCurrentGroup(rememberManager)
}
```

で激しい動きをします。

なので、途中まで以下は読んじゃいましたが、 `slots.removeCurrentGroup(rememberManager)`を読んでいきましょう。

<details>
<p>

## changes[0] = SlotTable.advancedBy(10)

前のフェーズで記録されていた操作を一個ずつ行っていきます。

`record { _, slots, _ -> slots.advanceBy(distance) }`

```
Recomposer.runRecomposeAndApplyChanges()
└── CompositionImpl.applyChanges()
    └── *SlotTable.advancedBy(10)* ← Now reading
```

currentGroupというカーソルみたいなものがあって10進めるみたい。


```kotlin
    /**
     * Advance [currentGroup] by [amount]. The [currentGroup] group cannot be advanced outside the
     * currently started [parent].
     */
    fun advanceBy(amount: Int) {
        require(amount >= 0) { "Cannot seek backwards" }
        check(insertCount <= 0) { "Cannot call seek() while inserting" }
// currentGroupは0 amountは10なので、indexは10になる。
        val index = currentGroup + amount
        @Suppress("ConvertTwoComparisonsToRangeCheck")
        runtimeCheck(index >= parent && index <= currentGroupEnd) {
            "Cannot seek outside the current group ($parent-$currentGroupEnd)"
        }
// currentGroupは10になる。
        this.currentGroup = index
        val newSlot = groups.dataIndex(groupIndexToAddress(index))
// currentSlotは15。currentSlotEndも15。
        this.currentSlot = newSlot
        this.currentSlotEnd = newSlot
    }
```

```
slotTable:
androidx.compose.runtime.SlotTable@5935541
Group(0) key=100, nodes=2, size=16, slots=[0: {}]
 Group(1) key=1000, nodes=2, size=15
  Group(2) key=200, nodes=2, size=14 objectKey=OpaqueKey(key=provider)
   Group(3) key=-985533281, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@4fb4ae6, androidx.compose.runtime.internal.ComposableLambdaImpl@3b52827]
    Group(4) key=-337788286, nodes=2, size=12 aux=C(Content), slots=[5: androidx.compose.runtime.RecomposeScopeImpl@b882ad4]
     Group(5) key=-3687241, nodes=0, size=1 aux=C(remember):Composables.kt#9igjgp, slots=[7: MutableState(value=false)@167707773]
     Group(6) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[9: MutableState(value=false)@167707773, Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>, java.lang.Object>]
     Group(7) key=1036442245, nodes=0, size=2 aux=C(LaunchedEffect)P(1)336@14101L58:Effects.kt#9igjgp
      Group(8) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[13: kotlin.Unit, androidx.compose.runtime.LaunchedEffectImpl@8d3f428]
     Group(9) key=-337788139, nodes=1, size=4
// ↓ ここがcurrentGroup currentSlotは15。currentSlotEndも15。
      Group(10) key=1815931685, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@a4ed0be]
       Group(11) key=1546164276, nodes=1, size=2 aux=C(ReusableComposeNode):Composables.kt#9igjgp
        Group(12) key=125, nodes=0, size=1 node=Node1(name=node1), slots=[18: node1]
     Group(13) key=1815931958, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@81cf51f]
      Group(14) key=1546164276, nodes=1, size=2 aux=C(ReusableComposeNode):Composables.kt#9igjgp
       Group(15) key=125, nodes=0, size=1 node=Node2(name=node2), slots=[22: node2]
```

##  changes[1] = { _, slots, _ -> slots.ensureStarted(0) }

`private val startRootGroup: Change = { _, slots, _ -> slots.ensureStarted(0) }`



```kotlin:SlotWriter
    /**
     * If the start of a group was skipped using [skip], calling [ensureStarted] puts the writer
     * into the same state as if [startGroup] or [startNode] was called on the group starting at
     * [index]. If, after starting, the group, [currentGroup] is not a the end of the group or
     * [currentGroup] is not at the start of a group for which [index] is not location the parent
     * group, an exception is thrown.
     *
     * Calling [ensureStarted] implies that an [endGroup] should be called once the end of the
     * group is reached.
     * [skip]を使ってグループの線っとうがスキップされた場合、[ensureStarted]を呼んでwriterを同じ状態にする。
     * どのような状態かというとindexでグループの開始で[startGroup] or [startNode]が呼ばれたかのような状態。
     * 開始後に[currentGroup]がgroupの終わりか、[index]が[currentGroup]の親グループのロケーションでない時に例外を投げる
     */
    fun ensureStarted(index: Int) {
        require(insertCount <= 0) { "Cannot call ensureStarted() while inserting" }
        val parent = parent
// parentは-1が入っている
// indexは0
        if (parent != index) {
            // The new parent a child of the current group.
            @Suppress("ConvertTwoComparisonsToRangeCheck")
            require(index >= parent && index < currentGroupEnd) {
                "Started group must be a subgroup of the group at $parent"
            }

            val oldCurrent = currentGroup
            val oldCurrentSlot = currentSlot
            val oldCurrentSlotEnd = currentSlotEnd
// currentGroupが10から0に戻される
            currentGroup = index
            startGroup()
            currentGroup = oldCurrent
            currentSlot = oldCurrentSlot
            currentSlotEnd = oldCurrentSlotEnd
        }
    }

    fun startGroup() {
        require(insertCount == 0) { "Key must be supplied when inserting" }
        startGroup(key = 0, objectKey = Composer.Empty, isNode = false, aux = Composer.Empty)
    }
```

## startGroup()

```
Recomposer.runRecomposeAndApplyChanges()
└── CompositionImpl.applyChanges()
    ├── SlotWriter.advancedBy(10)
    └── SlotWriter.ensureStarted(0)
        └── startGroup()
            └── *startGroup(key=0, objectKey=Empty, isNode=false, aux=Empty)* ← Now reading
```

```kotlin
    private fun startGroup(key: Int, objectKey: Any?, isNode: Boolean, aux: Any?) {
        val inserting = insertCount > 0
        nodeCountStack.push(nodeCount)

        currentGroupEnd = if (inserting) {
            insertGroups(1)
            val current = currentGroup
            val currentAddress = groupIndexToAddress(current)
            val hasObjectKey = objectKey !== Composer.Empty
            val hasAux = !isNode && aux !== Composer.Empty
            groups.initGroup(
                address = currentAddress,
                key = key,
                isNode = isNode,
                hasDataKey = hasObjectKey,
                hasData = hasAux,
                parentAnchor = parent,
                dataAnchor = currentSlot
            )
            currentSlotEnd = currentSlot

            val dataSlotsNeeded = (if (isNode) 1 else 0) +
                (if (hasObjectKey) 1 else 0) +
                (if (hasAux) 1 else 0)
            if (dataSlotsNeeded > 0) {
                insertSlots(dataSlotsNeeded, current)
                val slots = slots
                var currentSlot = currentSlot
                if (isNode) slots[currentSlot++] = aux
                if (hasObjectKey) slots[currentSlot++] = objectKey
                if (hasAux) slots[currentSlot++] = aux
                this.currentSlot = currentSlot
            }
            nodeCount = 0
            val newCurrent = current + 1
            this.parent = current
            this.currentGroup = newCurrent
            newCurrent
        } else {
            val previousParent = parent
            startStack.push(previousParent)
            saveCurrentGroupEnd()
            val currentGroup = currentGroup
            val currentGroupAddress = groupIndexToAddress(currentGroup)
            if (aux != Composer.Empty) {
                if (isNode)
                    updateNode(aux)
                else
                    updateAux(aux)
            }
            currentSlot = groups.slotIndex(currentGroupAddress)
            currentSlotEnd = groups.dataIndex(
                groupIndexToAddress(this.currentGroup + 1)
            )
            nodeCount = groups.nodeCount(currentGroupAddress)

            this.parent = currentGroup
            this.currentGroup = currentGroup + 1
            currentGroup + groups.groupSize(currentGroupAddress)
        }
    }
```

</details>


## changes[3] = slots.removeCurrentGroup(rememberManager)


以下の状態変化がどのように起こるのか見ていきましょう。

```
before3
SlotWriter.toString():SlotWriter(current = 10 end=13 size = 16 gap=16-32)
nodeCountStack.size:2 nodeCountStack:0,2,
startStack.size:2 startStack:-1,0,
endStack.size:2 endStack:0,0,
nodeCount:1
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^9: key=1815931685, nodes=1, dataAnchor=15, parentAnchor=9)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=16, parentAnchor=10)
Group(  12#1^11: key=125, nodes=0, dataAnchor=17, parentAnchor=11)
Group(  13#3^4: key=1815931958, nodes=1, dataAnchor=19, parentAnchor=4)
Group(  14#2^13: key=1546164276, nodes=1, dataAnchor=20, parentAnchor=13)
Group(  15#1^14: key=125, nodes=0, dataAnchor=21, parentAnchor=14)

3 = private val removeCurrentGroupInstance: Change = { _, slots, rememberManager ->
slots.removeCurrentGroup(rememberManager)
}


before4
SlotWriter.toString():SlotWriter(current = 10 end=10 size = 13 gap=10-29)
nodeCountStack.size:2 nodeCountStack:0,2,
startStack.size:2 startStack:-1,0,
endStack.size:2 endStack:0,0,
nodeCount:0
groupsAsString():
Group(   0#16^-1: key=100, nodes=2, dataAnchor=0, parentAnchor=-1)
Group(   1#15^0: key=1000, nodes=2, dataAnchor=1, parentAnchor=0)
Group(   2#14^1: key=200, nodes=2, dataAnchor=1, parentAnchor=1)
Group(   3#13^2: key=-985533281, nodes=2, dataAnchor=2, parentAnchor=2)
Group(   4#12^3: key=-337788286, nodes=2, dataAnchor=4, parentAnchor=3)
Group(   5#1^4: key=-3687241, nodes=0, dataAnchor=6, parentAnchor=4)
Group(   6#1^4: key=-3686930, nodes=0, dataAnchor=8, parentAnchor=4)
Group(   7#2^4: key=1036442245, nodes=0, dataAnchor=11, parentAnchor=4)
Group(   8#1^7: key=-3686930, nodes=0, dataAnchor=12, parentAnchor=7)
Group(   9#4^4: key=-337788139, nodes=1, dataAnchor=15, parentAnchor=4)
Group(  10#3^4: key=1815931958, nodes=1, dataAnchor=-5, parentAnchor=4)
Group(  11#2^10: key=1546164276, nodes=1, dataAnchor=-4, parentAnchor=-5)
Group(  12#1^11: key=125, nodes=0, dataAnchor=-3, parentAnchor=-4)
```



```kotlin:Composer.kt
internal fun SlotWriter.removeCurrentGroup(rememberManager: RememberManager) {
    // Notify the lifecycle manager of any observers leaving the slot table
    // The notification order should ensure that listeners are notified of leaving
    // in opposite order that they are notified of entering.

    // To ensure this order, we call `enters` as a pre-order traversal
    // of the group tree, and then call `leaves` in the inverse order.
// まず、変更があれば、pendingInvalidScopesをtrueにするのと、Rememberがそこにいればそれを忘れるということを行います。
    for (slot in groupSlots()) {
        @Suppress("DEPRECATION")
        when (slot) {
            is RememberObserver -> {
                rememberManager.forgetting(slot)
            }
            is RecomposeScopeImpl -> {
// ここに1度だけ入り、`pendingInvalidScopes = true`をtrueにして、
// RecomposeScopeImplがもつcompositionをnullにします。
                val composition = slot.composition
                if (composition != null) {
                    composition.pendingInvalidScopes = true
                    slot.composition = null
                }
            }
        }
    }

    removeGroup()
}

    /**
     * Returns an iterator for all the slots of group and all the children of the group.
     */
    fun groupSlots(): Iterator<Any?> {
        val start = groups.dataIndex(groupIndexToAddress(currentGroup))
        val end = groups.dataIndex(
            groupIndexToAddress(currentGroup + groupSize(currentGroup))
        )
        return object : Iterator<Any?> {
            var current = start
            override fun hasNext(): Boolean = current < end
            override fun next(): Any? =
                if (hasNext()) slots[dataIndexToDataAddress(current++)] else null
        }
    }
```


```
Recomposer.runRecomposeAndApplyChanges()
└── CompositionImpl.applyChanges()
    ├── SlotWriter.advancedBy(10)
    ├── SlotWriter.ensureStarted(0)
    │   └── SlotWriter.startGroup()
    │       └── SlotWriter.startGroup(key=0, objectKey=Empty, isNode=false, aux=Empty)
    │           ├── SlotWriter.saveCurrentGroupEnd()
    │           ├── SlotWriter.groupIndexToAddress(0)
    │           └── IntArray.slotIndex(0)
    │               ├── IntArray.slotAnchor(0)
    │               └── SlotWriter.dataAnchorToDataIndex(0)
    ├── ...
    └── SlotWriter.removeCurrentGroup()
        └── SlotWriter.removeGroup()
            ├── SlotWriter.skipGroup()
            └── SlotWriter.removeGroups(10, 3)
                └── *moveGroupGapTo(10)* ← Now reading
```

以下でデバッグ
`"<- group $gapStart -><- gap $gapLen -><- group ${capacity-gapStart-gapLen} ->"`

元はgroupsの中身はこれになっていて、
<- group 16 -><- gap 16 -><- group 0 ->
以下の関数を通過するとgroupsは以下のようになる
<- group 10 -><- gap 16 -><- group 6 ->

```kotlin
    /**
     * Move the gap in [groups] to [index].
     * groupsにあるgapをindexに移動する。
     */
    private fun moveGroupGapTo(index: Int) {
        val gapLen = groupGapLen
        val gapStart = groupGapStart
// gapStart = 16
// index = 10
        if (gapStart != index) {
            if (anchors.isNotEmpty()) updateAnchors(gapStart, index)
            if (gapLen > 0) {
// gapLen = 16
                val groups = groups
                // Here physical is used to mean an index of the actual first int of the group in the
                // array as opposed ot the logical address which is in groups of Group_Field_Size
                // integers. IntArray.copyInto expects physical indexes.
                val groupPhysicalAddress = index * Group_Fields_Size
                val groupPhysicalGapLen = gapLen * Group_Fields_Size
                val groupPhysicalGapStart = gapStart * Group_Fields_Size
                if (index < gapStart) {
// gapStart = 16
// index = 10
                    groups.copyInto(
                        destination = groups,
// groupPhysicalAddress + groupPhysicalGapLen = 130
                        destinationOffset = groupPhysicalAddress + groupPhysicalGapLen,
// groupPhysicalAddress = 50
                        startIndex = groupPhysicalAddress,
// groupPhysicalGapStart = 80
                        endIndex = groupPhysicalGapStart
                    )
// つまりgroup 11 - 13をgroupsの一番後ろに移動する。
                } else {
                    groups.copyInto(
                        destination = groups,
                        destinationOffset = groupPhysicalGapStart,
                        startIndex = groupPhysicalGapStart + groupPhysicalGapLen,
                        endIndex = groupPhysicalAddress + groupPhysicalGapLen
                    )
                }
            }

            // Gap has moved so the anchor for the groups that moved have changed so the parent
            // anchors that refer to these groups must be updated.
// Gapが動いたので、動いたgroupのanchorとparent anchorはアップデートされる。
// 確かに前後比較で見るとdataAnchorが移動していることが分かる。
            var groupAddress = if (index < gapStart) index + gapLen else gapStart
            val capacity = capacity
            runtimeCheck(groupAddress < capacity)
            while (groupAddress < capacity) {
                val oldAnchor = groups.parentAnchor(groupAddress)
                val oldIndex = parentAnchorToIndex(oldAnchor)
                val newAnchor = parentIndexToAnchor(oldIndex, index)
                if (newAnchor != oldAnchor) {
                    groups.updateParentAnchor(groupAddress, newAnchor)
                }
                groupAddress++
                if (groupAddress == index) groupAddress += gapLen
            }
        }
        this.groupGapStart = index
    }
```

そして戻ったremoveGroups()では。。

```kotlin
Recomposer.runRecomposeAndApplyChanges()
└── CompositionImpl.applyChanges()
    ├── SlotWriter.advancedBy(10)
    ├── SlotWriter.ensureStarted(0)
    │   └── SlotWriter.startGroup()
    │       └── SlotWriter.startGroup(key=0, objectKey=Empty, isNode=false, aux=Empty)
    │           ├── SlotWriter.saveCurrentGroupEnd()
    │           ├── SlotWriter.groupIndexToAddress(0)
    │           └── IntArray.slotIndex(0)
    │               ├── IntArray.slotAnchor(0)
    │               └── SlotWriter.dataAnchorToDataIndex(0)
    ├── ...
    └── SlotWriter.removeCurrentGroup()
        └── SlotWriter.removeGroup()
            ├── SlotWriter.skipGroup()
            └── *SlotWriter.removeGroups(10, 3)* ← Now reading
                └── moveGroupGapTo(10)

```

```kotlin
    /**
     * Remove [len] group from [start].
     */
    private fun removeGroups(start: Int, len: Int): Boolean {
        return if (len > 0) {
            var anchorsRemoved = false
            val anchors = anchors

            // Move the gap to start of the removal and grow the gap
            moveGroupGapTo(start)
            if (anchors.isNotEmpty()) anchorsRemoved = removeAnchors(start, len)
            groupGapStart = start
            val previousGapLen = groupGapLen
            val newGapLen = previousGapLen + len
            groupGapLen = newGapLen

            // Adjust the gap owner if necessary.
            val slotsGapOwner = slotsGapOwner
            if (slotsGapOwner > start) {
                this.slotsGapOwner = slotsGapOwner - len
            }
            if (currentGroupEnd >= groupGapStart) currentGroupEnd -= len
            anchorsRemoved
        } else false
    }
```

これが終わった後、以下のようになります。
groupsにデータは入っているけど、それが無視される形になります。

`<- group 10 -><- gap 19 -><- group 3 ->`

## 書き込みが終わった後にどうなるか？

Gapが今以下のようになっていますが、これを一旦gapを後ろに戻すことで次のrecomposeに備えます。

`<- group 10 -><- gap 19 -><- group 3 ->`

`<- group 13 -><- gap 19 -><- group 0 ->`


```kotlin:SlotTable
    /**
     * Write to the slot table in [block]. Only one writer can be created for a slot table at a
     * time and all readers must be closed an do readers can be created while the slot table is
     * being written to.
     *
     * @see SlotWriter
     */
    inline fun <T> write(block: (writer: SlotWriter) -> T): T = openWriter()
        .let { writer ->
            try {
                block(writer)
            } finally {
                writer.close()
            }
        }
```

```kotlin:SlotWriter
    /**
     * Close the writer
     */
    fun close() {
        closed = true
        // Ensure, for readers, there is no gap
        moveGroupGapTo(size)
        moveSlotGapTo(slots.size - slotsGapLen, groupGapStart)
        table.close(
            writer = this,
            groups = groups,
            groupsSize = groupGapStart,
            slots = slots,
            slotsSize = slotsGapStart,
            anchors = anchors
        )
    }
```


```kotlin:SlotTable

    /**
     * Close [writer] and adopt the slot arrays returned. The [SlotTable] is invalid until
     * [SlotWriter.close] is called as the [SlotWriter] is modifying [groups] and [slots]
     * directly and will only make copies of the arrays if the slot table grows.
     */
    internal fun close(
        writer: SlotWriter,
        groups: IntArray,
        groupsSize: Int,
        slots: Array<Any?>,
        slotsSize: Int,
        anchors: ArrayList<Anchor>
    ) {
        require(writer.table === this && this.writer) { "Unexpected writer close()" }
        this.writer = false
        setTo(groups, groupsSize, slots, slotsSize, anchors)
    }

    /**
     * Used internally by [SlotWriter.moveFrom] to swap arrays with a slot table target
     * [SlotTable] is empty.
     */
    internal fun setTo(
        groups: IntArray,
        groupsSize: Int,
        slots: Array<Any?>,
        slotsSize: Int,
        anchors: ArrayList<Anchor>
    ) {
        // Adopt the slots from the writer
        this.groups = groups
        this.groupsSize = groupsSize
        this.slots = slots
        this.slotsSize = slotsSize
        this.anchors = anchors
    }
```