Method dispatch axis: map id -> body via Runner::run switch
Recovers how a script method id maps to its implementation, the foundation for body-level normalisation. Each CMC_*_Runner::run is a switch(id) (vtable slot 17); every case is the method body — inline (MSVC6) or a tail-call to a separate show()/load() (MSVC8). The extractor parses the jump table at the disassembly level (Ghidra's decompiler jump-table recovery silently dropped the big runners), fingerprints each case by its ordered CALL anchors (Class::method / vtbl+0xNN), and expands thin wrappers one level so MSVC8 lines up with MSVC6. Validated on the golden pair: Animo SHOW..RESUME (id 1-4) yield identical leaves (getAnimo + vtbl+0xa0/0xa4/0x4c/0x50) across both compilers. Coverage 30/32 runners; Piklib 475 / BlooMoo 619 dispatch rows. - extract_engine_surface.py: extract_method_dispatch (schema_version -> 4) - snapshots regenerated with the method_dispatch axis - ams: Snapshot.method_dispatch; diff axis keyed (owner,id) on [impl,calls] with method-name join; render METHOD BODIES section; cli --only dispatch; owner filter - UI: "Ciała metod" diff axis + browse tab - tests: body-change unit + cross-compiler vtbl assertion -> 29/29 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -72,6 +72,26 @@ def test_field_type_change_and_owner_filter():
|
||||
assert d["fields"]["added"] == [] and d["fields"]["removed"] == []
|
||||
|
||||
|
||||
def test_method_dispatch_body_change():
|
||||
old = _snap(
|
||||
methods=[{"owner": "CMC_Animo", "name": "SHOW", "id": 1}],
|
||||
method_dispatch=[{"owner": "CMC_Animo", "id": 1, "impl": None,
|
||||
"impl_addr": "0x1", "calls": ["CMC_Animo::getAnimo", "vtbl+0xa0"]}],
|
||||
)
|
||||
new = _snap(
|
||||
methods=[{"owner": "CMC_Animo", "name": "SHOW", "id": 1}],
|
||||
method_dispatch=[{"owner": "CMC_Animo", "id": 1, "impl": None,
|
||||
"impl_addr": "0x1", "calls": ["CMC_Animo::getAnimo", "vtbl+0xa4"]}],
|
||||
)
|
||||
d = compute_diff(old, new)["method_dispatch"]
|
||||
assert len(d["changed"]) == 1
|
||||
ch = d["changed"][0]
|
||||
assert ch["item"]["name"] == "SHOW" # name joined from the methods axis on (owner, id)
|
||||
assert ch["changes"]["calls"] == [["CMC_Animo::getAnimo", "vtbl+0xa0"],
|
||||
["CMC_Animo::getAnimo", "vtbl+0xa4"]]
|
||||
assert "METHOD BODIES" in render_text(compute_diff(old, new))
|
||||
|
||||
|
||||
def test_render_no_diff():
|
||||
out = render_text(compute_diff(_snap(), _snap()))
|
||||
assert "(no differences)" in out
|
||||
@@ -101,3 +121,10 @@ def test_golden_pair_piklib_to_bloomoo():
|
||||
# rendering must not raise and must mention the new types
|
||||
text = render_text(d)
|
||||
assert "GRBUFFER" in text and "MOUSE" in text
|
||||
|
||||
# method bodies recovered cross-compiler: Animo SHOW (id 1) maps to the same vtable leaf
|
||||
# despite MSVC6 inlining it and MSVC8 keeping it as a separate show() function
|
||||
disp_old = {(r["owner"], r["id"]): r for r in old.method_dispatch}
|
||||
disp_new = {(r["owner"], r["id"]): r for r in new.method_dispatch}
|
||||
assert disp_old[("CMC_Animo", 1)]["calls"][-1] == "vtbl+0xa0"
|
||||
assert disp_new[("CMC_Animo", 1)]["calls"][-1] == "vtbl+0xa0"
|
||||
|
||||
Reference in New Issue
Block a user