From 078a05b83e298607db17cabd5e540d73ee6f126f Mon Sep 17 00:00:00 2001 From: nicodes Date: Mon, 3 Aug 2026 03:00:27 -0600 Subject: [PATCH] Say that termcade_init has to come first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ABI lists the exports without stating that one of them constructs the game, so a second host implementation reads the table, calls termcade_playfield to learn the framebuffer size, and takes a nil pointer dereference inside the guest. Found by running tetris under a JS WebAssembly engine to see whether the app could host games too. The arcade has always had the order right — abi, init, playfield — which is exactly why nothing caught it: the only host was the one that already knew. Co-Authored-By: Claude Opus 5 (1M context) --- docs/abi.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/abi.md b/docs/abi.md index 8250719..388d266 100644 --- a/docs/abi.md +++ b/docs/abi.md @@ -18,6 +18,11 @@ Instantiation runs `_initialize` (package initializers), never `main`. memory. The HUD pointer is valid until the next guest call; the pixel buffer pointer must stay valid for the instance's lifetime. - The host calls exports one at a time from one goroutine; no reentrancy. +- **`termcade_init` comes first.** It is what constructs the game, so every + other export except `termcade_abi` traps until it has run — a guest written + in Go dereferences a nil game and takes the instance down. The arcade calls + `termcade_abi`, then `termcade_init`, then `termcade_playfield`, and any + other host must do the same. - Per-call budgets: 5s for setup calls, 500ms per frame call. A call that exceeds its budget kills the instance.