idle sits you down and camps you, and the addons anchor, parse and draw

- five idle minutes sit you down and flag you AFK, thirty log you out through the ordinary countdown; holding a key is idle, moving the mouse is not, and a swimmer stays afloat (2092)
- a knockdown no longer wedges: the landing froze the clip that held the base-animation lock, leaving a knocked-down player on their back for the session (2098); the perf cost pill starts hidden (2099)
- Lua is exactly 1.12's 5.0 — no vararg expression, no #, no %, the remembered table size — so Cartographer loads without errors and Mik's Scrolling Battle Text shows its text, in its own shipped font now that addon TTFs load (2101, 2102, 2103)
- a widget name is a Lua global for SetPoint and its kin, so Bartender2's bars spread out; a button's NormalText disowns its justify, the raise renumbers hidden frames too, and a missing toc file warns instead of erroring (2105, 2100, 2104, 2107, 2106)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-09-08 16:35:03 +07:00
co-authored by Claude Fable 5.1
parent 1783660496
commit 99b5600e25
52 changed files with 3713 additions and 359 deletions
+28 -4
View File
@@ -5,6 +5,12 @@ the version mlua 0.11 resolves to. Wired in through `[patch.crates-io]` in the w
## Why a fork exists at all
**benilla's Lua has to accept exactly the grammar the 1.12.1 client's Lua accepts — no more and no
less.** Every hunk below is that one rule: three restore 5.0 constructs 5.1 deleted, three delete
5.1 constructs 5.0 never had. A grammar difference in either direction is invisible to every
instrument we own until an addon trips over it, and the corpus *asks about* the difference
directly (decisions 1208, 2101).
The 1.12 addon corpus is Lua **5.0** code. It uses the iterator-less generic-for:
```lua
@@ -35,23 +41,41 @@ downgrade, and it is the entire reason this is a one-hunk fork rather than a ven
## The delta, and the command that proves it
**Current state: three hunks, in three files.** `src/lib.rs` additionally differs by having Lua
**Current state: six hunks, in three files.** `src/lib.rs` additionally differs by having Lua
5.2/5.3/5.4/5.5 stripped from the `Version` enum (with their source trees deleted) — benilla builds
5.1 and only 5.1, and a fork that still *offered* the other four would answer a request for one with
a missing directory at build time instead of a compile error.
**Three hunks restore what 5.1 removed:**
| file | hunk | why | record |
|---|---|---|---|
| `lvm.c` | 5.0's `OP_TFORPREP` table→`next` substitution, folded into the top of `OP_TFORLOOP` | `for k, v in someTable do` raised "attempt to call a table value"; it was the first session-start error for **60** corpus addons | 1215 |
| `luaconf.h` | `LUA_COMPAT_LSTR` `1` → `2` | 5.1 kept 5.0's nesting machinery and put an advisory error in front of it; two corpus addons died on "nesting of `[[...]]` is deprecated" | — |
| `lparser.c` | 5.0's compat-semicolon skip restored at the top of `constructor()`'s field loop — 5.0's own line, verbatim, comment included | one extra `;` after a field separator inside a table constructor (`Back_Title = AL["Factions"];;`, ×20 in AtlasLoot's `ButtonRegistry.lua`) parses on the client and died here with "unexpected symbol near `;`"; statement-level `;;` stays rejected by both dialects | 1315 |
**Three delete what 5.1 added** — all in `lparser.c`, all byte-read out of the client's own parser
(`simpleexp 0x6fd240`, `getunopr 0x6fe0a0`, `getbinopr 0x6fe0c0`), all landing on 5.0's own
`unexpected symbol` at `prefixexp 0x6fde40`:
| hunk | why | record |
|---|---|---|
| `simpleexp`'s `case TK_DOTS` deleted — `...` is no longer an expression | the Ace2 corpus **asks** which interpreter it is on by compiling `return function(...) return ... end`; 170 `loadstring` sites in the 219-addon corpus are that question and 92 library files gate on it. Answering `true` put Cartographer on its client-2.0 branch, hooking `CloseSpecialWindows` — a name 1.12 does not have. Also restores 5.0's rule that **every** vararg function gets its `arg` table | 1208, 2101 |
| `getunopr`'s `case '#'` deleted — no length operator | the client's `getunopr` tests exactly two tokens (`-`, `not`) and its `OPR_NOUNOPR` is **2**: a three-member enum, 5.0's. 5.0 asks a table with `table.getn` and a string with `string.len` | 2101 |
| `getbinopr`'s `case '%'` deleted — no modulo operator | the client's `getbinopr` switch is based at `'*'` (0x2A), so `%` (0x25) is below its range and reaches `OPR_NOBINOPR` = **14**: a fifteen-member `BinOpr`, 5.0's. 5.0 spells it `math.mod` | 2101 |
The ordering was deliberate and it held: the fork landed **inert**, byte-identical to upstream, so
the build pipeline was proven transparent *before* any behavioural payload went into it — and when
the suite then moved, the patch was the only thing that could have moved it. Both hunks are measured
against the 218-addon corpus rather than argued: the long-string one alone took loaded 134 → 139 and
the suite then moved, the patch was the only thing that could have moved it. Every hunk is measured
against the corpus rather than argued: the long-string one alone took loaded 134 → 139 and
survivors 99 → 104.
**The deletions are safe because nothing the reference runs uses those constructs, and that was
scanned rather than assumed** (2101 §4): a comment/string-stripping scan of the extracted 1.12
FrameXML (177 files), GlueXML, Blizzard's own addons and the director's installed AddOns folder
finds **zero** sites of all three. Our own engine Lua did use them and was rewritten into the 5.0
spellings; the rule now is that no chunk of ours may use a construct the client's parser rejects.
To verify the Lua sources against upstream at any time:
```sh
@@ -60,7 +84,7 @@ diff -r third_party/lua-src/lua-5.1.5 \
~/.cargo/registry/src/*/lua-src-550.0.0/lua-5.1.5
```
That must print **exactly** the three hunks in the table above and nothing else. If it ever prints
That must print **exactly** the six hunks in the tables above and nothing else. If it ever prints
more, something crept in.
## Status of the payload
+55 -12
View File
@@ -742,8 +742,8 @@ static void primaryexp (LexState *ls, expdesc *v) {
static void simpleexp (LexState *ls, expdesc *v) {
/* simpleexp -> NUMBER | STRING | NIL | true | false | ... |
constructor | FUNCTION body | primaryexp */
/* simpleexp -> NUMBER | STRING | NIL | true | false | constructor |
FUNCTION body | primaryexp */
switch (ls->t.token) {
case TK_NUMBER: {
init_exp(v, VKNUM, 0);
@@ -766,14 +766,39 @@ static void simpleexp (LexState *ls, expdesc *v) {
init_exp(v, VFALSE, 0);
break;
}
case TK_DOTS: { /* vararg */
FuncState *fs = ls->fs;
check_condition(ls, fs->f->is_vararg,
"cannot use " LUA_QL("...") " outside a vararg function");
fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));
break;
}
/* BENILLA: 5.1's vararg EXPRESSION arm, deleted (decision 2101; see
** third_party/lua-src/BENILLA.md). Lua 5.0 has no grammar for `...' as a
** value -- a vararg function reads its extra arguments out of the implicit
** `arg' table -- and `...' as an expression is the construct the Ace2 corpus
** uses to ASK which interpreter it is running:
**
** local lua51 = loadstring("return function(...) return ... end") and true or false
**
** 170 loadstring sites across the 219-addon corpus are that question, and 92
** library files in 24 folders gate on it (decision 1208). Answering it wrong
** makes every one of them take a client-2.0 branch on a 1.12 client -- which
** is how Cartographer came to hook `CloseSpecialWindows', a name 1.12 does
** not have.
**
** Byte-verified in the client itself, not inferred from stock 5.0. simpleexp
** is 0x6fd240; it switches on `token - 0x7B' bounded at 0xA3, indexes the
** byte table at 0x6fd338 and jumps through the 8-entry table at 0x6fd318.
** TK_DOTS is 0x118 (TK_NAME is 0x116 -- wow-5875-re system/ui/scratch/
** lua-dialect.md 9), so its index byte is 0x9D, whose value is 7: the DEFAULT
** arm, 0x6fd30a, which tail-jumps to primaryexp 0x6fdb00. The seven real arms
** are `{', TK_FALSE, TK_FUNCTION, TK_NIL, TK_TRUE, TK_NUMBER and TK_STRING --
** stock Lua 5.0's simpleexp exactly, with no vararg case.
**
** So `...' as a value reaches prefixexp 0x6fde40, whose head accepts only `('
** or TK_NAME and otherwise raises "unexpected symbol" (0x871e80) -- the same
** reject site lua-dialect.md 9 documents for a stray `;'. Deleting the arm
** reproduces that error here, verbatim.
**
** It also restores 5.0's `arg' rule for free: the deleted line cleared
** VARARG_NEEDSARG for any function that mentioned `...', so `arg' was not
** built for it. With the arm gone, NEEDSARG is never cleared and EVERY vararg
** function gets its `arg' table -- which is what 5.0 does and what all 177
** files of the shipped 1.12 FrameXML read. */
case '{': { /* constructor */
constructor(ls, v);
return;
@@ -796,7 +821,15 @@ static UnOpr getunopr (int op) {
switch (op) {
case TK_NOT: return OPR_NOT;
case '-': return OPR_MINUS;
case '#': return OPR_LEN;
/* BENILLA: 5.1's `#' length operator, deleted (decision 2101). 5.0 asks a
** table for its size with table.getn/getn and a string with string.len; the
** operator does not exist and neither does its __len metamethod.
** Byte-verified: getunopr is 0x6fe0a0, a leaf with exactly TWO token tests
** -- `cmp ecx,0x2D' (`-') then `cmp ecx,0x10E' (TK_NOT), then setne/inc -- so
** its OPR_NOUNOPR is 2, i.e. the enum has three members, not 5.1's four. The
** metamethod-name pool at 0x871896 agrees: no __len (wow-5875-re
** system/ui/scratch/lua-dialect.md 1, discriminator 4). A `#' therefore
** reaches simpleexp's default arm and raises "unexpected symbol near `#'". */
default: return OPR_NOUNOPR;
}
}
@@ -808,7 +841,17 @@ static BinOpr getbinopr (int op) {
case '-': return OPR_SUB;
case '*': return OPR_MUL;
case '/': return OPR_DIV;
case '%': return OPR_MOD;
/* BENILLA: 5.1's `%' modulo operator, deleted (decision 2101). 5.0 spells it
** math.mod (and the bare global `mod'), which this VM already publishes.
** Byte-verified: getbinopr is 0x6fe0c0 and its switch is based at `ecx-0x2A'
** -- `*', the LOWEST token it handles. `%' is 0x25, below the base, so it is
** outside the range test (`cmp eax,0xF2; ja') and falls to the default,
** 0x6fe129, `mov eax,0xE' = OPR_NOBINOPR 14 -- a 15-member BinOpr, 5.0's, not
** 5.1's 16-member one with OPR_MOD. Had 5.1's `%' case been present the
** switch would have been based at 0x25. The metamethod pool has no __mod
** either (wow-5875-re system/ui/scratch/lua-dialect.md 1, discriminator 4).
** priority[] is indexed by the BinOpr enum, which is untouched -- only the
** token that reaches OPR_MOD is gone. */
case '^': return OPR_POW;
case TK_CONCAT: return OPR_CONCAT;
case TK_NE: return OPR_NE;