Lua 5.0 behaviours of the client that an addon can observe, which benilla-lua's 5.1 base got differently:
- string.sub and string.find keep a position's low 32 bits (`0x7fbd55`).
- An escaped byte at or above 0x80 matches nothing (`0x7fcbc6`).
- `error()` checks its argument (`0x702993`) and adds the position at any level but 0 (`0x7029a8`).
- `__unm` gets the operand and nil (`0x6f8edc`).
- `__index` chains reach 102 tables and `__newindex` 101.
- math.deg multiplies by 180/pi (`0x7fb14d`).
The bare aliases run as their own `compat.lua` chunk (`0x703b21`), each on the client's line. Its closures keep a local `math`, so their error lines match and replacing the global `math` leaves them working.
`seterrorhandler` keeps the top value, and nil clears it (`0x702930`).
Each fix has a test.
Fixes#2632Fixes#2633Fixes#2634Fixes#2635Fixes#2636Fixes#2637Fixes#2638Fixes#2639Fixes#2640Fixes#2654🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ten differences from 1.12.1 in the UI core.
- StatusBar range and value follow `0x783270`/`0x7832d0` (a reversed pair collapses, a value before a range is dropped); the health watchers set both each update; the fill draws once both are set, its coordinates built from scratch (`0x7833c0`).
- A full collection runs before PLAYER_LOGIN (`0x490941`); the addon harness runs the client's world-entry cascade.
- Every index argument raises its own usage line (`0x6f34d0`, then the binding's literal).
- Strata, layer and frame-type names match whole (`0x6ee32c`); stack lines name unnamed functions by global (`0x6fbf90`).
- SetCursor takes a custom file (`0x523c40`); EditBox accepts OnInputLanguageChanged (`0x77a40d`).
- debugprofilestop answers milliseconds (a Deviation: multi-core 1.12.1 returns cycles).
A byte audit re-derived every address.
Fixes#1178Fixes#1915Fixes#1917Fixes#1908Fixes#1839Fixes#1869Fixes#1170Fixes#1105Fixes#1186Fixes#1952🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
benilla-lua's tables and globals now follow the client's Lua 5.0, not 5.1.
Tables:
- They size, fill and rehash as 5.0's ltable.c does: the hash part doubles (`0x6fab90`), and a log size of 0 is the shared dummy (`0x6fa601`).
- A hash part past 2^24 nodes raises `table overflow`.
- Constructors store every 32 items.
- A weak table's clearing removes the key, so `next` from it raises (`0x6f79d0`).
- A weak-keyed table keeps its string keys (`0x6f79f0`).
Globals and libraries:
- `ipairs` returns itself, and a second argument steps once.
- `setglobal` assigns the top of the stack.
- The state opens in the client's order, with no `_G`.
Each new test fails without its fix.
Fixes#1487Fixes#1789Fixes#1790Fixes#1169🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lua memory now counts as the client's does:
- Every object is charged at its 32-bit Lua 5.0 size: a string is 17 bytes plus its length (`0x6f9da1`), a table header 32 (`0x6fa500`), a node 40, and a closure 36 plus 4 per upvalue (`0x6f9ebf`).
- Prototypes count 5.0's instructions and constants (`0x6fcb2b`).
- The string table and the concat buffer are counted as 5.0 sizes them.
The stock UI reads 10,618 KB, against 13,294 KB before.
The host's old C-host shim overrode benilla-lua's natives. It is gone:
- `collectgarbage` now moves the real collector, taking `__ftol`'s low word (`0x6f4400`);
- `gcinfo` reports that collector;
- `^` is the client's `math_pow` (`0x7fb4bd`);
- `loadstring` returns one value (`0x7032ba`).
An audit re-derived every size from the bytes. Each fix fails its test without it.
Fixes#1779Fixes#1780🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
benilla-lua's lexer and compiler now behave as the client's 5.0:
- A control byte is "invalid control char" (`0x6ffabb`).
- Every chunk lexes as source, binary ones too (`0x6f6a15`).
- Lines end at `\n` alone (`0x6ffb94`); a dot is never a NUL (`0x6ff98a`).
- A `while` condition over 100 instructions is too complex (`0x6fd03b`), counted as 5.0's code generator counts it.
- Upvalues get no name in errors (`0x6fc5e0`).
- `string.dump` refuses closures with upvalues, and dumps the stack top (`0x6f438a`, `0x6f4373`).
The client installs no Lua hook (`lua_sethook` has no caller), so the load's runaway guard has no client mechanism to follow. It is now an abort no `pcall` can catch, recorded as a deviation.
An audit re-derived every commit from the bytes. Each fix fails its test without it.
Fixes#1755Fixes#1756Fixes#1757Fixes#1758Fixes#1759Fixes#1767Fixes#1434Fixes#1445Fixes#1777Fixes#1778🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
benilla-lua now matches the client's Lua 5.0 wherever an addon can see the difference:
- Number keys hash as `hashnum` does (`0x6fa260`), so `pairs` walks them in the client's order.
- Calls nest at most 4096 deep (`0x6f61c0`).
- `^` calls the global `__pow` (`0x6f9b5e`).
- The parser's limits and messages are the client's (`0x6ff440`); the client has no assignment limit.
- A numeric for's variable is its counter (`0x6fe380`, `0x6f9262`).
- A generic for uses `forlist`'s layout (`0x6fe540`): the first name is the control, and closures share the variables.
- Only calls name a callee (`0x6fc499`), and a stray token reports `<eof> expected` (`0x871cf4`).
Each fix has a test that fails without it. Two audits re-derived every address from the bytes. The bench is faster or level.
Fixes#1369Fixes#1370Fixes#1371Fixes#1372Fixes#1374Fixes#1377Fixes#1752Fixes#1753Fixes#1754🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>