DynamicHelpers.GetMember`/`SetMember` sporadically traps with `unreachable`

Description

Any dynamic-typed property read (occasionally also a write) can — unpredictably, not on every call — trap with a WASM unreachable instruction inside Elements’ own runtime, aborting the entire WASM instance. The DevTools console only ever surfaces a single truncated top frame:

Fatal exception in WebAssembly!
Exception: NullReferenceException: Member access on null reference
Uncaught {stack: RuntimeError: unreachable
    at AlliedHealthClient.wasm-<hash>:wasm-function[1988]:0x120163), message: unreachable}

Capturing the exception via a page-level window.addEventListener('error', ...) (a local try/catch around the JS call that triggers the WASM work does not catch it — it surfaces later than that call returns) yields the full, multi-frame stack, e.g.:

RuntimeError: unreachable
    at ElementsRaiseException
    at ExternalCalls.RaiseException
    at DynamicHelpers.GetMember
    at DynamicView.OnLayoutLoaded          <-- application code, one dynamic .property read
    at DynamicView.Mount's closure (fLoadHandler)
    at MessageHandler.Invoke
    at MessageBus.Publish
    at (JS/WASM boundary, imp.env.__island_invoke)
    at System.WebAssembly.InvokeMethod
    at System.Reflection.MethodInfo.Invoke

The application frame at the point of the trap varies between runs of identical code with identical input — observed in DynamicView.OnLayoutLoaded (reading .status/.data/.layout off a plain JS object), LayoutEngine.RenderStatCard (via the already-“safe” __ahGetProp bridge), LayoutEngine.ParseNode (a SetMember, not a GetMember), and LoginView.HandleSubmit (reading a login-form field — code that had already succeeded dozens of times earlier in the same session). All of these call sites are reached via the app’s “hidden trigger button + onclick +=” bridge pattern (see the App.pas remarks above WireBridgeTriggers), i.e. a delegate invoked through System.Reflection.MethodInfo.Invoke across the JS/WASM boundary — every observed crash is several frames below that reflection-based invoke.

My apologies, notice that the bug ID postback is not working. We are investigating: bugs://E27880

I’m told this looks like a bug in the user code, not compiler or library:

For the supplied clean repro: yes.

payload.status is absent, and the code still evaluates statusVal.ToString in an and expression, producing the null-reference exception. With Wasm exceptions disabled, that surfaces as unreachable.

That does not rule out a separate RTL/compiler defect in the original application, but this clean repro is explained and fixed in user code by guarding with assigned(statusVal) before dereferencing it.

Thanks for looking at the clean repro — the diagnosis was right in substance, though the literal fix isn’t and then: Oxygene doesn’t have that operator (confirmed by a compile error, “Expression expected”, when I tried it).

What’s actually going on: and/or between two statically-typed Boolean operands short-circuit normally, but when the operands are dynamic, both sides seem to evaluate unconditionally before combining. So (x <> nil) and (x.ToString = 'foo') still calls .ToString on a nil x — the left-hand nil check doesn’t guard anything once dynamic dispatch is involved.

I audited our app for this exact shape (a dynamic value nil-checked and dereferenced in the same and chain) and found 8 real instances across 6 files. Restructured each as a nested if instead of a compound and. One useful data point: a direct WebSocket capture of a real server response (bypassing the WASM client entirely) confirmed our success responses have no status key at all — so a status.ToString guarded only by status <> nil inside an and was firing on literally every successful message, not intermittently. That’s fixed now.

However, after fixing all of those, our list-screen crash (array-shaped payload, e.g. a response with an empty data:  ) still reproduces identically — same NullReferenceException: Member access on null reference, same call site (extracting fields off the parsed WebSocket message before it’s even dispatched to application code). I confirmed via the same direct WebSocket capture that the actual payload is well-formed — empty array, flat string columns, nothing unexpectedly null — so this isn’t an app-level bug. Looks like the array-shaped-payload marshaling issue is a second, separate bug from the and/dynamic one above.

So: two distinct issues bundled into this thread. The and/dynamic short-circuit one is fixed on our end (might be worth a doc note somewhere — it’s surprising that a dynamic and silently drops the short-circuit guarantee a plain Boolean and gives you). The array-shaped-payload one is still open and still crashes for us on every list-type screen.

My apologies; what’s the exact code that didn’t compile? I don’t recall suggesting and then, thast indeed not a supported syntax.

Ah interesting! I’ll have to raise that up witht he compile rteam if we consider this as designed or a bug. i agree it’s a bit unintuitive.

Hmm, okay. i will take this back to the team…

Logged as bugs://E27883 to review dynamic and short-cuicuit.

We’ll the exact captured list response and the small OnMessage/field-extraction method from the failing build.

Update, and a correction to my last post: after fixing the 8 and/dynamic instances I described, the array-shaped-payload crash I said was still open has not reproduced across several fresh re-tests — dashboard load, then a list screen, then dashboard again, then another list screen, all dynamic-heavy load_layout round-trips in one session. All rendered correctly or failed gracefully with an ordinary app-level error. Zero WASM exceptions.

Worth flagging the methodology issue that caused my last post to overstate confidence: our server serves the WASM bundle from a build output directory, and the browser caches AlliedHealthClient.wasm/.js independently by a ?v=... query string baked into index.html, separate from the outer page’s own cache. For a chunk of this investigation I was rebuilding and copying files correctly but re-testing against a stale cached .wasm with zero errors to signal it — the browser just silently kept running the old code. Once I confirmed via network-request inspection that the browser was actually fetching the freshly built file, the “still crashes” result from my last post turned out to be unreliable.

So: I’m no longer confident there’s a second, distinct array-marshaling bug here at all. It’s entirely possible the and/dynamic short-circuit issue was the whole story, and what looked like a second crash was just me testing an old build. Not declaring it fully resolved — this class of bug has looked fixed before and come back — but wanted to correct the record rather than leave a “still open” claim standing that I can no longer reproduce. Happy to dig further if useful for E27880.

Excellent. Keep an eye on it, and we can reopen if the issue returns.

Separately, i think we’ve decioded the lack of shortcircuit on dynamic is not needed and we can fix it to behave as expected. Not 100% sure yet.