subagent transcript
session 18e25685 · workflow wf_23df5b1c
delegated task
You are an adversarial verifier for a FreeCAD-wasm code review. A reviewer claims: SEVERITY: high CATEGORY: gl-function-resolution FILE: freecad-port/
▸ Magik
You are an adversarial verifier for a FreeCAD-wasm code review. A reviewer claims:
SEVERITY: high
CATEGORY: gl-function-resolution
FILE: freecad-port/FreeCAD/src/3rdParty/coin/src/glue/gl.cpp
SUMMARY: After JSPI, invert and complete the getprocaddress path so Coin resolves EVERY modern GL entry point through our signature-matched `fcWasmResolveGL` C symbols instead of emscripten_GetProcAddress — this removes Blocker B (indirect-call signature traps) deterministically.
DETAIL/EVIDENCE: src/3rdParty/coin/src/glue/gl.cpp:609-651 `cc_glglue_getprocaddress`: the `#ifdef __EMSCRIPTEN__` branch currently tries `emscripten_GetProcAddress(symname)` FIRST (line 620) and only falls back to `fcWasmResolveGL` (line 627) when that returns null. That is backwards per R9 §3.1: emscripten's returned pointers are the ones called *indirectly* by Coin, where wasm enforces exact type signatures and mismatches trap ('function signature mismatch' / 'table index out of bounds'). Coin resolves ~157 modern entry points this way — 138 via `PROC(w, ...)` (macro at gl.cpp:881) plus 19 direct `getprocaddress(w, "gl...EXT")` (glGenFramebuffersEXT, glBindRenderbufferEXT, glGenerateMipmap, glRenderbufferStorageEXT, ...). Fix: (1) on wasm, resolve through `fcWasmResolveGL` FIRST and only use emscripten as a last resort for functions we don't wrap; (2) extend the `fcWasmResolveGL` table in src/Gui/WasmGLFixedFunc.cpp:508-525 (currently only ~15 legacy symbols) to cover the whole PROC() set — buffers (glGenBuffers/BindBuffer/BufferData/DeleteBuffers/MapBuffer*), textures (glGenTextures/BindTexture/TexImage2D/TexParameteri/glActiveTexture), FBO/renderbuffer family, glDrawRangeElements, blend/stencil/glPolygonOffset — each as a thin `EM_JS`->`GLctx.<call>` wrapper (real WebGL2) or an emulation entry, with a signature exactly matching Coin's `PFNGL...PROC` typedef. Coin then only ever calls our statically-linked C symbols, eliminating the indirect-call type check entirely (R9 §3.1, Phase C1).
RECOMMENDATION: Reorder the __EMSCRIPTEN__ branch to prefer fcWasmResolveGL; enumerate all PROC()/getprocaddress symbols (grep in gl.cpp) and add a signature-matched wrapper for each in WasmGLFixedFunc.cpp. Keep emscripten_GetProcAddress only as a fallback for symbols with no wrapper.
Independently verify by reading the actual file(s) and running git/grep as needed (repo at /home/magik6k/lcad-wasm/freecad-port/FreeCAD, toolchain at /opt/toolchains, deploy at /home/magik6k/lcad-wasm/freecad-port/deploy-gui). Default to REFUTED if you cannot confirm the evidence. Adjust severity to what the evidence actually supports.
Fable
thinking
I should check the actual files to verify what's being claimed.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/glue/gl.cpp", "offset": 590, "limit": 80}
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && git log --oneline -5 2>/dev/null; echo \"---\"; grep -n \"fcWasmResolveGL\\|emscripten_GetProcAddress\\|__EMSCRIPTEN__\" src/3rdParty/coin/src/glue/gl.cpp | head -40", "descrip…
Fable
thinking
The reviewer's evidence checks out—emscripten_GetProcAddress is called first, followed by fcWasmResolveGL. Now I need to verify the PROC macro claims, the direct getprocaddress calls, and what's in the WasmGLFixedFunc.cpp table.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/WasmGLFixedFunc.cpp", "offset": 490, "limit": 60}
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD && echo \"=== PROC( count ===\"; grep -c \"PROC(w,\" src/3rdParty/coin/src/glue/gl.cpp; echo \"=== PROC macro def ===\"; grep -n \"define PROC\" src/3rdParty/coin/src/glue/gl.cpp;…
Fable
thinking
I'm verifying the key findings against the evidence: the backwards ordering of the fallback mechanism is confirmed, the macro count matches closely at 136 versus the expected 138, and I've verified exactly 19 direct getprocaddress calls.
Now I need to examine the core technical claim itself. The reviewer argues that emscripten_GetProcAddress returns pointers that cause type signature mismatches when called indirectly through the wasm table, triggering traps. But this premise seems flawed — emscripten's GL function wrappers are generated to match standard GL prototypes, so the signatures should align correctly. Even more fundamentally, the claim that using statically-linked C symbols through …
Fable
⚙ Bash {"command": "echo \"=== find R9 / Blocker B docs ===\"; grep -rniEl \"blocker b|signature mismatch|indirect.?call|R9\" --include=*.md --include=*.txt . 2>/dev/null | head; echo \"=== search fcWasmResolveGL usages ===\"; grep -rn \"fcWasmRes…
Fable
thinking
I need to check what section 3.1 of the R9 document actually specifies to see if the reviewer's interpretation aligns with the original design.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/research; echo \"=== R9 section 3.1 ===\"; grep -n \"3.1\\|signature\\|indirect\\|Blocker\\|getprocaddress\\|fcWasmResolveGL\\|emscripten_GetProcAddress\" R9-coin-webgl-strategy.md | head…
Fable
thinking
I need to review the full section of R9 3.1 to see if this concern is already addressed in the documentation.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/research/R9-coin-webgl-strategy.md", "offset": 10, "limit": 90}
Fable
thinking
The reviewer's finding is essentially restating what's documented in R9 §3.1's Phase C1 plan, and it accurately reflects the design. Looking at the evidence, the code does have the ordering backwards—emscripten comes first, then fcWasmResolveGL—but the documented architecture explicitly says everything should resolve through fcWasmResolveGL, not emscripten, so there's a clear contradiction between the current implementation and the spec.
The concrete details all check out: the PROC macro counts are roughly accurate, the fcWasmResolveGL table does have around 14-15 legacy symbols that need completing, and the specific line numbers the reviewer cited are correct. The underlying technical prem …
Fable
I've verified the finding against the actual source and the repo's own design docs. Here's what I found.
**Evidence checks (all confirmed):**
- gl.cpp:609-651 `cc_glglue_getprocaddress`: the `#ifdef __EMSCRIPTEN__` branch does try `emscripten_GetProcAddress(symname)` FIRST (line 620) with `fcWasmResolveGL` only as fallback (line 627). Ordering is exactly as described — backwards relative to the intended architecture.
- PROC macro at gl.cpp:881 (`cc_glglue_getprocaddress` form) — confirmed. Count is 136 `PROC(w,...)` sites (reviewer said 138) + exactly 19 direct `getprocaddress(w,"gl...EXT")` calls (FBO/renderbuffer family, glGenerateMipmap, etc.) — reviewer's "~157" is 155; immaterial.
- `fcWasmResolveGL` in WasmGLFixedFunc.cpp:508-525 — confirmed, ~14 legacy symbols only (glClientActiveTexture, glMultiTexCoord*, matrix/client-array set).
**Alignment with repo intent:** research/R9-coin-webgl-strategy.md §3.1 ("The key architectural refinement (fixes Blocker B)") explicitly states: do NOT resolve to emscripten's implementations; resolve *everything* through `fcWasmResolveGL` with signatures matching Coin's `PFNGL…PROC` typedefs. R9 lines 20-25 document "function signature mismatch"/"table index out of bounds" traps as an *observed* failure class. So the finding faithfully restates the project's own plan, and the current ordering does contradict it.
**Where I moderate severity from high to medium:**
1. Not a demonstrated live crash. The current ASYNCIFY build "fully boots" and Stage 4 renders (git log + R9 §3 "proven to work"). The inversion is an interim state, not a regression breaking present rendering.
2. R9 §3.1 explicitly sequences this AFTER JSPI ("Do the JSPI migration first... then do the wrapper work"), and the git log shows JSPI is not yet in ("keep ASYNCIFY link config"). The finding's own summary concedes "After JSPI." So it's tracked, gated future work, not an urgent defect.
3. The recommendation understates effort/risk: for genuine WebGL2 functions (glGenBuffers/glBindBuffer/glTexImage2D/glGenTextures...), emscripten's GL layer performs non-trivial GL-integer-name ↔ WebGLObject handle translation. Routing these to "thin EM_JS→GLctx wrappers" would bypass that handle table and break unless reimplemented — so it is not thin, and blindly wrapping functions emscripten already handles correctly risks regressions. The phrase "eliminating the indirect-call type check entirely" is also imprecise: calling our own C symbols via function pointers still uses `call_indirect` with a signature check; the benefit is that our signatures match by construction, not that the check disappears.
The factual evidence is accurate and doc-backed, so the finding stands, but as medium-severity planned/architectural work rather than a live high-severity bug.
Fable
⚙ StructuredOutput {"verdict": "CONFIRMED", "reason": "All concrete evidence verified in source: gl.cpp:620 calls emscripten_GetProcAddress FIRST with fcWasmResolveGL only as fallback at line 627 (ordering is backwards vs. intended architecture); PROC macro a…