propagate method
Push phase: starting from link (the subscriber list of a node that
just changed), walks the graph iteratively marking subscribers
pending (or dirty via shallowPropagate later) and calling
notify on watching nodes. Recursion through mutable nodes is
flattened with an explicit EngineStack. innerWrite must be true
when the write happened inside a running effect, enabling the
re-entrancy bookkeeping (ReactiveFlags.recursed).
Fase push: partindo de link (a lista de subscribers de um nó que
acabou de mudar), percorre o grafo iterativamente marcando subscribers
como pending (ou dirty via shallowPropagate depois) e chamando
notify nos nós watching. A recursão através de nós mutáveis é
achatada com uma EngineStack explícita. innerWrite deve ser true
quando a escrita aconteceu dentro de um effect em execução, habilitando
a contabilidade de reentrância (ReactiveFlags.recursed).
Implementation
@pragma('vm:align-loops')
void propagate(ReactiveLink link, [bool innerWrite = false]) {
ReactiveLink? next = link.nextSub;
EngineStack<ReactiveLink?>? stack;
top:
do {
final ReactiveNode sub = link.sub;
ReactiveFlags flags = sub.flags;
if (!flags.hasAny(ReactiveFlags.propagationState)) {
// First visit this wave: mark "maybe stale".
// Primeira visita nesta onda: marca "talvez obsoleto".
sub.flags = flags | ReactiveFlags.pending;
if (innerWrite) {
sub.flags = sub.flags | ReactiveFlags.recursed;
}
} else if (!flags.hasAny(ReactiveFlags.anyRecursed)) {
// Already marked and not in a recursion scenario: stop here.
// Já marcado e sem cenário de recursão: para aqui.
flags = ReactiveFlags.none;
} else if (!flags.hasAny(ReactiveFlags.recursedCheck)) {
// Re-reached after a previous recursion: clear and re-mark.
// Alcançado de novo após recursão anterior: limpa e remarca.
sub.flags = (flags & ~ReactiveFlags.recursed) | ReactiveFlags.pending;
} else if (!flags.hasAny(ReactiveFlags.stale) && isValidLink(link, sub)) {
// A node reading itself mid-recompute (self-dependency).
// Um nó lendo a si mesmo no meio da recomputação (autodependência).
sub.flags = flags | ReactiveFlags.recursedPending;
flags = flags & ReactiveFlags.mutable;
} else {
flags = ReactiveFlags.none;
}
if (flags.hasAny(ReactiveFlags.watching)) {
notify(sub);
}
if (flags.hasAny(ReactiveFlags.mutable)) {
final ReactiveLink? subSubs = sub.subs;
if (subSubs != null) {
final ReactiveLink? nextSub = (link = subSubs).nextSub;
if (nextSub != null) {
stack = EngineStack<ReactiveLink?>(value: next, prev: stack);
next = nextSub;
}
continue;
}
}
if (next != null) {
link = next;
next = link.nextSub;
continue;
}
while (stack != null) {
final ReactiveLink? value = stack.value;
stack = stack.prev;
if (value != null) {
link = value;
next = link.nextSub;
continue top;
}
}
break;
} while (true);
}