From c9038f7d8daad962c4aed9d57f8815385a13d5f3 Mon Sep 17 00:00:00 2001 From: Winvoker Date: Sun, 11 Oct 2026 15:05:41 +0300 Subject: [PATCH] fix(progress): match stub calls across whitespace --- docs/dev/PROGRESS.md | 2 +- tools/progress.py | 10 ++++++---- tools/tests/test_progress.py | 24 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/dev/PROGRESS.md b/docs/dev/PROGRESS.md index dec716d9f..ca20cad09 100644 --- a/docs/dev/PROGRESS.md +++ b/docs/dev/PROGRESS.md @@ -4,7 +4,7 @@ ## Library counts -The scanner reads `APS5_VABI` function definitions under `core/libs/prx//`, excluding `tests` subdirectories and their descendants. Test callbacks do not contribute to library counts. A definition is pending when its body calls `NotImplemented_nid_no_patch`, directly or through a recognized local stub wrapper. Other definitions are counted as implemented. Braces inside string literals, character literals and comments do not delimit bodies. These counts describe declared functions, not every firmware export, verified ABI compatibility or playable games. Silent behavior and unverified assumptions remain in [Technical debt](TechnicalDebt.md). +The scanner reads `APS5_VABI` function definitions under `core/libs/prx//`, excluding `tests` subdirectories and their descendants. Test callbacks do not contribute to library counts. A definition is pending when its body calls `NotImplemented_nid_no_patch`, directly or through a recognized local stub wrapper. Call matching uses complete identifiers and accepts whitespace or comments before `(`. Other definitions are counted as implemented. Braces inside string literals, character literals and comments do not delimit bodies. These counts describe declared functions, not every firmware export, verified ABI compatibility or playable games. Silent behavior and unverified assumptions remain in [Technical debt](TechnicalDebt.md). A library with no scanned definitions can include a sibling library's source builder. The scanner recognizes literal `include(${CMAKE_CURRENT_SOURCE_DIR}/..//.cmake)` references, including quoted paths. It does not evaluate arbitrary CMake expressions. It also recognizes a top-level `.cpp` that includes another library's source with `#include "prx//.cpp"`. diff --git a/tools/progress.py b/tools/progress.py index 6f37f6cce..e3b281bb8 100644 --- a/tools/progress.py +++ b/tools/progress.py @@ -13,6 +13,7 @@ ISA = Path(__file__).resolve().parent / "rdna_isa.txt" SOURCE = f'https://github.com/{os.environ.get("GITHUB_REPOSITORY", "boykopovar/AnyPS5")}/blob/main' DEFINITION = re.compile(r"\bAPS5_VABI\s+(\w+)\s*\([^;{]*\)\s*(?:noexcept\s*)?(?:try\s*)?\{") STUB = "NotImplemented_nid_no_patch" +CALL = re.compile(r"\b(\w+)\s*\(") CMAKE_SHARED = re.compile(r'^\s*include\(\s*"?\$\{CMAKE_CURRENT_SOURCE_DIR\}/\.\./([\w.-]+)/[\w.-]+\.cmake"?\s*\)', re.M) SOURCE_SHARED = re.compile(r'^\s*#\s*include\s*"prx/([\w.-]+)/[\w./-]+\.cpp"', re.M) STUB_WRAPPER = re.compile(r"\bstatic\s+(?:\[\[noreturn\]\]\s+)?[\w:<>,\s*&]+?\s+(\w+)\s*\([^;{]*\)\s*\{") @@ -91,10 +92,11 @@ def body_end(text, start): def stub_calls(text): - calls = [STUB] + calls = {STUB} for match in STUB_WRAPPER.finditer(text): - if STUB in text[match.end() - 1:body_end(text, match.end() - 1)]: - calls.append(match.group(1) + "(") + body = text[match.end() - 1:body_end(text, match.end() - 1)] + if any(call.group(1) == STUB for call in CALL.finditer(body)): + calls.add(match.group(1)) return calls @@ -156,7 +158,7 @@ def scan_library(path): if name.endswith("_nid_no_patch"): continue body = text[match.end() - 1:body_end(text, match.end() - 1)] - if any(call in body for call in calls): + if any(call.group(1) in calls for call in CALL.finditer(body)): todo.add(name) continue done.add(name) diff --git a/tools/tests/test_progress.py b/tools/tests/test_progress.py index a68933827..d5ee4c87a 100644 --- a/tools/tests/test_progress.py +++ b/tools/tests/test_progress.py @@ -20,6 +20,30 @@ class ProgressTests(unittest.TestCase): self.assertEqual(group["done_names"], []) self.assertEqual(group["todo_names"], ["sceExampleSupportFormat"]) + def test_stub_wrapper_calls_allow_whitespace(self): + for separator in ("", " ", "\t", "\n", " /* separator */ "): + with self.subTest(separator=separator), tempfile.TemporaryDirectory() as directory: + library = Path(directory) / "libSceExample" + library.mkdir() + (library / "Export.cpp").write_text( + "static int Unsupported() { NotImplemented_nid_no_patch(__func__); return 0; }\n" + f"int APS5_VABI Pending() {{ return Unsupported{separator}(); }}\n") + group = progress.scan_library(library) + self.assertEqual(group["done_names"], []) + self.assertEqual(group["todo_names"], ["Pending"]) + + def test_stub_wrapper_names_match_whole_identifiers(self): + with tempfile.TemporaryDirectory() as directory: + library = Path(directory) / "libSceExample" + library.mkdir() + (library / "Export.cpp").write_text( + "static int Unsupported() { NotImplemented_nid_no_patch(__func__); return 0; }\n" + "static int NotUnsupported(int value) { return value * 2; }\n" + "int APS5_VABI Ready(int value) { return NotUnsupported(value); }\n") + group = progress.scan_library(library) + self.assertEqual(group["done_names"], ["Ready"]) + self.assertEqual(group["todo_names"], []) + def test_braces_in_literals_and_comments_do_not_delimit_bodies(self): with tempfile.TemporaryDirectory() as directory: library = Path(directory) / "libSceExample"