You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/tools/snippets/monero_unused_functions.py

111 lines
3.6 KiB

"""
Find out which functions are unused in the Monero app - based on
`monero.pyi` file.
"""
import subprocess
import sys
from pathlib import Path
from typing import Any, Dict, List, Set
CURRENT_DIR = Path(__file__).resolve().parent
ROOT_DIR = CURRENT_DIR.parent.parent
HELPER_FILE = ROOT_DIR / "core/src/apps/monero/xmr/crypto_helpers.py"
MOCK_FILE = ROOT_DIR / "core/mocks/generated/trezorcrypto/monero.pyi"
def generate_function_mapping() -> Dict[str, List[str]]:
"""Look at all Monero functions and generate a mapping of their usage"""
# Load all the function names in .pyi file
pyi_functions: Set[str] = set()
with open(MOCK_FILE, "r") as f:
lines = f.readlines()
for line in lines:
if line.startswith("def"):
f_name = line.split("(")[0].split(" ")[1]
pyi_functions.add(f_name)
# Load definitions of helper functions
helper_func_defs: Dict[str, str] = {}
with open(HELPER_FILE, "r") as f:
lines = f.readlines()
current_func = ""
for line in lines:
if line.startswith("def"):
current_func = line.split("(")[0].split(" ")[1]
helper_func_defs[current_func] = line
elif not current_func:
continue
else:
helper_func_defs[current_func] += line
# Try to connect function names with helper definitions
func_mapping: Dict[str, List[str]] = {}
for func_name in pyi_functions:
func_mapping[func_name] = []
for func_def_name, func_code in helper_func_defs.items():
if f".{func_name}(" in func_code:
func_mapping[func_name].append(func_def_name)
# Functions may not be used in helper file, alias them to themselves
if not func_mapping[func_name]:
func_mapping[func_name] = [func_name]
return func_mapping
def check_usage_of_functions(func_mapping: Dict[str, List[str]]) -> None:
"""Go through all the functions and check if they are used in the Monero app.
Generates a report and exits with an appropriate exit code.
"""
# Include boolean field to know what is used
is_used_mappings: Dict[str, Dict[str, Any]] = {}
for func_name, mapping in func_mapping.items():
is_used_mappings[func_name] = {"mapping": mapping, "is_used": False}
# Check if any of the mapping names is used - and mark it as used if so
for func_name in is_used_mappings:
for mapping in is_used_mappings[func_name]["mapping"]:
is_there = _is_used(mapping)
if is_there:
is_used_mappings[func_name]["is_used"] = True
break
# Find unused functions and generate a report
unused_functions = {
fc: val for fc, val in is_used_mappings.items() if not val["is_used"]
}
if not unused_functions:
print("SUCCESS: no functions are unused")
sys.exit(0)
else:
print(f"{len(unused_functions)} unused functions:")
for func, values in unused_functions.items():
print(func, values)
sys.exit(1)
def _is_used(func_name: str) -> bool:
"""Find function usage in the Monero app or in test files"""
cmds = [
rf'grep -r ".{func_name}\b" {ROOT_DIR}/core/src/apps/monero',
rf'grep -r ".{func_name}\b" {ROOT_DIR}/core/tests',
]
for cmd in cmds:
grep_result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
if grep_result.returncode == 0:
return True
return False
if "__main__" == __name__:
func_mapping = generate_function_mapping()
check_usage_of_functions(func_mapping)