Remaining test-call wrappers — 2026-09-22
Pass-5 #7 asks for API-metadata-generated test exports. The current audit
started at 191 generated / 68 handwritten wrappers. Twenty Winsock wrappers
were already exactly the generator's save-ESP/call/restore-ESP/read-EAX contract.
They now opt into test_call: true; 211 are generated and 48 remain handwritten.
The removed wrappers cover socket, bind, listen, connect, accept, send, recv, select, shutdown, closesocket, ioctlsocket, setsockopt, getsockopt, getsockname, htons, ntohs, inet_addr, inet_ntoa, gethostbyname and WSAGetLastError. The generated exports retain their names and signatures. A parsed comparison against the pre-change functions proves identical instruction trees after normalizing local names/indices and grouped parameter declarations.
test-api-generation-metadata.js now rejects handwritten wrappers matching
that generated contract. Its self-checks cover renamed locals, numeric locals,
grouped parameters, and preservation of different call targets/defaulted input.
Before migration it rejected test_call_socket; afterward it passes. This is
an exact structural guard, not proof that every non-matching body is necessary.
Remaining inventory
| Shape | Remaining wrappers / next action |
|---|---|
| Full API argument list, more than five words | LoadImageA, CreateRoundRectRgn, MaskBlt, GetTextExtentExPointA/W, GetGlyphOutlineA, GetCharacterPlacementA, CreateDIBitmap, TabbedTextOutA/W, BitBlt, PatBlt, StretchBlt, SetDIBitsToDevice, DrawDibDraw, RoundRect, Arc, ArcTo, AngleArc, Chord, Pie, sendto, recvfrom. Extend/test generated stack-argument support before migrating; some currently omit ESP restoration. |
| Reduced/defaulted signatures | GetVolumeInformationA, CreateFontW, CreateDCA/W, StartDocA, CreateDIBSection, SelectPalette, mmioGetInfo, ExtTextOutA/W, MoveToEx, OpenMutexA, CreateMutexA, GetClassInfoW, SHGetFileInfoW. Preserve or explicitly migrate their callers; do not silently change test ABIs. |
| Extra adapter names | CreateDIBSectionUsage, ExtTextOutAWithDx, WSAFDIsSet. These names differ from their underlying API entry. |
| Direct core calls | WinHelpA/W call the help core; separate test_invoke exports already exercise public handlers. |
| Deliberate ESP side effects in current wrappers | TlsGetValue, TlsSetValue, TlsFree set a fixed test stack; SetLastError does not restore ESP and has no result. Audit caller expectations before migration. |
| No API-table entry | WSAIsBlocking has a handwritten handler/test export but no API metadata entry. Retained rather than dropping its existing test surface. Public name resolution is a separate gap to verify. |
The first pass initially counted WSAIsBlocking among the migrations; the metadata audit and runtime test caught that omission, and its export was restored before the final passing run. No new API was registered by this change.
Verification passed: 42/42 Winsock checks, 6/6 hostname checks, 5/5 real two-process virtual-LAN loopback checks, metadata/structural checks, the 20-function before/after comparison, generator freshness, append-only API IDs, fragment balance, logical operands, tier discovery and whitespace.
This is not closure of all test-wrapper work. No networking implementation, stub return, callback behavior or production API ABI was changed. Tests run in the shared worktree, including another agent's pending Winsock changes; no clean release, native Win98 or performance result is claimed.
Stack-argument follow-up
The generator now accepts opted-in i32-word signatures through 16 arguments.
For signatures above five, it writes all arguments at ESP+4, +8, ...
through guest gs32, passes the first five to the handler, and restores ESP
after the synchronous call. Writing only the tail would miss handlers such
as PatBlt which also read early stack words. The caller supplies writable
space for return address plus arguments; the return slot is not overwritten.
This is a direct test bridge, not a callback/async runner or a variadic/64-bit
argument ABI. Existing five-or-fewer generated wrappers are unchanged.
All 23 full-signature wrappers in the inventory's first row are migrated: 234 generated / 25 handwritten. Public handler implementations and export signatures are unchanged. Unlike the old CreateDIBitmap test wrapper, the generated one restores ESP; the other wrappers already restored it. The full-frame write is intentional, not an instruction-identical refactor.
test-api-stack-wrappers.js replaces only the generated wrappers'
called endpoints with an ABI recorder during compilation. It exercises all
23 at four byte alignments: 92 calls, checking first-five handler inputs,
zero name pointer, every stack argument observed inside the handler, return
value, ESP restoration, and guards before/after the frame and in the return
slot. A compiler-only negative control omitting the 13th argument write fails
DrawDibDraw's recorded-stack assertion. No source/artifact changes are needed
for that negative control. Real-handler GDI and Winsock suites run separately.
Verification passed: the 92 ABI calls, 39 raster-handler checks, six LoadImage DIB-section checks, bitmap text layout, 42 Winsock checks and the existing 224-record font-metric tolerance gate. That last gate permits documented metric differences; passing it is not exact native font compatibility. Metadata/generator, API IDs, fragments, logical operands, handler cleanup, tier discovery and whitespace also passed in the shared worktree.
The remaining 25 comprise the inventory's other five rows; their signature, setup or endpoint differences still require explicit handling. No claim of complete review closure or improved runtime performance follows from this work.
TLS wrapper audit
Only test-process-id.js calls the three handwritten TLS Get/Set/Free exports.
Its original checks inspected invalid-index results, not the helpers' stack
effects. Each helper replaced ESP with 0x00300000 and left it advanced by the
handler. That scratch address is not part of the API or needed by these handlers.
The added tests require the caller's ESP to survive every direct test call,
check both 64 and UINT_MAX as invalid indices with error 87, and exercise valid
Get/Set values independently in two WASM instances sharing process memory.
The old wrapper fails the new ESP assertion with 0x00300008 instead of the
caller's 0x07408000. The three wrappers now use generated metadata exports,
bringing the inventory to 237 generated / 22 handwritten. Their names,
parameters and return values are unchanged; preservation of caller ESP is the
intentional test-helper behavior change. Production TLS handlers are unchanged.
Verification passed: the shared-process identity/TLS suite including new ABI,
error and value-isolation checks; metadata generation, API IDs, fragment balance,
logical operands, tier discovery and whitespace. The isolation check uses two
instances sequentially, not simultaneous Worker stress.
A separate runtime gap was found, not fixed by wrapper generation:
handle_TlsFree returns success for any index below 64 but releases no index.
tls_reserve is a monotonically increasing cursor which remains exhausted at
64. Proper reusable allocation requires a coordinated allocator/free change
and tests across thread creation/static TLS and shared-memory instances; the
wrapper tests must not be cited as evidence that this lifetime contract works.
Microsoft documents index reuse for
TlsFree
and initially zero slots for
TlsAlloc.
These are current official contracts, not a fresh native Win98 capture.
The allocator is also used by 08-pe-loader.wat for static TLS; spawn metadata
publishes a monotonic minimum through set_tls_next_index. Both paths need
coverage before replacing the cursor with a reusable-index allocator.
SetLastError remains handwritten here: its current export is void and advances ESP, so migrating it requires explicitly handling that test-call contract.
Subsequent native Win98 observations show 80 slots, immediate cross-thread clearing on free, and preservation of LastError on successful Alloc/Set/Free. Thus the existing emulator tests' index-64 rejection is an implementation-limit assertion, not native conformance, and must be replaced in the runtime fix. The repeated native fixture now defines that follow-up's target; this wrapper migration did not correct those runtime discrepancies.
The later TLS runtime change implements that captured sequence and adds generated TlsAlloc test calls: 238 generated / 22 manual.
Full-signature GDI follow-up
MoveToEx and SelectPalette now use generated full-signature test exports: 240 generated / 20 manual. All existing callers explicitly supply the formerly hidden NULL previous-point pointer or FALSE background flag. This is an intentional test-export ABI extension, not a production API change. MoveToEx also now preserves ESP; its old helper left it advanced by 20 bytes.
test-gdi-wrapper-signatures.js fails against the old export's three-argument
signature. With generation enabled it verifies the four-argument MoveToEx
surface, signed previous-position output, guards around that POINT, and ESP
preservation for NULL and non-NULL output. SelectPalette exposes all three
arguments and preserves ESP with either background flag. The production
SelectPalette implementation currently does not distinguish the background
flag; those checks are not evidence of foreground/background palette behavior
on native indexed displays.
Correction: AngleArc is not an all-integer signature
The broader path suite exposed a regression introduced by 1369e721: its
AngleArc export originally accepted two f32 angles, but the all-i32 generator
changed their interpretation to integer bit patterns. The earlier statement
that every export signature was unchanged was wrong. Restoring HEAD's wrappers
in a compiler-only baseline reproduces the same path failure, excluding today's
MoveToEx/SelectPalette migration as its cause.
Generation now derives f32 parameters from existing args[].type: "FLOAT"
metadata and reinterprets their bits into both direct handler arguments and
stack words. No separate type list is introduced. The ABI recorder now passes
fractional negative numbers for FLOAT arguments and independently computes
their expected IEEE-754 words. The existing AngleArc path checks cover a
90-degree sweep, multiple turns, and NaN/infinite input rejection. This restores
the original floating-point test ABI; the production handler is unchanged.
Verification: full-signature/POINT/ESP regression, 92 ABI recorder calls, 28 path checks, 10 palette checks, nine geometry checks, nine Priority-0/1 checks, nine bitmap checks and the real-window surface test pass. Metadata, generator freshness, append-only API IDs, fragment balance, logical operands, handler ESP, test tiers and whitespace checks pass. The benchmark caller was updated mechanically but no timing result is claimed. These are shared-worktree checks, not a full release or native Win98 GDI conformance sweep.
Eight defaulted adapters migrated
CreateDCA/W, StartDocA, mmioGetInfo, OpenMutexA, CreateMutexA, GetClassInfoW
and SHGetFileInfoW now expose the complete metadata signatures. Existing test
callers explicitly supply their previous zero defaults, including the leading
instance/security arguments; OpenMutexA retains its former 0x001f0001 access
mask. No production handler changes. The last four old wrappers advanced ESP;
their generated replacements preserve it. Inventory: 248 generated / 12
manual.
The ABI recorder now exercises every generated wrapper, not only those with stack arguments: 248 exports at four alignments, 992 calls. It checks arity, all five direct argument positions (including zero padding), FLOAT bit patterns, stack words for signatures above five, untouched stack words for short signatures, return-address/boundary guards, result and restored ESP. Zero-argument wrappers are covered without reading a nonexistent argument. A compiler-only negative control replacing CreateMutexA's name argument with zero fails the direct-argument assertion.
The MMIO behavioral rerun found an existing stale assertion, reproduced with
all WAT sources loaded from committed HEAD. A provider-backed read parked on
IO_WAIT has not returned to the guest: io_block preserves EAX, restores the
frame and redirects to the thunk. The test now seeds and checks unchanged EAX
while suspended, retaining the yield/ESP/EIP checks and the actual byte count
and copied bytes after retry. The buffered-refill assertion's wording likewise
no longer calls its intermediate zero a completed API success. This changes
test expectations, not the runtime's asynchronous read behavior.
The remaining manual exports are GetVolumeInformationA, CreateFontW, CreateDIBSection, CreateDIBSectionUsage, ExtTextOutA/W, ExtTextOutAWithDx, WinHelpA/W, WSAFDIsSet, WSAIsBlocking and SetLastError. These still need explicit signature, endpoint or public-registration decisions; this is not full P5 #7 closure.
Verification includes printer-surface rendering, SetAbortProc, GDI state,
MMIO buffer/lazy retry, 33 wide-API checks and 17 kernel32 error checks.
While another agent's present-pacer edit temporarily left the shared DirectX
fragment unbalanced, a temporary compiler harness pinned WAT to 7e0941d2
plus only these eight wrapper migrations. Its 992 ABI calls, MMIO, wide-API
and kernel32 suites passed; JS/tests remained from the working tree, so this
was a pinned-WAT check, not a clean full-tree release. The unbalanced fragment
was subsequently repaired by its owner. No other agent's code was reverted.
The MMIO, wide-API and kernel32 suites then passed again on current worktree
WAT. Metadata/generation, API IDs, fragment balance, handler ESP, test tiers and
whitespace checks passed. No full build, browser sweep or timing claim.
Volume and text stack-argument adapters
GetVolumeInformationA and ExtTextOutA/W now use the generated eight-argument exports. ExtTextOutAWithDx is removed: its callers use ExtTextOutA's normal eighth argument, and every former seven-argument caller explicitly supplies NULL. The dynamic A/W glyph-index call site is migrated too. The inventory is 251 generated / 8 manual, and the complete ABI recorder passes 1,004 calls at four alignments.
Volume callers no longer supply a private stack pointer as an API argument or poke the filesystem-name pointer into that private frame. They set ESP to writable test storage and pass all eight real arguments, including a nonzero filesystem-name capacity. The volume-label helper now allocates the full 36-byte return-plus-arguments frame; generated calls restore ESP.
This exposes a separate production gap, not fixed here:
volume_information reads the filesystem-name pointer at ESP+28 but ignores
the capacity at ESP+32 and writes the complete FAT/CDFS string. The prior ISO
test omitted that capacity yet expected the string; its success therefore did
not establish bounded-buffer correctness. The migrated ISO test supplies 16
characters. Zero/short capacities and output/error semantics need a native
Win98 reference and a bounded A/W runtime fix; do not cite these green tests
as conformance for that case.
Verification passed: 24 ISO checks, volume-label mutation, ExtTextOut clipping and opaque backgrounds, A/W glyph-index rendering including explicit ETO_PDY advances, default bitmap fonts, bitmap layout and text-path compatibility, and 21 Diablo font-atlas checks. Metadata/generator freshness, append-only API IDs, fragment balance, handler ESP, tiers and whitespace checks passed. No production volume/text handler, native reference, browser or performance change is claimed. Remaining adapters: CreateFontW, CreateDIBSection and its Usage alias, WinHelpA/W, WSAFDIsSet, WSAIsBlocking, SetLastError.
The subsequent native volume-buffer oracle captures 84 cases twice. Win98 rejects short ANSI buffers with error 111 and ordered partial output (scalars, filesystem name, then label); Unicode returns 120 without touching outputs. Runtime correction remains open, and the current Unicode extension needs an explicit compatibility-policy distinction.
The volume runtime follow-up now fixes ANSI bounds and output order against all 42 native ANSI cases. The existing Unicode extension receives the same safeguards without claiming native W conformance. Its generated test export brings the inventory to 252 generated / 8 manual.
WinHelp endpoints and Winsock alias
WinHelpA/W and __WSAFDIsSet now use generated public-handler wrappers:
255 generated / 5 manual. The two handwritten WinHelp invocation bodies
and the handwritten WSAFDIsSet alias are removed; their callers use canonical
test_call_WinHelpA/W and test_call___WSAFDIsSet exports.
The old exports named test_call_WinHelpA/W actually bypassed the public
handlers and called the help dispatch core directly. Those useful lower-layer
hooks remain, explicitly named test_help_dispatch_a/w. Parser tests keep
using that layer; public UI/lifetime tests and the CLI help-macro action use
the generated public-handler layer. This is not merely a census rename:
three boilerplate bodies are deleted without conflating the two help layers.
No production help or network handler changes.
Verification passed: 1,020 ABI recorder calls (255 wrappers at four stack alignments), 621 WinHelp checks and 42 Winsock checks. Metadata, generated dispatch freshness, append-only API IDs, fragment balance, handler ESP, test tiers, CLI JavaScript syntax and whitespace checks pass. The CLI help-macro call site was migrated but not exercised through a real CLI app run here. No new native reference, browser sweep, performance result or full release-build claim.
Remaining handwritten adapters: CreateFontW, CreateDIBSection, CreateDIBSectionUsage, WSAIsBlocking and SetLastError. Their special signatures or missing public registration still require individual review; P5 #7 is not yet closed.
SetLastError adapter stack lifetime
SetLastError now uses the generated adapter: 256 generated / 4 manual. Its old handwritten adapter leaked the public handler's eight-byte stdcall cleanup on every test call. The generated adapter restores ESP. SetLastError itself remains void; the adapter's standard EAX snapshot is not an API return value and callers must not interpret it as one. No production handler change.
The kernel32 regression checks all DWORD bits for zero, a positive value, the high bit and all ones, and checks ESP after each call: 25 checks pass. A compiler-only negative control removes just this adapter's ESP restore: all four new stack assertions fail while the error-code assertions still pass. The complete ABI recorder passes 1,024 calls. TLS native replay (108 API observations), cross-thread clearing/static-template checks, and DisableThreadLibraryCalls tests pass. Metadata, generator freshness, API IDs, fragment balance, handler ESP, tiers and whitespace checks pass. No browser, performance or full release-build claim. Remaining: CreateFontW, the two DIB adapters and WSAIsBlocking; DIB migration is deferred during another agent's active sparse-BITMAPINFO correction.
CreateFontW full signature
CreateFontW now uses its metadata's full 14-argument generated adapter, bringing the inventory to 257 generated / 3 manual. All 24 caller files (tests and the font-specimen tool) now pass explicit values instead of relying on the former four-argument helper. Height, weight, italic and face retain their prior inputs; the other arguments are zero. This also initializes charset, which the old helper left as whatever occupied ESP+36. No production font handler was changed.
The font-object regression now supplies nonzero width, charset and pitch/family, checks those fields alongside height, weight, italic and face through public GetObjectW, checks ESP preservation, and deletes the object. The complete ABI recorder passes 1,028 calls, covering all 14 argument positions at four stack alignments. All 24 caller files pass syntax checks. Font-object, default-bitmap-font, wide text extents, GDI state, scalable text and glyph-index tests pass. The 224-record Win98 metric reference test passes its existing measured tolerances; those tolerances include substantial mismatches and are not pixel-exact or general font conformance. Metadata, generator freshness, API IDs, fragment balance, handler ESP, test tiers and whitespace checks pass. No specimen rendering, benchmark, browser sweep or full release-build result is claimed.
Remaining adapters are CreateDIBSection, CreateDIBSectionUsage and WSAIsBlocking. Broader review closure remains open.
DIB adapters and final registration gap
CreateDIBSection now has one generated six-argument export. The three-argument
helper and four-argument Usage alias are removed; RGB and logical-palette
callers pass their usage explicitly, followed by the output pointer and zero
section/offset. The inventory is 258 generated / 1 manual. The old
three-argument helper leaked 28 bytes of ESP per call; the generated adapter
restores ESP, now asserted in the public palette suite. All 39 working-tree
caller files pass syntax checks. This commit migrates 38 existing caller files;
the sparse-BITMAPINFO test's one call was migrated in the shared worktree and
included by its owner in bd56cd1f, without claiming that agent's file.
The full ABI recorder passes 1,032 calls. Palette checks pass 10/10 and raster checks pass 39/39. Bitmap handlers and DIB address translation each pass 9/9; dirty-sync and the other agent's sparse-BITMAPINFO regression pass on the shared worktree (the latter includes that agent's production fix). Metadata/generation, API IDs, fragment balance, handler ESP, tiers and whitespace checks pass. This migration does not change production DIB behavior or claim support for mapping-backed DIBs: the handler still ignores hSection/offset, and behavioral callers here supply zero. That runtime gap remains open, as do native/browser/full-release validation.
The final handwritten adapter, WSAIsBlocking, is not ordinary boilerplate waiting for a metadata flag: its handler is absent from the API table, and its test invokes it directly. Microsoft's WSAIsBlocking documentation places this legacy API behind WINSOCK/WSOCK32 compatibility, not a direct WS2_32 export. The existing handler always returns false because this runtime does not execute nested blocking hooks. Microsoft's blocking-hook description also describes per-thread application hooks and nested Winsock restrictions. Adding a name to the global table would not establish those semantics or DLL-scoped visibility. Registration, ordinal resolution and blocking-hook behavior need a coordinated correction, not a census-only migration. P5 #7 and the wider review remain open.