our API surface is diffed against the client's 19,572 globals
- `--surface` re-runs the comparison against the captured in-world global table on demand, and `--surface-dump` writes our raw side for any tool to join — a one-shot measurement stops drifting - CreateFrame with an unresolvable template raises exactly as the client does, instead of handing back a bare frame; `inherits=` is one name, matched case-insensitively - CreateFontString and CreateTexture honour their third argument — 49 corpus call sites were passing an inherit into the void - the addon-message inbound byte map is corrected (a test had agreed with the wrong constants), and the group-loot roll template publishes under the reference's own name Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -350,8 +350,12 @@ fn kind_method_registries(lua: &Lua, this: &Table) -> &'static [&'static str] {
|
||||
/// `OnLoad` must be able to see the frame's real name, and `$parent` inside the template must
|
||||
/// resolve against **the caller's** name, not the template's.
|
||||
///
|
||||
/// An unusable template (unknown, non-virtual, the wrong shape) is a warning on the model and a
|
||||
/// perfectly usable frame — never an error that takes the frame with it.
|
||||
/// An **unresolvable** template name raises `CreateFrame(): Couldn't find inherited node "%s"` and
|
||||
/// creates nothing — the reference's own behaviour, byte-verified (`0x7061dd` → `luaL_error`, which
|
||||
/// never returns). A template that resolves but is *unusable in some other way* — declared without
|
||||
/// `virtual="true"`, or of a shape that does not fit the kind asked for — is still a warning plus a
|
||||
/// working frame, because the registry lookup itself succeeded and that is the only thing the miss
|
||||
/// branch tests.
|
||||
fn create_frame(
|
||||
lua: &Lua,
|
||||
(kind, name, parent, inherits): (String, Option<Value>, Option<Value>, Option<Value>),
|
||||
@@ -392,6 +396,39 @@ fn create_frame(
|
||||
}
|
||||
}
|
||||
|
||||
// ── THE TEMPLATE LOOKUP HAPPENS HERE — before anything is constructed or published ──────────
|
||||
//
|
||||
// `0x7061dd` calls the registry lookup `0x6ee6f0` and, on NULL, falls into
|
||||
// `luaL_error(L, "CreateFrame(): Couldn't find inherited node \"%s\"")` at `0x7061ed`. That
|
||||
// never returns — `luaG_errormsg`/`luaD_throw` contain no `ret` at all and end in `longjmp`, so
|
||||
// the `xor eax,eax; ret` after the call is dead code. The frame is **not created**, no global
|
||||
// is published, and the calling statement is abandoned; the error is ordinary Lua propagation
|
||||
// and `pcall`-catchable, which is how the widget dispatcher (`0x704f10`, every handler under
|
||||
// `lua_pcall`) turns it into one dead handler rather than a dead client.
|
||||
//
|
||||
// We used to warn and hand back a usable bare frame, and the doc above said the reference
|
||||
// "creates the frame anyway". That claim is refuted at the bytes. It is the same class as 1249:
|
||||
// a failure the client reports and we swallowed — and swallowing is the worse half, because the
|
||||
// addon then runs on a frame with none of the art, regions or scripts it asked for and fails
|
||||
// later, somewhere unrelated.
|
||||
//
|
||||
// Ordering is the carved part, not a detail: the miss branch precedes the synthetic-node build
|
||||
// (`0x706208`) and the `name` store (`0x70622d`), so a miss leaves no partial widget and no
|
||||
// orphan global behind. Doing this after construction would leave both.
|
||||
if let Some(t) = template.as_deref().filter(|t| !t.is_empty()) {
|
||||
let known = {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
let templates = model.framexml_templates.borrow();
|
||||
// One name, verbatim, folded — the law `framexml::expand` resolves under.
|
||||
templates.contains_key(t) || templates.keys().any(|k| k.eq_ignore_ascii_case(t))
|
||||
};
|
||||
if !known {
|
||||
return Err(mlua::Error::runtime(format!(
|
||||
"CreateFrame(): Couldn't find inherited node \"{t}\""
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Create in the arena, mint the id, seed a default layout input. All under one write borrow.
|
||||
let id = {
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
|
||||
Reference in New Issue
Block a user