wasm32 never runs a unit's finalization or an atexit handler
import atexit
atexit.register(lambda: print("bye")) # CPython / x86-64: body, bye
print("body") # wasm32: body
A Pascal unit's finalization section was skipped the same way.
Cause and fix
At the end of the body, pasparser_prog.inc calls PXXExitProcess if it
exists, and the finalizer runner otherwise. pyparser.inc calls the runner
directly. Both calls go through EmitCallProc (machine code), and a wasm32
main is built from the body's chunks, so neither call existed there. Each
driver now sets WasmFiniRunner := GetFiniRunnerProc on wasm32, and
WasmEmitMainWrapper calls it after the last chunk. The runner guards itself
with __pxx_fini_done, so a Halt (which already reached the runner on
wasm32) followed by the fall-off cannot run the finalizers twice.
Measured (2026-09-29, fixedpoint 1b5cb8f8976e)
test/test_a_unit_finalization_runs_on_wasm32.pasuses two units, whereuwasmfini2usesuwasmfiniand so must be finalised first. It matches on x86-64, i386, arm32, aarch64, riscv32, xtensa windowed/call0 and wasm32. With the previous compiler, only wasm32 differs.- The expectation is PXX's x86-64 output, not FPC's. FPC 3.2.2 runs these finalizers (a file written from one is created), but loses a finalizer's console output on stdout and stderr alike.
test/test_nilpy_atexit_runs_on_wasm32.npycovers two handlers (last registered runs first), one of which reads state the other changed. It equals CPython on x86-64, i386, riscv32 and wasm32. With the previous compiler, only wasm32 differs.Halt(3)on wasm32 exits 3 and runs each finalizer once.- The 79 wasm32 Makefile blocks: no block changes status.