Class FieldReconciler
slider.sliderModel = m, this.orientation
= VERTICAL in a migrated subclass — which bypass the setter and therefore never reach the
Vaadin peer.
Why this exists at all: in real Swing the JDK's method bodies read those fields at use time, so a direct write mostly takes effect at the next repaint (minus the listener rewiring the setter would have done — the idiom is partially broken on the desktop too). The emulator's peer has no repaint that re-reads fields, so without detection a direct write would do nothing, which is worse than the JDK. The reconcile maps "next repaint" to SB-Emulators' equivalents:
- Steady state:
EHelper.callSwing(Runnable)reconciles the session's registered emulators in its epilogue (outermost envelope only) — every peer→Swing event, Timer fire, SwingWorker callback and post-park continuation funnels through callSwing (R_callswing_envelope), so the write is repaired inside the very roundtrip that made it, before the response flushes. - Bootstrap:
register(FieldReconciler.Reconcilable, Component)arms a one-shotbeforeClientResponseon the emulator's peer, catching field writes made while the app builds its UI during navigation attach, before any callSwing has run. Never re-armed from inside the callback: Flow runs registrations made during the flush in the same pass (StateTree loops to a fixpoint), so a self-rearming callback livelocks — measured 2026-08-31, see D_field_write_reconcile.
A detected write logs a loud ERROR (not the EHelper.onUnimplemented(String, String, Object...) WARN lane —
this is not an SB-Emulators coverage gap but an app antipattern that causes stale data on the desktop
as well) and then repairs by re-running the setter-equivalent push, which is deliberately
more than the JDK does (the JDK leaves the setter's listener rewiring broken
forever) — the accepted R_no_silent_improvements wobble the ERROR names.
The registry is session-scoped (surviving the F5 @PreserveOnRefresh
teleport, like D_session_scoped_pools) and weakly referenced, so disposed frames drop out on
their own. All entry points run under the session lock — callSwing's epilogue and Flow's
beforeClientResponse both hold it — so a plain WeakHashMap-backed set suffices.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceOne emulator's reconcile hook; implemented by each class carrying reconciled fields. -
Method Summary
Modifier and TypeMethodDescriptionstatic voidreconcileAll(com.vaadin.flow.server.VaadinSession session) Reconciles every live registered emulator of this session.static voidregister(FieldReconciler.Reconcilable emulator, com.vaadin.flow.component.Component peer) Adds the emulator to its session's reconcile set and arms the one-shot bootstrap check on its peer.static voidreportDirectWrite(Object emulator, String field, String setter) The loud half of a repaired direct write.
-
Method Details
-
register
public static void register(FieldReconciler.Reconcilable emulator, com.vaadin.flow.component.Component peer) Adds the emulator to its session's reconcile set and arms the one-shot bootstrap check on its peer. Call once, from the emulator's root constructor. No-ops without a current session (bare unit construction) — in production and under Karibu one is always current. -
reconcileAll
public static void reconcileAll(com.vaadin.flow.server.VaadinSession session) Reconciles every live registered emulator of this session. Requires the session lock. -
reportDirectWrite
-