fix(import-audit): parse export aliases as C++ tokens (#4196)

Co-authored-by: lorkydey <lorky.contact@gmail.com>
This commit is contained in:
Lorky 2026-10-12 09:30:54 +02:00 committed by GitHub
parent 3048da7c04
commit 44e9050a58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 51 additions and 4 deletions

View File

@ -14,7 +14,6 @@ import progress
ROOT = Path(__file__).resolve().parent.parent
PRX = ROOT / "core" / "libs" / "prx"
NID = re.compile(r"[A-Za-z0-9+\-]{11}")
EXPORT_ALIAS = re.compile(r'APS5_EXPORT\("([A-Za-z0-9+\-]{11})",\s*(\w+)\)')
NID_POSTFIX = "_nid_postfix"
CLASSES = ("implemented", "stub", "absent", "module")
PE_DIRECTORY_OFFSET = {0x20b: 112, 0x10b: 96}
@ -208,7 +207,7 @@ def source_stubs(source):
for library in sorted(item for item in Path(source).iterdir() if item.is_dir()):
aliases = {}
for file in library.rglob("*.cpp"):
for nid, name in EXPORT_ALIAS.findall(file.read_text(errors="ignore")):
for nid, name in nid_names.source_aliases(file.read_text(errors="ignore")):
aliases[name] = nid
group = progress.scan_library(library)
done = {nid_of(name, aliases) for name in group["done_names"]}

View File

@ -81,7 +81,7 @@ def source_aliases(text):
continue
if re.fullmatch(r'"[A-Za-z0-9+\-]{11}"', literal) is None:
continue
if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", name) and "unknown" in name.lower():
if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", name):
yield literal[1:-1], name
@ -97,7 +97,8 @@ def collect_unknowns():
for path in source_files(PRX):
text = path.read_text(encoding="utf-8")
for nid, name in source_aliases(text):
found.setdefault(nid, "%s:%s" % (path.relative_to(ROOT).as_posix(), name))
if "unknown" in name.lower():
found.setdefault(nid, "%s:%s" % (path.relative_to(ROOT).as_posix(), name))
return found

View File

@ -191,6 +191,48 @@ class ImportAuditTests(unittest.TestCase):
records, _ = import_audit.audit([(nid, "libB.prx")], exports, stubs, {"libA.prx", "libB.prx"}, set())
self.assertEqual(records[0]["class"], "implemented")
def test_formatted_export_aliases_remain_stubs(self):
work = self.work
nid = "AAAAAAAAAAA"
(work.libs / "libSceAlias.prx").write_bytes(exporting(nid))
registry = work.root / "game.json"
result = work.root / "audit.json"
write_registry(registry, [(nid, "libSceAlias.prx")])
aliases = [
'APS5_EXPORT (\n "AAAAAAAAAAA",\n NAME\n);\n',
'APS5_EXPORT(/* nid */ "AAAAAAAAAAA", /* name */ NAME);\n',
'APS5_EX\\\nPORT("AAAAAAAAAAA", NAME);\n',
]
for name in ("sceAliasUnknown", "sceAliasedCall"):
for alias in aliases:
with self.subTest(name=name, alias=alias):
write_source(work.source, "libSceAlias", alias.replace("NAME", name) + STUB_SOURCE.format(name=name))
code, _, err = work.run(registry, "--json", str(result))
self.assertEqual((code, err), (0, ""))
record, = json.loads(result.read_text())["imports"]
self.assertEqual((record["class"], record["name"]), ("stub", name))
def test_comments_and_literals_do_not_override_export_aliases(self):
work = self.work
nid, name = "AAAAAAAAAAA", "sceAliasUnknown"
(work.libs / "libSceAlias.prx").write_bytes(exporting(nid))
registry = work.root / "game.json"
result = work.root / "audit.json"
write_registry(registry, [(nid, "libSceAlias.prx")])
source = 'APS5_EXPORT("AAAAAAAAAAA", sceAliasUnknown);\n' + STUB_SOURCE.format(name=name)
decoys = [
'// APS5_EXPORT("BBBBBBBBBBB", sceAliasUnknown);\n',
'/* APS5_EXPORT("BBBBBBBBBBB", sceAliasUnknown); */\n',
'auto example = R"text(APS5_EXPORT("BBBBBBBBBBB", sceAliasUnknown);)text";\n',
]
for decoy in decoys:
with self.subTest(decoy=decoy):
write_source(work.source, "libSceAlias", source + decoy)
code, _, err = work.run(registry, "--json", str(result))
self.assertEqual((code, err), (0, ""))
record, = json.loads(result.read_text())["imports"]
self.assertEqual((record["class"], record["name"]), ("stub", name))
@unittest.skipUnless((import_audit.ROOT / ".git").exists(), "needs a git checkout")
def test_json_is_stamped_with_the_source_commit(self):
work = self.work

View File

@ -161,6 +161,11 @@ int APS5_VABI sceReal() { return 0; }
self.write_source('APS5_EXPORT (\n "AAAAAAAAAAA" ,\n exampleUnknown00\n );\n')
self.assertEqual(nid_names.collect_unknowns(), {"AAAAAAAAAAA": "core/libs/prx/libSceExample/Export.cpp:exampleUnknown00"})
def test_known_aliases_are_not_unknown_exports(self):
self.write_source('APS5_EXPORT("AAAAAAAAAAA", sceKnownCall);\n'
'APS5_EXPORT("BBBBBBBBBBB", exampleUnknown00);\n')
self.assertEqual(nid_names.collect_unknowns(), {"BBBBBBBBBBB": "core/libs/prx/libSceExample/Export.cpp:exampleUnknown00"})
def test_windows_source_paths_use_portable_separators(self):
self.write_source('APS5_EXPORT("AAAAAAAAAAA", exampleUnknown00);\n')
relative = PureWindowsPath("core/libs/prx/libSceExample/Export.cpp")