mirror of
https://github.com/isledecomp/isle.git
synced 2025-10-26 09:54:18 +00:00
Match vtables with virtual inheritance (#717)
* Match vtables with virtual inheritance * Simplify vtable name check * Thunk alert
This commit is contained in:
@@ -4,6 +4,7 @@ from isledecomp.cvdump.demangler import (
|
||||
demangle_vtable,
|
||||
parse_encoded_number,
|
||||
InvalidEncodedNumberError,
|
||||
get_vtordisp_name,
|
||||
)
|
||||
|
||||
string_demangle_cases = [
|
||||
@@ -46,13 +47,37 @@ def test_invalid_encoded_number():
|
||||
|
||||
|
||||
vtable_cases = [
|
||||
("??_7LegoCarBuildAnimPresenter@@6B@", "LegoCarBuildAnimPresenter"),
|
||||
("??_7?$MxCollection@PAVLegoWorld@@@@6B@", "MxCollection<LegoWorld *>"),
|
||||
("??_7?$MxPtrList@VLegoPathController@@@@6B@", "MxPtrList<LegoPathController>"),
|
||||
("??_7Renderer@Tgl@@6B@", "Tgl::Renderer"),
|
||||
("??_7LegoCarBuildAnimPresenter@@6B@", "LegoCarBuildAnimPresenter::`vftable'"),
|
||||
("??_7?$MxCollection@PAVLegoWorld@@@@6B@", "MxCollection<LegoWorld *>::`vftable'"),
|
||||
(
|
||||
"??_7?$MxPtrList@VLegoPathController@@@@6B@",
|
||||
"MxPtrList<LegoPathController>::`vftable'",
|
||||
),
|
||||
("??_7Renderer@Tgl@@6B@", "Tgl::Renderer::`vftable'"),
|
||||
("??_7LegoExtraActor@@6B0@@", "LegoExtraActor::`vftable'{for `LegoExtraActor'}"),
|
||||
(
|
||||
"??_7LegoExtraActor@@6BLegoAnimActor@@@",
|
||||
"LegoExtraActor::`vftable'{for `LegoAnimActor'}",
|
||||
),
|
||||
(
|
||||
"??_7LegoAnimActor@@6B?$LegoContainer@PAM@@@",
|
||||
"LegoAnimActor::`vftable'{for `LegoContainer<float *>'}",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("symbol, class_name", vtable_cases)
|
||||
def test_vtable(symbol, class_name):
|
||||
assert demangle_vtable(symbol) == class_name
|
||||
|
||||
|
||||
def test_vtordisp():
|
||||
"""Make sure we can accurately detect an adjuster thunk symbol"""
|
||||
assert get_vtordisp_name("") is None
|
||||
assert get_vtordisp_name("?ClassName@LegoExtraActor@@UBEPBDXZ") is None
|
||||
assert (
|
||||
get_vtordisp_name("?ClassName@LegoExtraActor@@$4PPPPPPPM@A@BEPBDXZ") is not None
|
||||
)
|
||||
|
||||
# A function called vtordisp
|
||||
assert get_vtordisp_name("?vtordisp@LegoExtraActor@@UBEPBDXZ") is None
|
||||
|
||||
@@ -711,3 +711,46 @@ def test_header_function_declaration(parser):
|
||||
|
||||
assert len(parser.alerts) == 1
|
||||
assert parser.alerts[0].code == ParserError.NO_IMPLEMENTATION
|
||||
|
||||
|
||||
def test_extra(parser):
|
||||
"""Allow a fourth field in the decomp annotation. Its use will vary
|
||||
depending on the marker type. Currently this is only used to identify
|
||||
a vtable with virtual inheritance."""
|
||||
|
||||
# Intentionally using non-vtable markers here.
|
||||
# We might want to emit a parser warning for unnecessary extra info.
|
||||
parser.read_lines(
|
||||
[
|
||||
"// GLOBAL: TEST 0x5555 Haha",
|
||||
"int g_variable = 0;",
|
||||
"// FUNCTION: TEST 0x1234 Something",
|
||||
"void Test() { g_variable++; }",
|
||||
"// LIBRARY: TEST 0x8080 Printf",
|
||||
"// _printf",
|
||||
]
|
||||
)
|
||||
|
||||
# We don't use this information (yet) but this is all fine.
|
||||
assert len(parser.alerts) == 0
|
||||
|
||||
|
||||
def test_virtual_inheritance(parser):
|
||||
"""Indicate the base class for a vtable where the class uses
|
||||
virtual inheritance."""
|
||||
parser.read_lines(
|
||||
[
|
||||
"// VTABLE: HELLO 0x1234",
|
||||
"// VTABLE: HELLO 0x1238 Greetings",
|
||||
"// VTABLE: HELLO 0x123c Howdy",
|
||||
"class HiThere : public virtual Greetings {",
|
||||
"};",
|
||||
]
|
||||
)
|
||||
|
||||
assert len(parser.alerts) == 0
|
||||
assert len(parser.vtables) == 3
|
||||
assert parser.vtables[0].base_class is None
|
||||
assert parser.vtables[1].base_class == "Greetings"
|
||||
assert parser.vtables[2].base_class == "Howdy"
|
||||
assert all(v.name == "HiThere" for v in parser.vtables)
|
||||
|
||||
@@ -65,6 +65,14 @@ marker_samples = [
|
||||
# TODO: These match but shouldn't.
|
||||
# (False, False, '// FUNCTION: LEGO1 0'),
|
||||
# (False, False, '// FUNCTION: LEGO1 0x'),
|
||||
# Extra field
|
||||
(True, True, "// VTABLE: HELLO 0x1234 Extra"),
|
||||
# Extra with spaces
|
||||
(True, True, "// VTABLE: HELLO 0x1234 Whatever<SubClass *>"),
|
||||
# Extra, no space (if the first non-hex character is not in [a-f])
|
||||
(True, False, "// VTABLE: HELLO 0x1234Hello"),
|
||||
# Extra, many spaces
|
||||
(True, False, "// VTABLE: HELLO 0x1234 Hello"),
|
||||
]
|
||||
|
||||
|
||||
@@ -174,3 +182,27 @@ string_match_cases = [
|
||||
@pytest.mark.parametrize("line, string", string_match_cases)
|
||||
def test_get_string_contents(line: str, string: str):
|
||||
assert get_string_contents(line) == string
|
||||
|
||||
|
||||
def test_marker_extra_spaces():
|
||||
"""The extra field can contain spaces"""
|
||||
marker = match_marker("// VTABLE: TEST 0x1234 S p a c e s")
|
||||
assert marker.extra == "S p a c e s"
|
||||
|
||||
# Trailing spaces removed
|
||||
marker = match_marker("// VTABLE: TEST 0x8888 spaces ")
|
||||
assert marker.extra == "spaces"
|
||||
|
||||
# Trailing newline removed if present
|
||||
marker = match_marker("// VTABLE: TEST 0x5555 newline\n")
|
||||
assert marker.extra == "newline"
|
||||
|
||||
|
||||
def test_marker_trailing_spaces():
|
||||
"""Should ignore trailing spaces. (Invalid extra field)
|
||||
Offset field not truncated, extra field set to None."""
|
||||
|
||||
marker = match_marker("// VTABLE: TEST 0x1234 ")
|
||||
assert marker is not None
|
||||
assert marker.offset == 0x1234
|
||||
assert marker.extra is None
|
||||
|
||||
Reference in New Issue
Block a user