mirror of
https://github.com/isledecomp/isle.git
synced 2026-01-24 00:31:16 +00:00
Get vtable class name
This commit is contained in:
parent
9f1302e8d8
commit
cbeb4168e0
@ -7,6 +7,7 @@
|
|||||||
is_blank_or_comment,
|
is_blank_or_comment,
|
||||||
match_marker,
|
match_marker,
|
||||||
is_marker_exact,
|
is_marker_exact,
|
||||||
|
get_class_name,
|
||||||
get_synthetic_name,
|
get_synthetic_name,
|
||||||
remove_trailing_comment,
|
remove_trailing_comment,
|
||||||
)
|
)
|
||||||
@ -195,14 +196,18 @@ def _vtable_marker(self, marker: DecompMarker):
|
|||||||
self._syntax_warning(ParserError.DUPLICATE_MODULE)
|
self._syntax_warning(ParserError.DUPLICATE_MODULE)
|
||||||
self.state = ReaderState.IN_VTABLE
|
self.state = ReaderState.IN_VTABLE
|
||||||
|
|
||||||
def _vtable_done(self):
|
def _vtable_done(self, class_name: str = None):
|
||||||
|
if class_name is None:
|
||||||
|
# Best we can do
|
||||||
|
class_name = self.last_line.strip()
|
||||||
|
|
||||||
for marker in self.tbl_markers.iter():
|
for marker in self.tbl_markers.iter():
|
||||||
self.vtables.append(
|
self.vtables.append(
|
||||||
ParserVtable(
|
ParserVtable(
|
||||||
line_number=self.line_number,
|
line_number=self.line_number,
|
||||||
module=marker.module,
|
module=marker.module,
|
||||||
offset=marker.offset,
|
offset=marker.offset,
|
||||||
class_name=self.last_line.strip(),
|
class_name=class_name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -380,8 +385,9 @@ def read_line(self, line: str):
|
|||||||
self._variable_done()
|
self._variable_done()
|
||||||
|
|
||||||
elif self.state == ReaderState.IN_VTABLE:
|
elif self.state == ReaderState.IN_VTABLE:
|
||||||
if not is_blank_or_comment(line):
|
vtable_class = get_class_name(line)
|
||||||
self._vtable_done()
|
if vtable_class is not None:
|
||||||
|
self._vtable_done(class_name=vtable_class)
|
||||||
|
|
||||||
def read_lines(self, lines: Iterable):
|
def read_lines(self, lines: Iterable):
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
|||||||
@ -63,3 +63,33 @@ def match_marker(line: str) -> DecompMarker | None:
|
|||||||
|
|
||||||
def is_marker_exact(line: str) -> bool:
|
def is_marker_exact(line: str) -> bool:
|
||||||
return markerExactRegex.match(line) is not None
|
return markerExactRegex.match(line) is not None
|
||||||
|
|
||||||
|
|
||||||
|
template_class_decl_regex = re.compile(
|
||||||
|
r"\s*(?:\/\/)?\s*class (\w+)<([\w]+)\s*(\*+)?\s*>"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class_decl_regex = re.compile(r"\s*(?:\/\/)?\s*class (\w+)")
|
||||||
|
|
||||||
|
|
||||||
|
def get_class_name(line: str) -> str | None:
|
||||||
|
"""For VTABLE markers, extract the class name from the code line or comment
|
||||||
|
where it appears."""
|
||||||
|
|
||||||
|
match = template_class_decl_regex.match(line)
|
||||||
|
if match is not None:
|
||||||
|
# For template classes, we should reformat the class name so it matches
|
||||||
|
# the output from cvdump: one space between the template type and any asterisks
|
||||||
|
# if it is a pointer type.
|
||||||
|
(class_name, template_type, asterisks) = match.groups()
|
||||||
|
if asterisks is not None:
|
||||||
|
return f"{class_name}<{template_type} {asterisks}>"
|
||||||
|
|
||||||
|
return f"{class_name}<{template_type}>"
|
||||||
|
|
||||||
|
match = class_decl_regex.match(line)
|
||||||
|
if match is not None:
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|||||||
@ -193,6 +193,7 @@ def test_multiple_vtables(parser):
|
|||||||
)
|
)
|
||||||
assert len(parser.alerts) == 0
|
assert len(parser.alerts) == 0
|
||||||
assert len(parser.vtables) == 2
|
assert len(parser.vtables) == 2
|
||||||
|
assert parser.vtables[0].class_name == "MxString"
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_vtables_same_module(parser):
|
def test_multiple_vtables_same_module(parser):
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
is_blank_or_comment,
|
is_blank_or_comment,
|
||||||
match_marker,
|
match_marker,
|
||||||
is_marker_exact,
|
is_marker_exact,
|
||||||
|
get_class_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -96,3 +97,36 @@ def test_marker_dict_type_replace():
|
|||||||
markers = list(d.iter())
|
markers = list(d.iter())
|
||||||
assert len(markers) == 1
|
assert len(markers) == 1
|
||||||
assert markers[0].type == "FUNCTION"
|
assert markers[0].type == "FUNCTION"
|
||||||
|
|
||||||
|
|
||||||
|
class_name_match_cases = [
|
||||||
|
("class MxString {", "MxString"),
|
||||||
|
("// class MxString", "MxString"),
|
||||||
|
("class MxString : public MxCore {", "MxString"),
|
||||||
|
("class MxPtrList<MxPresenter>", "MxPtrList<MxPresenter>"),
|
||||||
|
# If it is possible to match the symbol MxList<LegoPathController *>::`vftable'
|
||||||
|
# we should get the correct class name if possible. If the template type is a pointer,
|
||||||
|
# the asterisk and class name are separated by one space.
|
||||||
|
("// class MxList<LegoPathController *>", "MxList<LegoPathController *>"),
|
||||||
|
("// class MxList<LegoPathController*>", "MxList<LegoPathController *>"),
|
||||||
|
("// class MxList<LegoPathController* >", "MxList<LegoPathController *>"),
|
||||||
|
# I don't know if this would ever come up, but sure, why not?
|
||||||
|
("// class MxList<LegoPathController**>", "MxList<LegoPathController **>"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("line, class_name", class_name_match_cases)
|
||||||
|
def test_get_class_name(line: str, class_name: str):
|
||||||
|
assert get_class_name(line) == class_name
|
||||||
|
|
||||||
|
|
||||||
|
class_name_no_match_cases = [
|
||||||
|
"MxString { ",
|
||||||
|
"clas MxString",
|
||||||
|
"// MxPtrList<MxPresenter>::`scalar deleting destructor'",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("line", class_name_no_match_cases)
|
||||||
|
def test_get_class_name_none(line: str):
|
||||||
|
assert get_class_name(line) is None
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user