https://qiita.com/takahirom/items/0e72bee081de8cf4f05f
の続きのstateが書き換わってからの動きを追っています。
@Composable
fun Content() {
var state by remember { mutableStateOf(true) }
LaunchedEffect(Unit) {
delay(12000)
state = false
}
if (state) {
Node1()
}
Node2()
}
自分用のコードリーディイングメモ記事で、もっとわかりやすいのを後で出すと思います。
以下の中で3:の部分を読んでいきます。3の途中までになります。
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の書き換えが走る。 (予想。コード未読)
Content() startRestartGroup()
まずContent()のデコンパイル後のコード
挫けそうになりますが、やっていきます
このfinal int $changed
には引数には渡された1が入っています。
composerには呼び元のComposerImplが入ってます。
@Composable
public static final void Content(@Nullable Composer $composer, final int $changed) {
$composer = $composer.startRestartGroup(-337788314);
ComposerKt.sourceInformation($composer, "C(Content)");
if ($changed == 0 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
} else {
int $changed$iv = false;
int $i$f$remember = false;
$composer.startReplaceableGroup(-3687241);
ComposerKt.sourceInformation($composer, "C(remember):Composables.kt#9igjgp");
boolean invalid$iv$iv = false;
int $i$f$cache = false;
Object var8 = $composer.rememberedValue();
boolean var9 = false;
boolean var10 = false;
int var12 = false;
boolean var13;
Object var10000;
if (var8 == Composer.Companion.getEmpty()) {
var13 = false;
MutableState var14 = SnapshotStateKt.mutableStateOf$default(true, (SnapshotMutationPolicy)null, 2, (Object)null);
$composer.updateRememberedValue(var14);
var10000 = var14;
} else {
var10000 = var8;
}
Object var15 = var10000;
$composer.endReplaceableGroup();
final MutableState state$delegate = (MutableState)var15;
Unit var19 = Unit.INSTANCE;
$changed$iv = true;
$i$f$remember = false;
$composer.startReplaceableGroup(-3686930);
ComposerKt.sourceInformation($composer, "C(remember)P(1):Composables.kt#9igjgp");
invalid$iv$iv = $composer.changed(state$delegate);
$i$f$cache = false;
var8 = $composer.rememberedValue();
var9 = false;
var10 = false;
var12 = false;
Object var10001;
if (!invalid$iv$iv && var8 != Composer.Companion.getEmpty()) {
var10001 = var8;
} else {
Unit var16 = var19;
var13 = false;
Function2 var17 = (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
Continuation var10001 = (Continuation)this;
this.label = 1;
if (DelayKt.delay(10000L, var10001) == var2) {
return var2;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
MainKt.Content$lambda-3(state$delegate, false);
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation $completion) {
return (Continuation)(new <anonymous constructor>($completion));
}
@Nullable
public final Object invoke(@NotNull CoroutineScope p1, @Nullable Continuation p2) {
return ((<undefinedtype>)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);
}
});
var19 = var16;
$composer.updateRememberedValue(var17);
var10001 = var17;
}
var15 = var10001;
$composer.endReplaceableGroup();
EffectsKt.LaunchedEffect(var19, (Function2)var15, $composer, 0);
$composer.startReplaceableGroup(-337788167);
if (Content$lambda-2(state$delegate)) {
Node1((String)null, $composer, 0, 1);
}
$composer.endReplaceableGroup();
Node2((String)null, $composer, 0, 1);
}
ScopeUpdateScope var18 = $composer.endRestartGroup();
if (var18 != null) {
var18.updateScope((Function2)(new Function2() {
public final void invoke(@Nullable Composer $composer, int $force) {
MainKt.Content($composer, $changed | 1);
}
}));
}
}
startRestartGroup()
start()とaddRecomposeScope()が呼ばれる。
/**
* Start a restart group. A restart group creates a recompose scope and sets it as the current
* recompose scope of the composition. If the recompose scope is invalidated then this group
* will be recomposed. A recompose scope can be invalidated by calling invalidate on the object
* returned by [androidx.compose.runtime.currentRecomposeScope].
* restart groupをスタートする。
* restart groupはrecompose scopeを作り、compositionの今のrecompose scopeとして作ったrecompose scopeをセットする。
* recompose scopeが無効(invalidated)になっていたら、groupはrecomposeされる。
* recompose scopeはcurrentRecomposeScope()の呼び出し結果のinvalidate()を呼ぶことで無効にできる
*/
@ComposeCompilerApi
override fun startRestartGroup(key: Int): Composer {
start(key, null, false, null)
addRecomposeScope()
return this
}
Content()
└── *startRestartGroup()* ← Now reading
├── start()
└── addRecomposeScope()
このstart()は一つ前の記事のstart()と同じ。処理の内容は以下
Content()
└── startRestartGroup()
├── *start()* ← reading
└── addRecomposeScope()
1: 基本的にはReaderの現在のcurrentGroupとキーを比較
キーが同じである事がわかる
$composer = $composer.startRestartGroup(-337788314);
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=-985533309, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@716a3a3, androidx.compose.runtime.internal.ComposableLambdaImpl@d9186a0]
↓ ここと比較!!!
Group(4) key=-337788314, nodes=2, size=12 aux=C(Content), slots=[5: androidx.compose.runtime.RecomposeScopeImpl@dbb4259]
Group(5) key=-3687241, nodes=0, size=1 aux=C(remember):Composables.kt#9igjgp, slots=[7: MutableState(value=true)@13143582]
Group(6) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[9: MutableState(value=true)@13143582, 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@8642d5d]
Group(9) key=-337788167, nodes=1, size=4
Group(10) key=1815931657, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@6986ccc]
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=1815931930, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@1d4ab15]
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]
2: キーが同じであればSlotReaderを一つ進める
以下でデバッガーで前後比較する
reader.run {
"SlotReader(current = $currentGroup, groupKey = $groupKey, groupEnd = $groupEnd )"
}
SlotReader(current = 4, groupKey = -337788314, groupEnd = 16 )
が
SlotReader(current = 5, groupKey = -3687241, groupEnd = 16 )
になり、一つ進む
そして enterGroup(isNode, newPending)
によってComposer側の状態も更新される。
addRecomposeScope()
Content()
└── startRestartGroup()
├── start()
└── *addRecomposeScope()* ← Now reading
private fun addRecomposeScope() {
// insertingはfalseになる
if (inserting) {
val scope = RecomposeScopeImpl(composition as CompositionImpl)
invalidateStack.push(scope)
updateValue(scope)
scope.start(snapshot.id)
} else {
val invalidation = invalidations.removeLocation(reader.parent)
val scope = reader.next() as RecomposeScopeImpl
scope.requiresRecompose = invalidation != null
invalidateStack.push(scope)
scope.start(snapshot.id)
}
}
invalidateStackのpush後は以下
invalidateStackはendRestartGroup()のときに取り出して使うみたい。
start()を呼ぶと以下のようになり、このスコープでcompositionが始まるときに呼ばれる関数ということが書いてあります。
Content()
└── startRestartGroup()
├── start()
└── addRecomposeScope()
└── *RecomposeScopeImpl.start()* ← Now reading
/**
* Called when composition start composing into this scope. The [token] is a value that is
* unique everytime this is called. This is currently the snapshot id but that shouldn't be
* relied on.
*/
fun start(token: Int) {
currentToken = token
skipped = false
}
Content()
まず、$changedは固定の1が渡されたので、 getSkipping()
は呼ばれない。
そしてまたstartReplaceableGroup()によってGroupの開始が呼ばれる。。
@Composable
public static final void Content(@Nullable Composer $composer, final int $changed) {
$composer = $composer.startRestartGroup(-337788314);
ComposerKt.sourceInformation($composer, "C(Content)");
if ($changed == 0 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
} else {
int $changed$iv = false;
int $i$f$remember = false;
$composer.startReplaceableGroup(-3687241);
ComposerKt.sourceInformation($composer, "C(remember):Composables.kt#9igjgp");
boolean invalid$iv$iv = false;
int $i$f$cache = false;
Object var8 = $composer.rememberedValue();
boolean var9 = false;
boolean var10 = false;
int var12 = false;
boolean var13;
Object var10000;
if (var8 == Composer.Companion.getEmpty()) {
var13 = false;
MutableState var14 = SnapshotStateKt.mutableStateOf$default(true, (SnapshotMutationPolicy)null, 2, (Object)null);
$composer.updateRememberedValue(var14);
var10000 = var14;
} else {
var10000 = var8;
}
SlotReaderの以下の比較が行われるんだろうなと思いはしつつ、startRestartGroup()との違いに注目すると、addRecomposeScopeがあるかないかの違いになります。なので基本的に今のSlotTableと比較して一個currentGroupを進めるだけですね。
@ComposeCompilerApi
override fun startRestartGroup(key: Int): Composer {
start(key, null, false, null)
addRecomposeScope()
return this
}
override fun startReplaceableGroup(key: Int) = start(key, null, false, null)
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=-985533309, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@716a3a3, androidx.compose.runtime.internal.ComposableLambdaImpl@d9186a0]
Group(4) key=-337788314, nodes=2, size=12 aux=C(Content), slots=[5: androidx.compose.runtime.RecomposeScopeImpl@dbb4259]
↓ ここと比較!!!
Group(5) key=-3687241, nodes=0, size=1 aux=C(remember):Composables.kt#9igjgp, slots=[7: MutableState(value=true)@13143582]
Group(6) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[9: MutableState(value=true)@13143582, 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@8642d5d]
Group(9) key=-337788167, nodes=1, size=4
Group(10) key=1815931657, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@6986ccc]
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=1815931930, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@1d4ab15]
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]
rememberedValue()、 nextSlot()
@Composable
public static final void Content(@Nullable Composer $composer, final int $changed) {
$composer = $composer.startRestartGroup(-337788314);
ComposerKt.sourceInformation($composer, "C(Content)");
if ($changed == 0 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
} else {
int $changed$iv = false;
int $i$f$remember = false;
$composer.startReplaceableGroup(-3687241);
ComposerKt.sourceInformation($composer, "C(remember):Composables.kt#9igjgp");
boolean invalid$iv$iv = false;
int $i$f$cache = false;
Object var8 = $composer.rememberedValue(); // **←**
boolean var9 = false;
boolean var10 = false;
int var12 = false;
boolean var13;
Object var10000;
if (var8 == Composer.Companion.getEmpty()) {
var13 = false;
MutableState var14 = SnapshotStateKt.mutableStateOf$default(true, (SnapshotMutationPolicy)null, 2, (Object)null);
$composer.updateRememberedValue(var14);
var10000 = var14;
} else {
var10000 = var8;
}
override fun rememberedValue(): Any? = nextSlot()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
└── composer.rememberedValue()
└── *composer.nextSlot()* ← Now reading
insertingとreusingはfalseで、itにreader.next()によって受け取ったMutableState()を受け取る。
@PublishedApi
@OptIn(InternalComposeApi::class)
internal fun nextSlot(): Any? = if (inserting) {
validateNodeNotExpected()
Composer.Empty
} else reader.next().let { if (reusing) Composer.Empty else it }
MutableState()がrememberedValue()によって返るので、 if (var8 == Composer.Companion.getEmpty())
はfalseになり、
$composer.endReplaceableGroup();
が呼ばれる。
@Composable
public static final void Content(@Nullable Composer $composer, final int $changed) {
$composer = $composer.startRestartGroup(-337788314);
ComposerKt.sourceInformation($composer, "C(Content)");
if ($changed == 0 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
} else {
int $changed$iv = false;
int $i$f$remember = false;
$composer.startReplaceableGroup(-3687241);
ComposerKt.sourceInformation($composer, "C(remember):Composables.kt#9igjgp");
boolean invalid$iv$iv = false;
int $i$f$cache = false;
Object var8 = $composer.rememberedValue();
boolean var9 = false;
boolean var10 = false;
int var12 = false;
boolean var13;
Object var10000;
if (var8 == Composer.Companion.getEmpty()) {
var13 = false;
MutableState var14 = SnapshotStateKt.mutableStateOf$default(true, (SnapshotMutationPolicy)null, 2, (Object)null);
$composer.updateRememberedValue(var14);
var10000 = var14;
} else {
var10000 = var8;
}
Object var15 = var10000;
$composer.endReplaceableGroup();
final MutableState state$delegate = (MutableState)var15;
Unit var19 = Unit.INSTANCE;
$changed$iv = true;
$i$f$remember = false;
$composer.startReplaceableGroup(-3686930);
ComposerKt.sourceInformation($composer, "C(remember)P(1):Composables.kt#9igjgp");
invalid$iv$iv = $composer.changed(state$delegate);
$i$f$cache = false;
var8 = $composer.rememberedValue();
var9 = false;
var10 = false;
var12 = false;
Object var10001;
// 以下は
if (!invalid$iv$iv && var8 != Composer.Companion.getEmpty()) {
var10001 = var8;
} else {
Unit var16 = var19;
var13 = false;
Function2 var17 = (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
Continuation var10001 = (Continuation)this;
this.label = 1;
if (DelayKt.delay(10000L, var10001) == var2) {
return var2;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
MainKt.Content$lambda-3(state$delegate, false);
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation $completion) {
return (Continuation)(new <anonymous constructor>($completion));
}
@Nullable
public final Object invoke(@NotNull CoroutineScope p1, @Nullable Continuation p2) {
return ((<undefinedtype>)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);
}
});
var19 = var16;
$composer.updateRememberedValue(var17);
var10001 = var17;
}
endReplaceableGroup() 始めからendGroup()まで
長い。。やっていきます。
@ComposeCompilerApi
override fun endReplaceableGroup() = endGroup()
private fun endGroup() = end(isNode = false)
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── *composer.end(isNode = false)* ← Now reading
まず、updateCompoundKeyWhenWeExitGroup()関連はSavedState関連なので今回は飛ばします。
private fun end(isNode: Boolean) {
// All the changes to the group (or node) have been recorded. All new nodes have been
// inserted but it has yet to determine which need to be removed or moved. Note that the
// changes are relative to the first change in the list of nodes that are changing.
if (inserting) {
val parent = writer.parent
updateCompoundKeyWhenWeExitGroup(
writer.groupKey(parent),
writer.groupObjectKey(parent),
writer.groupAux(parent)
)
} else {
val parent = reader.parent
updateCompoundKeyWhenWeExitGroup(
reader.groupKey(parent),
reader.groupObjectKey(parent),
reader.groupAux(parent)
)
}
var expectedNodeCount = groupNodeCount
val pending = pending
// pendingがnullなので、以下はfalseになるのでif文に入らない。
if (pending != null && pending.keyInfos.size > 0) {
...
}
// Detect removing nodes at the end. No pending is created in this case we just have more
// nodes in the previous composition than we expect (i.e. we are not yet at an end)
val removeIndex = nodeIndex
//reader.isGroupEnd == trueつまりcurrentGroup == currentEndなので、繰り返さない。
while (!reader.isGroupEnd) {
val startSlot = reader.currentGroup
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove)
invalidations.removeRange(startSlot, reader.currentGroup)
}
val inserting = inserting
// inserting = falseになる
if (inserting) {
...
} else {
//isNode = fasle
if (isNode) recordUp()
// 以下では判定によりなにもしない。
recordEndGroup()
val parentGroup = reader.parent
val parentNodeCount = updatedNodeCount(parentGroup)
// 以下も変化が今回はないので、色々飛ばされます。
if (expectedNodeCount != parentNodeCount) {
updateNodeCountOverrides(parentGroup, expectedNodeCount)
}
if (isNode) {
expectedNodeCount = 1
}
reader.endGroup()
// 動かなかったからか、なにもしない。
realizeMovement()
}
exitGroup(expectedNodeCount, inserting)
}
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── *composer.recordEndGroup()* ← Now reading
recordEndGroup()は判定がfalseになるので何もせずに終わる。
private fun recordEndGroup() {
val location = reader.parent
val currentStartedGroup = startedGroups.peekOr(-1)
runtimeCheck(currentStartedGroup <= location) { "Missed recording an endGroup" }
if (startedGroups.peekOr(-1) == location) {
startedGroups.pop()
recordSlotTableOperation(change = endGroupInstance)
}
}
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordEndGroup()
└── *SlotReader.endGroup()* ← Now reading
/**
* End the current group. Must be called after the corresponding [startGroup].
*/
fun endGroup() {
// emptyCount=0なのでtrueになる
if (emptyCount == 0) {
require(currentGroup == currentEnd) { "endGroup() not called at the end of a group" }
val parent = groups.parentAnchor(parent)
this.parent = parent
// parent < 0はfalseになる parentは4になっている。
// groupSizeは12になる。
// 12がcurrentEndになる。
currentEnd = if (parent < 0)
groupsSize
else
parent + groups.groupSize(parent)
}
}
以下にデバッグ用のコード変えています。
reader.run {
"SlotReader(current = $currentGroup, groupKey = $groupKey, currentEnd = $currentEnd )"
}
SlotReader(current = 6, groupKey = 0, currentEnd = 6 )が
SlotReader(current = 6, groupKey = -3686930, currentEnd = 16 )
に変化する。
exitGroup()
expectedNodeCount=0
inserting = falseで呼ばれる。
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordEndGroup()
├── SlotReader.endGroup()
└── *composer.exitGroup(expectedNodeCount = 0, inserting = false)* ← Now reading
基本的にComposerの状態を合わせるのみ。
private fun exitGroup(expectedNodeCount: Int, inserting: Boolean) {
// Restore the parent's state updating them if they have changed based on changes in the
// children. For example, if a group generates nodes then the number of generated nodes will
// increment the node index and the group's node count. If the parent is tracking structural
// changes in pending then restore that too.
val previousPending = pendingStack.pop()
if (previousPending != null && !inserting) {
previousPending.groupIndex++
}
this.pending = previousPending
this.nodeIndex = nodeIndexStack.pop() + expectedNodeCount
this.groupNodeCount = this.groupNodeCountStack.pop() + expectedNodeCount
}
composer.changed(state$delegate)
@Composable
public static final void Content(@Nullable Composer $composer, final int $changed) {
$composer = $composer.startRestartGroup(-337788314);
ComposerKt.sourceInformation($composer, "C(Content)");
if ($changed == 0 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
} else {
int $changed$iv = false;
int $i$f$remember = false;
$composer.startReplaceableGroup(-3687241);
ComposerKt.sourceInformation($composer, "C(remember):Composables.kt#9igjgp");
boolean invalid$iv$iv = false;
int $i$f$cache = false;
Object var8 = $composer.rememberedValue();
boolean var9 = false;
boolean var10 = false;
int var12 = false;
boolean var13;
Object var10000;
if (var8 == Composer.Companion.getEmpty()) {
var13 = false;
MutableState var14 = SnapshotStateKt.mutableStateOf$default(true, (SnapshotMutationPolicy)null, 2, (Object)null);
$composer.updateRememberedValue(var14);
var10000 = var14;
} else {
var10000 = var8;
}
Object var15 = var10000;
$composer.endReplaceableGroup();
final MutableState state$delegate = (MutableState)var15;
Unit var19 = Unit.INSTANCE;
$changed$iv = true;
$i$f$remember = false;
$composer.startReplaceableGroup(-3686930);
ComposerKt.sourceInformation($composer, "C(remember)P(1):Composables.kt#9igjgp");
invalid$iv$iv = $composer.changed(state$delegate); // ← ここ
$i$f$cache = false;
var8 = $composer.rememberedValue();
var9 = false;
var10 = false;
var12 = false;
Object var10001;
if (!invalid$iv$iv && var8 != Composer.Companion.getEmpty()) {
var10001 = var8;
} else {
Unit var16 = var19;
var13 = false;
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
└── *composer.changed(MutableState(value = false))* ← Now reading
└── nextSlot()
/**
* Determine if the current slot table value is equal to the given value, if true, the value
* is scheduled to be skipped during [ControlledComposition.applyChanges] and [changes] return
* false; otherwise [ControlledComposition.applyChanges] will update the slot table to [value].
* In either case the composer's slot table is advanced.
*
* @param value the value to be compared.
*/
@ComposeCompilerApi
override fun changed(value: Any?): Boolean {
return if (nextSlot() != value) {
updateValue(value)
true
} else {
false
}
}
ここではfalseになる。
ちなみにnextSlot()でまたMutableStateを取り出して比較する。
つまり、if (!invalid$iv$iv && var8 != Composer.Companion.getEmpty())
の !invalid$iv$iv
はtrueになる。
2回目のrememberedValue()ではSlotTableから値を取り出すのだが、LaunchedEffectで使われていたラムダが取り出される。
var8 = $composer.rememberedValue();
つまり var8 != Composer.Companion.getEmpty()
も trueになるので、else部分は今回は実行されない。
@Composable
public static final void Content(@Nullable Composer $composer, final int $changed) {
$composer = $composer.startRestartGroup(-337788314);
ComposerKt.sourceInformation($composer, "C(Content)");
if ($changed == 0 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
} else {
int $changed$iv = false;
int $i$f$remember = false;
$composer.startReplaceableGroup(-3687241);
ComposerKt.sourceInformation($composer, "C(remember):Composables.kt#9igjgp");
boolean invalid$iv$iv = false;
int $i$f$cache = false;
Object var8 = $composer.rememberedValue();
boolean var9 = false;
boolean var10 = false;
int var12 = false;
boolean var13;
Object var10000;
if (var8 == Composer.Companion.getEmpty()) {
var13 = false;
MutableState var14 = SnapshotStateKt.mutableStateOf$default(true, (SnapshotMutationPolicy)null, 2, (Object)null);
$composer.updateRememberedValue(var14);
var10000 = var14;
} else {
var10000 = var8;
}
Object var15 = var10000;
$composer.endReplaceableGroup();
final MutableState state$delegate = (MutableState)var15;
Unit var19 = Unit.INSTANCE;
$changed$iv = true;
$i$f$remember = false;
$composer.startReplaceableGroup(-3686930);
ComposerKt.sourceInformation($composer, "C(remember)P(1):Composables.kt#9igjgp");
invalid$iv$iv = $composer.changed(state$delegate);
$i$f$cache = false;
var8 = $composer.rememberedValue();
var9 = false;
var10 = false;
var12 = false;
Object var10001;
if (!invalid$iv$iv && var8 != Composer.Companion.getEmpty()) {
var10001 = var8;
} else {
... //ここは実行されない
}
var15 = var10001;
$composer.endReplaceableGroup();
// 次はここだが、一旦これはスキップする。
EffectsKt.LaunchedEffect(var19, (Function2)var15, $composer, 0);
// 始まる。
$composer.startReplaceableGroup(-337788167);
// ここはfalseになる。
if (state$delegate.getValue()) {
Node1((String)null, $composer, 0, 1);
}
// そして以下で変化が現れるはず!!!
$composer.endReplaceableGroup();
Node2((String)null, $composer, 0, 1);
}
ScopeUpdateScope var18 = $composer.endRestartGroup();
if (var18 != null) {
var18.updateScope((Function2)(new Function2() {
public final void invoke(@Nullable Composer $composer, int $force) {
MainKt.Content($composer, $changed | 1);
}
}));
}
}
そしてついにnodeの削除の変更が見れます
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=-985533309, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@716a3a3, androidx.compose.runtime.internal.ComposableLambdaImpl@d9186a0]
Group(4) key=-337788314, nodes=2, size=12 aux=C(Content), slots=[5: androidx.compose.runtime.RecomposeScopeImpl@dbb4259]
Group(5) key=-3687241, nodes=0, size=1 aux=C(remember):Composables.kt#9igjgp, slots=[7: MutableState(value=true)@13143582]
Group(6) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[9: MutableState(value=true)@13143582, Function2<kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Unit>, java.lang.Object>]
↓ おそらくここはLaunchedEffectが進めてくれている。
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@8642d5d]
↓ ここと比較!!!
Group(9) key=-337788167, nodes=1, size=4
Group(10) key=1815931657, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@6986ccc]
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=1815931930, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@1d4ab15]
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]
Nodeが消えたときのendReplaceableGroup()
EffectsKt.LaunchedEffect(var19, (Function2)var15, $composer, 0);
$composer.startReplaceableGroup(-337788167);
if (Content$lambda-2(state$delegate)) {
Node1((String)null, $composer, 0, 1);
}
$composer.endReplaceableGroup();
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── *composer.end(isNode = false)* ← Now reading
private fun end(isNode: Boolean) {
// All the changes to the group (or node) have been recorded. All new nodes have been
// inserted but it has yet to determine which need to be removed or moved. Note that the
// changes are relative to the first change in the list of nodes that are changing.
// savedState関連なので、このif文は飛ばす。
if (inserting) {
val parent = writer.parent
updateCompoundKeyWhenWeExitGroup(
writer.groupKey(parent),
writer.groupObjectKey(parent),
writer.groupAux(parent)
)
} else {
val parent = reader.parent
updateCompoundKeyWhenWeExitGroup(
reader.groupKey(parent),
reader.groupObjectKey(parent),
reader.groupAux(parent)
)
}
var expectedNodeCount = groupNodeCount
val pending = pending
if (pending != null && pending.keyInfos.size > 0) {
// ここはfalseなので通らない
...
}
// Detect removing nodes at the end. No pending is created in this case we just have more
// nodes in the previous composition than we expect (i.e. we are not yet at an end)
// 削除の検知。
// この場合ペンディングに入れない。予想していたよりもノードが多かっただけ。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
// ここのwhileに入る!
val startSlot = reader.currentGroup
// startSlotは10になっている
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove)
invalidations.removeRange(startSlot, reader.currentGroup)
}
val inserting = inserting
if (inserting) {
if (isNode) {
registerInsertUpFixup()
expectedNodeCount = 1
}
reader.endEmpty()
val parentGroup = writer.parent
writer.endGroup()
if (!reader.inEmpty) {
val virtualIndex = insertedGroupVirtualIndex(parentGroup)
writer.endInsert()
writer.close()
recordInsert(insertAnchor)
this.inserting = false
if (!slotTable.isEmpty) {
updateNodeCount(virtualIndex, 0)
updateNodeCountOverrides(virtualIndex, expectedNodeCount)
}
}
} else {
if (isNode) recordUp()
recordEndGroup()
val parentGroup = reader.parent
val parentNodeCount = updatedNodeCount(parentGroup)
if (expectedNodeCount != parentNodeCount) {
updateNodeCountOverrides(parentGroup, expectedNodeCount)
}
if (isNode) {
expectedNodeCount = 1
}
reader.endGroup()
realizeMovement()
}
exitGroup(expectedNodeCount, inserting)
}
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=-985533309, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@a94b610, androidx.compose.runtime.internal.ComposableLambdaImpl@12a9509]
Group(4) key=-337788314, nodes=2, size=12 aux=C(Content), slots=[5: androidx.compose.runtime.RecomposeScopeImpl@680630e]
Group(5) key=-3687241, nodes=0, size=1 aux=C(remember):Composables.kt#9igjgp, slots=[7: MutableState(value=false)@229417263]
Group(6) key=-3686930, nodes=0, size=1 aux=C(remember)P(1):Composables.kt#9igjgp, slots=[9: MutableState(value=false)@229417263, 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@f73cc2]
Group(9) key=-337788167, nodes=1, size=4
// ↓ 今ここになっている
Group(10) key=1815931657, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@65e78c5]
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=1815931930, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@c63a21a]
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]
recordDelete()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── composer.recordDelete()
└── *composer.recordSlotEditingOperation(removeCurrentGroupInstance)* ← Now reading
├── composer.realizeOperationLocation()
├── composer.recordSlotEditing()
└── composer.record(removeCurrentGroupInstance)
removeCurrentGroupInstanceについては後で触れます。次はrecordSlotEditingOperation()。
/**
* When a group is removed the reader will move but the writer will not so to ensure both the
* writer and reader are tracking the same slot we advance the [writersReaderDelta] to
* account for the removal.
* グループが削除された時readerはmoveするが、writerはそうではないので、同じスロットを追跡するように
* writersReaderDeltaを進めて、削除分を考慮。
*/
private fun recordDelete() {
recordSlotEditingOperation(change = removeCurrentGroupInstance)
writersReaderDelta += reader.groupSize
}
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── composer.recordDelete()
└── *composer.recordSlotEditingOperation(removeCurrentGroupInstance)* ← Now reading
├── composer.recordDelete()
├── composer.recordSlotEditing()
└── composer.record(removeCurrentGroupInstance)
/**
* Record a change that will insert, remove or move a slot table group. This ensures the slot
* table is prepared for the change by ensuring the parent group is started and then ended
* as the group is left.
* slotTableへのinsert、move、removeなどのchangeをrecordする。
* これにより、SlotTableは、親グループが開始され、グループが離脱すると終了するようにすることで、変更に備えます。(ちょっと意味が分かっていません)
*/
private fun recordSlotEditingOperation(change: Change) {
realizeOperationLocation()
recordSlotEditing()
record(change)
}
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── composer.recordDelete()
└── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
├── *composer.realizeOperationLocation()* ← Now reading
├── composer.recordSlotEditing()
└── composer.record(removeCurrentGroupInstance)
private fun realizeOperationLocation(forParent: Boolean = false) {
val location = if (forParent) reader.parent else reader.currentGroup
val distance = location - writersReaderDelta
require(distance >= 0) { "Tried to seek backward" }
// distanceは10になる
// この時点ではwritersReaderDeltaは0になっている
if (distance > 0) {
// つまりrerodという関数で、slotsという引数でSlotWriterが渡ってくるので、
// ラムダの中ではSlotWriterに対して10進めてといっていることになる。
// この操作は一旦保存しておいて次のフェーズで利用される。
record { _, slots, _ -> slots.advanceBy(distance) }
// そして、10をwritersReaderDeltaに入れる。
writersReaderDelta = location
}
}
record()関数
ここでchange listにchangeを入れる
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── composer.recordDelete()
└── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
├── composer.realizeOperationLocation()
│ └── *composer.record { _, slots, _ -> slots.advanceBy(distance) }* ← Now reading
├── composer.recordSlotEditing()
└── composer.record(removeCurrentGroupInstance)
/**
* Add a raw change to the change list. Once [record] is called, the operation is realized
* into the change list. The helper routines below reduce the number of operations that must
* be realized to change the previous tree to the new tree as well as update the slot table
* to prepare for the next composition.
* 変更をchanged listに追加する。recordが一度呼ばれたら、その操作はchange listに入る。
* 前のツリーから新しいツリーに変更するために実現しなくてはならない操作の数を減らし、
* SlotTableを更新して次のcompositionに備える。
*/
private fun record(change: Change) {
changes.add(change)
}
recordSlotEditing()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── composer.recordDelete()
└── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
├── composer.realizeOperationLocation()
│ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
├── *composer.recordSlotEditing()* ← Now reading
└── composer.record(removeCurrentGroupInstance)
ここでは、recordSlotTableOperationを使って、後で、SlotWriter側で操作できるような操作をrecordする。
どういう操作かというと、Groupが始まったので、いろんなWriterのcurrentGroupなどを更新をかけるもの。
private val startRootGroup: Change = { _, slots, _ -> slots.ensureStarted(0) }
...
private fun recordSlotEditing() {
// During initial composition (when the slot table is empty), no group needs
// to be started.
if (!slotTable.isEmpty) {
// ここは最初だけ空らしいので、trueで通る。
val reader = reader
val location = reader.parent
// locationは9
if (startedGroups.peekOr(-1) != location) {
// startedGroupsは0が9個入っているStackで、0を受け取るので、ここに入る。
if (!startedGroup) {
// startedGroupはfalseなので、ここに入る
// We need to ensure the root group is started.
// ルートグループが始まっていることを確認する必要がある。
recordSlotTableOperation(change = startRootGroup)
// startedGroupがtrueに。
startedGroup = true
}
//
val anchor = reader.anchor(location)
startedGroups.push(location)
recordSlotTableOperation { _, slots, _ -> slots.ensureStarted(anchor) }
}
}
}
/**
* Record a change ensuring, when it is applied, the write matches the current slot in the
* reader.
*/
private fun recordSlotTableOperation(forParent: Boolean = false, change: Change) {
// ここではすでに移動しているので、移動しない。
realizeOperationLocation(forParent)
record(change)
}
SlotReader.anchor(location)
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── composer.recordDelete()
└── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
├── composer.realizeOperationLocation()
│ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
├── composer.recordSlotEditing()
│ ├── composer.recordSlotTableOperation()
│ └── *slotReader.anchor(location = 9)* ← Now reading
└── composer.record(removeCurrentGroupInstance)
fun anchor(index: Int = currentGroup) = table.anchors.getOrAdd(index, groupsSize) {
// まだ作られていなかったようで、作って返す。このときにtable.anchorsにも追加される。
Anchor(index)
}
tableからindexに相当するanchorを取り出す。anchorは追跡するためのものらしい。
/**
* An [Anchor] tracks a groups as its index changes due to other groups being inserted and
* removed before it. If the group the [Anchor] is tracking is removed, directly or indirectly,
* [valid] will return false. The current index of the group can be determined by passing either
* the [SlotTable] or [SlotWriter] to [toIndexFor]. If a [SlotWriter] is active, it must be used
* instead of the [SlotTable] as the anchor index could have shifted due to operations performed
* on the writer.
* Anchorは他のgroupが前に挿入されたり削除されたりすることでindexが変化するグループを追跡する。
* もしAnchorで追跡していたgroupが消された場合、直接が非直接的にvalidがfalseになる。
* そのグループの今のindexはSlotTableかSlotWriterのtoIndexForにわたすことで決定する。
* もしSlotWriterがActiveであれば、操作が行われることによってindexがシャッフルされる可能性があるので、SlotWriterがSlotTableの代わりに使われる必要がある、
*/
internal class Anchor(loc: Int) {
internal var location: Int = loc
val valid get() = location != Int.MIN_VALUE
fun toIndexFor(slots: SlotTable) = slots.anchorIndex(this)
fun toIndexFor(writer: SlotWriter) = writer.anchorIndex(this)
}
recordSlotEditing() 途中から
private val startRootGroup: Change = { _, slots, _ -> slots.ensureStarted(0) }
...
private fun recordSlotEditing() {
// During initial composition (when the slot table is empty), no group needs
// to be started.
if (!slotTable.isEmpty) {
// ここは最初だけ空らしいので、trueで通る。
val reader = reader
val location = reader.parent
// locationは9
if (startedGroups.peekOr(-1) != location) {
// startedGroupsは0が9個入っているStackで、0を受け取るので、ここに入る。
if (!startedGroup) {
// startedGroupはfalseなので、ここに入る
// We need to ensure the root group is started.
// ルートグループが始まっていることを確認する必要がある。
recordSlotTableOperation(change = startRootGroup)
// startedGroupがtrueに。
startedGroup = true
}
// このlocation = 9を追跡するためにanchorを作る
val anchor = reader.anchor(location)
// startedGroupsに9を追加する
startedGroups.push(location)
// anchorに対してensureStartedして、change listでWriterを使うときにlocationを動かせるように
recordSlotTableOperation { _, slots, _ -> slots.ensureStarted(anchor) }
}
}
}
あとは呼び出し元に戻っていきます。
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── composer.recordDelete()
└── *composer.recordSlotEditingOperation(removeCurrentGroupInstance)* ← Now reading
├── composer.realizeOperationLocation()
│ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
├── composer.recordSlotEditing()
│ ├── composer.recordSlotTableOperation()
│ └── slotReader.anchor(location = 9)
└── composer.record(removeCurrentGroupInstance)
private fun recordSlotEditingOperation(change: Change) {
realizeOperationLocation()
recordSlotEditing()
// 以下でrecordします。このchnageってなんでしたっけ?っていうことで戻ってきましょう
record(change)
}
recordDelete() 途中から
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
└── *composer.recordDelete()* ← Now reading
└── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
├── composer.realizeOperationLocation()
│ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
├── composer.recordSlotEditing()
│ ├── composer.recordSlotTableOperation()
│ └── slotReader.anchor(location = 9)
└── composer.record(removeCurrentGroupInstance)
このchangeで渡していたのはslotWriterに対してのremoveCurrentGroupを呼ぶものですね。これで今のグループがあとで削除されそうです。
private val removeCurrentGroupInstance: Change = { _, slots, rememberManager ->
slots.removeCurrentGroup(rememberManager)
}
private fun recordDelete() {
recordSlotEditingOperation(change = removeCurrentGroupInstance)
writersReaderDelta += reader.groupSize
そして、reader.groupSizeをwritersReaderDeltaに入れます。reader.groupSizeは3になるのでwritersReaderDeltaは10+3で13になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com
/0/27388/b82263d6-7de3-8376-83ad-3fc4c1709506.png)
end()に戻る
recordDelete()
が終わったので、次はval nodesToRemove = reader.skipGroup()
になります。
private fun end(isNode: Boolean) {
// All the changes to the group (or node) have been recorded. All new nodes have been
// inserted but it has yet to determine which need to be removed or moved. Note that the
// changes are relative to the first change in the list of nodes that are changing.
// savedState関連なので、このif文は飛ばす。
if (inserting) {
val parent = writer.parent
updateCompoundKeyWhenWeExitGroup(
writer.groupKey(parent),
writer.groupObjectKey(parent),
writer.groupAux(parent)
)
} else {
val parent = reader.parent
updateCompoundKeyWhenWeExitGroup(
reader.groupKey(parent),
reader.groupObjectKey(parent),
reader.groupAux(parent)
)
}
var expectedNodeCount = groupNodeCount
val pending = pending
if (pending != null && pending.keyInfos.size > 0) {
// ここはfalseなので通らない
...
}
// Detect removing nodes at the end. No pending is created in this case we just have more
// nodes in the previous composition than we expect (i.e. we are not yet at an end)
// 削除の検知。
// この場合ペンディングに入れない。予想していたよりもノードが多かっただけ。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
// ここのwhileに入る!
val startSlot = reader.currentGroup
// startSlotは10になっている
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove)
invalidations.removeRange(startSlot, reader.currentGroup)
}
val inserting = inserting
if (inserting) {
if (isNode) {
registerInsertUpFixup()
expectedNodeCount = 1
}
reader.endEmpty()
val parentGroup = writer.parent
writer.endGroup()
if (!reader.inEmpty) {
val virtualIndex = insertedGroupVirtualIndex(parentGroup)
writer.endInsert()
writer.close()
recordInsert(insertAnchor)
this.inserting = false
if (!slotTable.isEmpty) {
updateNodeCount(virtualIndex, 0)
updateNodeCountOverrides(virtualIndex, expectedNodeCount)
}
}
} else {
if (isNode) recordUp()
recordEndGroup()
val parentGroup = reader.parent
val parentNodeCount = updatedNodeCount(parentGroup)
if (expectedNodeCount != parentNodeCount) {
updateNodeCountOverrides(parentGroup, expectedNodeCount)
}
if (isNode) {
expectedNodeCount = 1
}
reader.endGroup()
realizeMovement()
}
exitGroup(expectedNodeCount, inserting)
}
上記の以下の部分を見ていっています。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
val startSlot = reader.currentGroup
recordDelete()
val nodesToRemove = reader.skipGroup() // ←
recordRemoveNode(removeIndex, nodesToRemove)
invalidations.removeRange(startSlot, reader.currentGroup)
skipGroup()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
└── *slotReader.skipGroup()* ← Now reading
/**
* Skip a group. Must be called at the start of a group.
*/
fun skipGroup(): Int {
require(emptyCount == 0) { "Cannot skip while in an empty region" }
val count = if (groups.isNode(currentGroup)) 1 else groups.nodeCount(currentGroup)
currentGroup += groups.groupSize(currentGroup)
return count
}
currentGroupが10だったのが、13になります。
countは1になります。
つまり recordDelete()
はchange listに追加する処理で、skipGroup()ではSlotReaderを進める処理という感じかな?
recordRemoveNode()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
└── *composer.recordRemoveNode(count = 1, nodeIndex = 0)* ← Now reading
次はrecordRemoveNodeです。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
val startSlot = reader.currentGroup
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove) // ←
invalidations.removeRange(startSlot, reader.currentGroup)
その前のchange listのおさらいです。
change listは次のフェーズで、slot writerに書き込んでいくためのリストなんですが、
recordという名前がつくメソッドでそのchange listに追加していっているようです。
そのため、確認しておきます。
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)
}
currentGroupを削除。
つまり今のanchorの場所が削除される。ということになりそうですね
さて、もう次のフェーズでGroupが削除されることは分かっているのですがこれ以上に何をするのかを見ていきましょう。
nodeIndex、countには1、0が入っています。
また引数として渡されているのはremoveIndex, nodesToRemoveなので、nodeIndex1から1個削除したいということですね。
private fun recordRemoveNode(nodeIndex: Int, count: Int) {
if (count > 0) {
runtimeCheck(nodeIndex >= 0) { "Invalid remove index $nodeIndex" }
if (previousRemove == nodeIndex) previousCount += count
else {
// 動かなかったからか、if文で何もしなそう。
realizeMovement()
previousRemove = nodeIndex
previousCount = count
}
}
}
ここでは基本的に何もしないですが、フィールドの変更が入ります。
previousRemoveが-1だったのが、0になり
previousCountが1に変わります。
削除された場所と数を保管します。
endに戻る
private fun end(isNode: Boolean) {
// All the changes to the group (or node) have been recorded. All new nodes have been
// inserted but it has yet to determine which need to be removed or moved. Note that the
// changes are relative to the first change in the list of nodes that are changing.
// savedState関連なので、このif文は飛ばす。
if (inserting) {
val parent = writer.parent
updateCompoundKeyWhenWeExitGroup(
writer.groupKey(parent),
writer.groupObjectKey(parent),
writer.groupAux(parent)
)
} else {
val parent = reader.parent
updateCompoundKeyWhenWeExitGroup(
reader.groupKey(parent),
reader.groupObjectKey(parent),
reader.groupAux(parent)
)
}
var expectedNodeCount = groupNodeCount
val pending = pending
if (pending != null && pending.keyInfos.size > 0) {
// ここはfalseなので通らない
...
}
// Detect removing nodes at the end. No pending is created in this case we just have more
// nodes in the previous composition than we expect (i.e. we are not yet at an end)
// 削除の検知。
// この場合ペンディングに入れない。予想していたよりもノードが多かっただけ。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
// ここのwhileに入る!
val startSlot = reader.currentGroup
// startSlotは10になっている
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove)
// 前に削除されているので、削除するものはない。一応startSlotは10、reader.currentGroupは13になる。
invalidations.removeRange(startSlot, reader.currentGroup)
}
val inserting = inserting
if (inserting) {
// falseになるのでここは通らない。
...
} else {
if (isNode) recordUp() // falseなので何もしない
recordEndGroup() // 次はここ
val parentGroup = reader.parent
val parentNodeCount = updatedNodeCount(parentGroup)
if (expectedNodeCount != parentNodeCount) {
updateNodeCountOverrides(parentGroup, expectedNodeCount)
}
if (isNode) {
expectedNodeCount = 1
}
reader.endGroup()
realizeMovement()
}
exitGroup(expectedNodeCount, inserting)
}
recordEndGroup()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
├── composer.recordRemoveNode(count = 1, nodeIndex = 0)
└── *composer.recordEndGroup()* ← Now reading
private val endGroupInstance: Change = { _, slots, _ -> slots.endGroup() }
private fun recordEndGroup() {
// locationは9になります
// またcurrentStartedGroupも9になります
// startedGroupsはrecordDeleteから呼ばれるrecordSlotEditing()で追加されていましたね
val location = reader.parent
val currentStartedGroup = startedGroups.peekOr(-1)
runtimeCheck(currentStartedGroup <= location) { "Missed recording an endGroup" }
if (startedGroups.peekOr(-1) == location) {
// trueになる
startedGroups.pop()
// 9をpop
// change listに `slots.endGroup()` の操作を保存。
recordSlotTableOperation(change = endGroupInstance)
}
}
このメソッドを呼び終わったあとのchange listの様子はこちら
end()に戻る
private fun end(isNode: Boolean) {
// All the changes to the group (or node) have been recorded. All new nodes have been
// inserted but it has yet to determine which need to be removed or moved. Note that the
// changes are relative to the first change in the list of nodes that are changing.
// savedState関連なので、このif文は飛ばす。
if (inserting) {
val parent = writer.parent
updateCompoundKeyWhenWeExitGroup(
writer.groupKey(parent),
writer.groupObjectKey(parent),
writer.groupAux(parent)
)
} else {
val parent = reader.parent
updateCompoundKeyWhenWeExitGroup(
reader.groupKey(parent),
reader.groupObjectKey(parent),
reader.groupAux(parent)
)
}
var expectedNodeCount = groupNodeCount
val pending = pending
if (pending != null && pending.keyInfos.size > 0) {
// ここはfalseなので通らない
...
}
// Detect removing nodes at the end. No pending is created in this case we just have more
// nodes in the previous composition than we expect (i.e. we are not yet at an end)
// 削除の検知。
// この場合ペンディングに入れない。予想していたよりもノードが多かっただけ。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
// ここのwhileに入る!
val startSlot = reader.currentGroup
// startSlotは10になっている
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove)
// 前に削除されているので、削除するものはない。一応startSlotは10、reader.currentGroupは13になる。
invalidations.removeRange(startSlot, reader.currentGroup)
}
val inserting = inserting
if (inserting) {
// falseになるのでここは通らない。
...
} else {
if (isNode) recordUp() // falseなので何もしない
recordEndGroup() // endの操作を保存
val parentGroup = reader.parent
val parentNodeCount = updatedNodeCount(parentGroup)
if (expectedNodeCount != parentNodeCount) {
updateNodeCountOverrides(parentGroup, expectedNodeCount)
}
if (isNode) {
expectedNodeCount = 1
}
reader.endGroup()
realizeMovement()
}
exitGroup(expectedNodeCount, inserting)
}
updatedNodeCount(parentGroup)
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
├── composer.recordRemoveNode(count = 1, nodeIndex = 0)
├── composer.recordEndGroup()
└── *composer.updatedNodeCount(group = 9)* ← Now reading
ここではslotReaderからgroupに対応するnodeの数を取得している。
private fun updatedNodeCount(group: Int): Int {
// groupには9が入っている
if (group < 0) return nodeCountVirtualOverrides?.let { it[group] } ?: 0
val nodeCounts = nodeCountOverrides
// nodeCountOverridesおよびnodeCountsはnull
if (nodeCounts != null) {
val override = nodeCounts[group]
if (override >= 0) return override
}
// SlotTableに保管されているnodeの数を取得する
return reader.nodeCount(group)
// 1が返る
}
// SlotReader内
fun nodeCount(index: Int) = groups.nodeCount(index)
private fun IntArray.nodeCount(address: Int) =
this[address * Group_Fields_Size + GroupInfo_Offset] and NodeCount_Mask
end()に戻る
private fun end(isNode: Boolean) {
// All the changes to the group (or node) have been recorded. All new nodes have been
// inserted but it has yet to determine which need to be removed or moved. Note that the
// changes are relative to the first change in the list of nodes that are changing.
// savedState関連なので、このif文は飛ばす。
if (inserting) {
val parent = writer.parent
updateCompoundKeyWhenWeExitGroup(
writer.groupKey(parent),
writer.groupObjectKey(parent),
writer.groupAux(parent)
)
} else {
val parent = reader.parent
updateCompoundKeyWhenWeExitGroup(
reader.groupKey(parent),
reader.groupObjectKey(parent),
reader.groupAux(parent)
)
}
var expectedNodeCount = groupNodeCount
val pending = pending
if (pending != null && pending.keyInfos.size > 0) {
// ここはfalseなので通らない
...
}
// Detect removing nodes at the end. No pending is created in this case we just have more
// nodes in the previous composition than we expect (i.e. we are not yet at an end)
// 削除の検知。
// この場合ペンディングに入れない。予想していたよりもノードが多かっただけ。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
// ここのwhileに入る!
val startSlot = reader.currentGroup
// startSlotは10になっている
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove)
// 前に削除されているので、削除するものはない。一応startSlotは10、reader.currentGroupは13になる。
invalidations.removeRange(startSlot, reader.currentGroup)
}
val inserting = inserting
if (inserting) {
// falseになるのでここは通らない。
...
} else {
if (isNode) recordUp() // falseなので何もしない
recordEndGroup() // endの操作を保存
val parentGroup = reader.parent
val parentNodeCount = updatedNodeCount(parentGroup)
if (expectedNodeCount != parentNodeCount) {
// expectedNodeCount = 0 で parentNodeCountは1! 変更が検知されてif文に入る!
updateNodeCountOverrides(parentGroup, expectedNodeCount)
}
if (isNode) {
expectedNodeCount = 1
}
reader.endGroup()
realizeMovement()
}
exitGroup(expectedNodeCount, inserting)
}
updateNodeCountOverrides()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
├── composer.recordRemoveNode(count = 1, nodeIndex = 0)
├── composer.recordEndGroup()
├── composer.updatedNodeCount(group = 9)
└── *composer.updateNodeCountOverrides(group = 9, newCount = 0)* ← Now reading
/**
* As operations to insert and remove nodes are recorded, the number of nodes that will be in
* the group after changes are applied is maintained in a side overrides table. This method
* updates that count and then updates any parent groups that include the nodes this group
* emits.
* insertやremove nodeがrecordされると、side overrides tableに変更を適応したあとの
* グループのノード数が保管される。
* このメソッドはカウントを更新し、このグループがemitしたノードを含む親グループをアップデートする。
*/
private fun updateNodeCountOverrides(group: Int, newCount: Int) {
// groupはgroup, newCouuntは0
// The value of group can be negative which indicates it is tracking an inserted group
// instead of an existing group. The index is a virtual index calculated by
// insertedGroupVirtualIndex which corresponds to the location of the groups to insert in
// the insertTable.
// グループの値はマイナスになる場合がある。それは既存のグループの代わりにinsertされたグループを
// 追跡していることを示す。
// indexはvertual indexで、insertedGroupVirtualIndexによって計算される。
// insertedGroupVirtualIndexはinsertTableのgroupのinsertされる位置に相当する。
val currentCount = updatedNodeCount(group)
if (currentCount != newCount) {
// currentCount = 1, newCount = 0なので、if文に入る!
// Update the overrides
val delta = newCount - currentCount
var current = group
var minPending = pendingStack.size - 1
// データの状況
// delta = -1
// group = 9
// pendingStack.size = 4 (pendingStackのアイテムたちはすべてnull)
// minPending = 3
// current = 9
while (current != -1) {
// updatedNodeCountはまた1を返すので deltaは-1なので、0になる。
val newCurrentNodes = updatedNodeCount(current) + delta
// current = 9, newCurrentNodes = 0でupdateNodeCount()を呼び出す。
updateNodeCount(current, newCurrentNodes)
for (pendingIndex in minPending downTo 0) {
val pending = pendingStack.peek(pendingIndex)
if (pending != null && pending.updateNodeCount(current, newCurrentNodes)) {
minPending = pendingIndex - 1
break
}
}
@Suppress("LiftReturnOrAssignment")
if (current < 0) {
current = reader.parent
} else {
if (reader.isNode(current)) break
current = reader.parent(current)
}
}
}
}
updatedNodeCount()はgroupに対応するnodeの数を返して1を返す。
private fun updatedNodeCount(group: Int): Int {
// group = 9が渡る
// falseになる
if (group < 0) return nodeCountVirtualOverrides?.let { it[group] } ?: 0
val nodeCounts = nodeCountOverrides
if (nodeCounts != null) {
// nodeCountOverrides = nodeCounts = nullなので、ここは実行されない
// 今回は実行されないが、後述のupdateNodeCount()で代入されて使えるようになる
val override = nodeCounts[group]
if (override >= 0) return override
}
// 以下は1になる。
// これは今の返されたあとcurrentCountに入るので、前の値が取れるみたい。
return reader.nodeCount(group)
}
updateNodeCount()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
├── composer.recordRemoveNode(count = 1, nodeIndex = 0)
├── composer.recordEndGroup()
├── composer.updatedNodeCount(group = 9)
└── composer.updateNodeCountOverrides(group = 9, newCount = 0)
├── composer.updatedNodeCount(group = 9)
└── *composer.updateNodeCount(group = 9, count = 0)* ← Now reading
IntArrayを作成し、groupの数だけ-1で埋めて、
nodeCountOverridesに代入し、nodeCountOverridesを作成する。
nodeCounts[group] = count
で今変更があったgroupのカウントだけ更新する。
つまりnodeCountOverridesに変更があって0個になった場所を書き込む。
private fun updateNodeCount(group: Int, count: Int) {
if (updatedNodeCount(group) != count) {
if (group < 0) {
// group = 9
// count = 0
// updatedNodeCount(group) = 1
val virtualCounts = nodeCountVirtualOverrides ?: run {
val newCounts = HashMap<Int, Int>()
nodeCountVirtualOverrides = newCounts
newCounts
}
virtualCounts[group] = count
} else {
val nodeCounts = nodeCountOverrides ?: run {
val newCounts = IntArray(reader.size)
newCounts.fill(-1)
nodeCountOverrides = newCounts
newCounts
}
nodeCounts[group] = count
}
}
}
実行後のnodeCountOverridesは以下の通り。
updateNodeCountOverrides()に戻る
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
├── composer.recordRemoveNode(count = 1, nodeIndex = 0)
├── composer.recordEndGroup()
├── composer.updatedNodeCount(group = 9)
└── *composer.updateNodeCountOverrides(group = 9, newCount = 0)* ← Now reading
└── composer.updatedNodeCount(group = 9)
/**
* As operations to insert and remove nodes are recorded, the number of nodes that will be in
* the group after changes are applied is maintained in a side overrides table. This method
* updates that count and then updates any parent groups that include the nodes this group
* emits.
* insertやremove nodeがrecordされると、side overrides tableに変更を適応したあとの
* グループのノード数が保管される。
* このメソッドはカウントを更新し、このグループがemitしたノードを含む親グループをアップデートする。
*/
private fun updateNodeCountOverrides(group: Int, newCount: Int) {
// groupはgroup, newCouuntは0
// The value of group can be negative which indicates it is tracking an inserted group
// instead of an existing group. The index is a virtual index calculated by
// insertedGroupVirtualIndex which corresponds to the location of the groups to insert in
// the insertTable.
// グループの値はマイナスになる場合がある。それは既存のグループの代わりにinsertされたグループを
// 追跡していることを示す。
// indexはvertual indexで、insertedGroupVirtualIndexによって計算される。
// insertedGroupVirtualIndexはinsertTableのgroupのinsertされる位置に相当する。
val currentCount = updatedNodeCount(group)
if (currentCount != newCount) {
// currentCount = 1, newCount = 0なので、if文に入る!
// Update the overrides
val delta = newCount - currentCount
var current = group
var minPending = pendingStack.size - 1
// データの状況
// delta = -1
// group = 9
// pendingStack.size = 4 (pendingStackのアイテムたちはすべてnull)
// minPending = 3
// current = 9
while (current != -1) {
// updatedNodeCountはまた1を返すので deltaは-1なので、0になる。
val newCurrentNodes = updatedNodeCount(current) + delta
// current = 9, newCurrentNodes = 0でupdateNodeCount()を呼び出す。
updateNodeCount(current, newCurrentNodes)
// minPendingは3になっていおり、そのため3 2 1と繰り返す
// pendingStackはサイズが4ですべてnullが入っているため、何も行われずにこのループを抜ける。
for (pendingIndex in minPending downTo 0) {
val pending = pendingStack.peek(pendingIndex)
if (pending != null && pending.updateNodeCount(current, newCurrentNodes)) {
minPending = pendingIndex - 1
break
}
}
@Suppress("LiftReturnOrAssignment")
// currentは9なので、elseに入る
if (current < 0) {
current = reader.parent
} else {
// reader.isNode(current)はfalse
if (reader.isNode(current)) break
// currentが9から4に変化する
current = reader.parent(current)
}
}
}
}
今のslotTableがこちらでcurrentが9から4に変化する。
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=-985533309, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@4fb4ae6, androidx.compose.runtime.internal.ComposableLambdaImpl@3b52827]
// 4↓に変わる
Group(4) key=-337788314, 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]
// 9↓から
Group(9) key=-337788167, nodes=1, size=4
Group(10) key=1815931657, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@7421fc3]
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=1815931930, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@a72040]
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]
updateNodeCountOverrides()に戻る
updateNodeCountOverrides()のwhile文は-1になるまで繰り返す。つまり親のカウントをpendingStackに書き込んでいくということになる。
/**
* As operations to insert and remove nodes are recorded, the number of nodes that will be in
* the group after changes are applied is maintained in a side overrides table. This method
* updates that count and then updates any parent groups that include the nodes this group
* emits.
* insertやremove nodeがrecordされると、side overrides tableに変更を適応したあとの
* グループのノード数が保管される。
* このメソッドはカウントを更新し、このグループがemitしたノードを含む親グループをアップデートする。
*/
private fun updateNodeCountOverrides(group: Int, newCount: Int) {
// groupはgroup, newCouuntは0
// The value of group can be negative which indicates it is tracking an inserted group
// instead of an existing group. The index is a virtual index calculated by
// insertedGroupVirtualIndex which corresponds to the location of the groups to insert in
// the insertTable.
// グループの値はマイナスになる場合がある。それは既存のグループの代わりにinsertされたグループを
// 追跡していることを示す。
// indexはvertual indexで、insertedGroupVirtualIndexによって計算される。
// insertedGroupVirtualIndexはinsertTableのgroupのinsertされる位置に相当する。
val currentCount = updatedNodeCount(group)
if (currentCount != newCount) {
// currentCount = 1, newCount = 0なので、if文に入る!
// Update the overrides
val delta = newCount - currentCount
var current = group
var minPending = pendingStack.size - 1
// データの状況
// delta = -1
// group = 9
// pendingStack.size = 4 (pendingStackのアイテムたちはすべてnull)
// minPending = 3
// current = 9
while (current != -1) {
// updatedNodeCountはまた1を返すので deltaは-1なので、0になる。
val newCurrentNodes = updatedNodeCount(current) + delta
// current = 9, newCurrentNodes = 0でupdateNodeCount()を呼び出す。
updateNodeCount(current, newCurrentNodes)
// minPendingは3になっていおり、そのため3 2 1と繰り返す
// pendingStackはサイズが4ですべてnullが入っているため、何も行われずにこのループを抜ける。
for (pendingIndex in minPending downTo 0) {
val pending = pendingStack.peek(pendingIndex)
if (pending != null && pending.updateNodeCount(current, newCurrentNodes)) {
minPending = pendingIndex - 1
break
}
}
@Suppress("LiftReturnOrAssignment")
// currentは9なので、elseに入る
if (current < 0) {
current = reader.parent
} else {
// reader.isNode(current)はfalse
if (reader.isNode(current)) break
// currentが9から
current = reader.parent(current)
}
}
}
}
このループが終わった後
nodeCountOverridesは以下のようになる。
node削除に伴って、これは以下のSlotTableにあるnodesのカウントを更新するためのロジックに見える。
androidx.compose.runtime.SlotTable@5935541
↓nodes=2になっているが、nodes=1にしたい!など
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=-985533309, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@4fb4ae6, androidx.compose.runtime.internal.ComposableLambdaImpl@3b52827]
Group(4) key=-337788314, 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=-337788167, nodes=1, size=4
Group(10) key=1815931657, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@7421fc3]
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=1815931930, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@a72040]
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]
end()に戻る
private fun end(isNode: Boolean) {
// All the changes to the group (or node) have been recorded. All new nodes have been
// inserted but it has yet to determine which need to be removed or moved. Note that the
// changes are relative to the first change in the list of nodes that are changing.
// savedState関連なので、このif文は飛ばす。
if (inserting) {
val parent = writer.parent
updateCompoundKeyWhenWeExitGroup(
writer.groupKey(parent),
writer.groupObjectKey(parent),
writer.groupAux(parent)
)
} else {
val parent = reader.parent
updateCompoundKeyWhenWeExitGroup(
reader.groupKey(parent),
reader.groupObjectKey(parent),
reader.groupAux(parent)
)
}
var expectedNodeCount = groupNodeCount
val pending = pending
if (pending != null && pending.keyInfos.size > 0) {
// ここはfalseなので通らない
...
}
// Detect removing nodes at the end. No pending is created in this case we just have more
// nodes in the previous composition than we expect (i.e. we are not yet at an end)
// 削除の検知。
// この場合ペンディングに入れない。予想していたよりもノードが多かっただけ。
val removeIndex = nodeIndex
while (!reader.isGroupEnd) {
// ここのwhileに入る!
val startSlot = reader.currentGroup
// startSlotは10になっている
recordDelete()
val nodesToRemove = reader.skipGroup()
recordRemoveNode(removeIndex, nodesToRemove)
// 前に削除されているので、削除するものはない。一応startSlotは10、reader.currentGroupは13になる。
invalidations.removeRange(startSlot, reader.currentGroup)
}
val inserting = inserting
if (inserting) {
// falseになるのでここは通らない。
...
} else {
if (isNode) recordUp() // falseなので何もしない
recordEndGroup() // endの操作を保存
val parentGroup = reader.parent
val parentNodeCount = updatedNodeCount(parentGroup)
if (expectedNodeCount != parentNodeCount) {
// expectedNodeCount = 0 で parentNodeCountは1! 変更が検知されてif文に入る!
// 以下で変更後のnodeの数をフィールドに保存する。
updateNodeCountOverrides(parentGroup, expectedNodeCount)
}
// isNode = false
if (isNode) {
expectedNodeCount = 1
}
reader.endGroup()
realizeMovement()
}
exitGroup(expectedNodeCount, inserting)
}
reader.endGroup()
これは一度読んでいる関数なので、さらっと
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
├── composer.recordRemoveNode(count = 1, nodeIndex = 0)
├── composer.recordEndGroup()
├── composer.updatedNodeCount(group = 9)
├── composer.updateNodeCountOverrides(group = 9, newCount = 0)
│ ├── composer.updatedNodeCount(group = 9)
│ ├── *composer.updateNodeCount(group = 9, count = 0)* ← Now reading
│ ├── *composer.updateNodeCount(group = 4, count = 1)* ← Now reading
│ └── *composer.updateNodeCount(group = ..., count = 1)* ← Now reading
└── *slotReader.endGroup()* ← Now reading
currentGroupは13になっている。
次に読んでいくためにcurrentEndを更新をかけているっぽい?
この関数を通して、これまでのcurrentEndが13だったのが16になる。
/**
* End the current group. Must be called after the corresponding [startGroup].
*/
fun endGroup() {
// emptyCount=0なのでtrueになる
if (emptyCount == 0) {
require(currentGroup == currentEnd) { "endGroup() not called at the end of a group" }
val parent = groups.parentAnchor(parent)
this.parent = parent
// parent < 0はfalseになる parentは4になっている。
// groups.groupSize(parent)は12になる。
// 16がcurrentEndになる。
currentEnd = if (parent < 0)
groupsSize
else
parent + groups.groupSize(parent)
}
}
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=-985533309, nodes=2, size=13, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@4fb4ae6, androidx.compose.runtime.internal.ComposableLambdaImpl@3b52827]
// ↓ parent
Group(4) key=-337788314, 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=-337788167, nodes=1, size=4
Group(10) key=1815931657, nodes=1, size=3, slots=[15: androidx.compose.runtime.RecomposeScopeImpl@7421fc3]
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=1815931930, nodes=1, size=3, slots=[19: androidx.compose.runtime.RecomposeScopeImpl@a72040]
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]
realizeMovement()
Content()
├── composer.startRestartGroup()
│ ├── composer.start()
│ └── composer.addRecomposeScope()
│ └── RecomposeScopeImpl.start()
├── composer.rememberedValue()
│ └── composer.nextSlot()
├── composer.endReplaceableGroup()
│ └── composer.endGroup()
│ └── composer.end(isNode = false)
│ ├── composer.recordEndGroup()
│ ├── SlotReader.endGroup()
│ └── composer.exitGroup(expectedNodeCount = 0, inserting = false)
├── composer.changed(MutableState(value = false))
│ └── nextSlot()
├── EffectsKt.LaunchedEffect()
├── composer.startReplaceableGroup()
└── composer.endReplaceableGroup()
└── composer.endGroup()
└── composer.end(isNode = false)
├── composer.recordDelete()
│ └── composer.recordSlotEditingOperation(removeCurrentGroupInstance)
│ ├── composer.realizeOperationLocation()
│ │ └── composer.record { _, slots, _ -> slots.advanceBy(distance) }
│ ├── composer.recordSlotEditing()
│ │ ├── composer.recordSlotTableOperation()
│ │ └── slotReader.anchor(location = 9)
│ └── composer.record(removeCurrentGroupInstance)
├── slotReader.skipGroup()
├── composer.recordRemoveNode(count = 1, nodeIndex = 0)
├── composer.recordEndGroup()
├── composer.updatedNodeCount(group = 9)
├── composer.updateNodeCountOverrides(group = 9, newCount = 0)
│ ├── composer.updatedNodeCount(group = 9)
│ ├── composer.updateNodeCount(group = 9, count = 0)
│ ├── composer.updateNodeCount(group = 4, count = 1)
│ └── composer.updateNodeCount(group = ..., count = 1)
├── slotReader.endGroup()
└── *composer.realizeMovement()* ← Now reading
private fun realizeMovement() {
val count = previousCount
previousCount = 0
if (count > 0) {
if (previousRemove >= 0) {
// previousCountは1
// countは1
// previousRemoveは0
val removeIndex = previousRemove
previousRemove = -1
recordApplierOperation { applier, _, _ -> applier.remove(removeIndex, count) }
} else {
val from = previousMoveFrom
previousMoveFrom = -1
val to = previousMoveTo
previousMoveTo = -1
recordApplierOperation { applier, _, _ -> applier.move(from, to, count) }
}
}
}
recordApplierOperation { applier, _, _ -> applier.remove(removeIndex, count) }
ここではrealizeUps()やrealizeDowns()では判定によって何も行われずに、recordとなるので
applier.remove(removeIndex, count)
の操作が保存される。
このapplierは実際のnodeをいじるAPIになっています。
/**
* Record a change ensuring, when it is applied, that the applier is focused on the current
* node.
*/
private fun recordApplierOperation(change: Change) {
realizeUps()
realizeDowns()
record(change)
}
これによって、ついに。ついに。end()が終わりました
まとめ
基本的にSlotReaderからの取得結果と呼び出した実装を比較し、変更をchangesやnodeCountOverridesに保存していきました。
まだSlotTableに対しての実際の変更は行われていません。
今のchangesをおさらいしましょう。
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) }
今回は削除があったときのchangesを追っていくことができました。
これらのchangesはまだ実行しておらず、次のフェーズでは実際にSlotTableに反映されていくわけです。
次は残ったNode2()は変化がないため、最適化されて呼び出されないのではないかと思われるので、そのあたりを追っていきます。