propagate method

void propagate(
  1. Link link
)

Propagates changes recursively through the dependency graph.

Starting from link, traverses the graph depth-first to mark all affected nodes as dirty or pending. Handles circular dependencies and recursion detection.

This method:

  • Marks dependent nodes as pending or dirty
  • Notifies watching effects for scheduling
  • Recursively propagates through mutable nodes
  • Uses an explicit stack to avoid call stack overflow

The propagation stops at non-mutable nodes (like effects) or when circular dependencies are detected.

Implementation

@pragma('vm:align-loops')
void propagate(Link link) {
  Link? next = link.nextSub;
  Stack<Link?>? stack;

  top:
  do {
    final sub = link.sub;
    ReactiveFlags flags = sub.flags;

    if ((flags &
            60 /*ReactiveFlags.recursedCheck | ReactiveFlags.recursed | ReactiveFlags.dirty | ReactiveFlags.pending*/
        ) ==
        ReactiveFlags.none) {
      sub.flags = flags | ReactiveFlags.pending;
    } else if ((flags &
            12 /*ReactiveFlags.recursedCheck | ReactiveFlags.recursed*/) ==
        ReactiveFlags.none) {
      flags = ReactiveFlags.none;
    } else if ((flags & ReactiveFlags.recursedCheck) == ReactiveFlags.none) {
      sub.flags =
          (flags & -9 /*~ReactiveFlags.recursed*/) | ReactiveFlags.pending;
    } else if ((flags & 48 /*ReactiveFlags.dirty | ReactiveFlags.pending*/) ==
            ReactiveFlags.none &&
        isValidLink(link, sub)) {
      sub.flags =
          flags | 40 /*(ReactiveFlags.recursed | ReactiveFlags.pending)*/;
      flags &= ReactiveFlags.mutable;
    } else {
      flags = ReactiveFlags.none;
    }

    if ((flags & ReactiveFlags.watching) != ReactiveFlags.none) {
      notify(sub);
    }

    if ((flags & ReactiveFlags.mutable) != ReactiveFlags.none) {
      final subSubs = sub.subs;
      if (subSubs != null) {
        final nextSub = (link = subSubs).nextSub;
        if (nextSub != null) {
          stack = Stack(value: next, prev: stack);
          next = nextSub;
        }
        continue;
      }
    }

    if (next != null) {
      link = next;
      next = link.nextSub;
      continue;
    }

    while (stack != null) {
      final Stack(:value, :prev) = stack;
      stack = prev;
      if (value != null) {
        link = value;
        next = link.nextSub;
        continue top;
      }
    }

    break;
  } while (true);
}