From 61ffa125fab714e2cfc90b7df396cfdb7ca7cfeb Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 15 May 2024 17:23:18 +0200 Subject: [PATCH 1/4] Copy variables when renaming --- .../variable_name_generation.py | 197 ++++++++++-------- decompiler/util/default.json | 10 + .../test_variable_name_generation.py | 103 ++++++--- 3 files changed, 195 insertions(+), 115 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/variable_name_generation.py b/decompiler/pipeline/controlflowanalysis/variable_name_generation.py index 906461de..39efc016 100644 --- a/decompiler/pipeline/controlflowanalysis/variable_name_generation.py +++ b/decompiler/pipeline/controlflowanalysis/variable_name_generation.py @@ -1,11 +1,15 @@ +import logging import string from abc import ABC, abstractmethod +from collections import defaultdict +from dataclasses import dataclass from enum import Enum -from typing import List, Set +from typing import Counter, List from decompiler.pipeline.stage import PipelineStage from decompiler.structures.pseudo import CustomType, Float, GlobalVariable, Integer, Pointer, Type, Variable from decompiler.structures.visitors.ast_dataflowobjectvisitor import BaseAstDataflowObjectVisitor +from decompiler.structures.visitors.substitute_visitor import SubstituteVisitor from decompiler.task import DecompilerTask @@ -13,15 +17,10 @@ class VariableCollector(BaseAstDataflowObjectVisitor): """Collect all variables in nodes/expressions""" def __init__(self): - self._variables: List[Variable] = [] + self.variables: list[Variable] = [] - def get_variables(self) -> list[Variable]: - """Get collected variables.""" - return self._variables - - def visit_variable(self, var: Variable): - """Add visited variables to list""" - self._variables.append(var) + def visit_variable(self, expression: Variable): + self.variables.append(expression) class NamingConvention(str, Enum): @@ -31,41 +30,28 @@ class NamingConvention(str, Enum): system_hungarian = "system_hungarian" -class RenamingScheme(ABC): - """Base class for different Renaming schemes.""" +@dataclass(frozen=True) +class VariableIdentifier: + name: str + ssa_label: int | None - def __init__(self, task: DecompilerTask) -> None: - """Collets all needed variables for renaming + filters which should not be renamed""" - collector = VariableCollector() - collector.visit_ast(task.syntax_tree) - self._ast = task.syntax_tree - self._params: List[Variable] = task.function_parameters - self._variables: Set[Variable] = set(filter(self._filter_variables, collector.get_variables())) - - def _filter_variables(self, item: Variable) -> bool: - """Return False if variable is either a: - - parameter - - GlobalVariable - """ - return not item in self._params and not isinstance(item, GlobalVariable) +def identifier(var: Variable) -> VariableIdentifier: + return VariableIdentifier(var.name, var.ssa_label) - def renameVariables(self): - """Rename all collected variables with a naming scheme.""" - names: dict[Variable, Variable] = {} - for var in self._variables: - names[var] = var.copy(name=self.getVariableName(var)) - - self._ast.replace_variables_in_subtree(self._ast.root, names) - +class RenamingScheme(ABC): @abstractmethod - def getVariableName(self, var: Variable) -> str: - "Should return a new name of a variable based on the old name and the counter" + def rename_variable(self, variable: Variable) -> Variable | None: pass +class NoRenamingScheme(RenamingScheme): + def rename_variable(self, variable: Variable) -> Variable | None: + return None + + class HungarianScheme(RenamingScheme): """Class which renames variables into hungarian notation.""" @@ -73,60 +59,88 @@ class HungarianScheme(RenamingScheme): Float: {16: "h", 32: "f", 64: "d", 80: "ld", 128: "q", 256: "o"}, Integer: {8: "ch", 16: "s", 32: "i", 64: "l", 128: "i128"}, } - def __init__(self, task: DecompilerTask) -> None: - super().__init__(task) - self._name = VariableNameGeneration.name - self._pointer_base: bool = task.options.getboolean(f"{self._name}.pointer_base", fallback=True) - self._type_separator: str = task.options.getstring(f"{self._name}.type_separator", fallback="") - self._counter_separator: str = task.options.getstring(f"{self._name}.counter_separator", fallback="") - self._variable_counter: dict[Variable, Integer] = {} - self._counter: Integer = 0 - - - def _get_counter(self, var: Variable) -> Integer: - """Look up if variable already has a counter, if not assign new one""" - if var not in self._variable_counter: # If the set _really_ works, then it's enough to just return a increasing number - self._variable_counter[var] = self._counter - self._counter += 1 - return self._variable_counter[var] + self._task = task + self._var_name: str = task.options.getstring(f"{VariableNameGeneration.name}.variable_name", fallback="var") + self._pointer_base: bool = task.options.getboolean(f"{VariableNameGeneration.name}.pointer_base", fallback=True) + self._type_separator: str = task.options.getstring(f"{VariableNameGeneration.name}.type_separator", fallback="") + self._counter_separator: str = task.options.getstring(f"{VariableNameGeneration.name}.counter_separator", fallback="") + + self._variables = self._get_variables_to_rename() + + counter = Counter[tuple[str, Type]]() + self._variable_rename_map: dict[VariableIdentifier, str] = {} + + variable_id: VariableIdentifier + vars: list[Variable] + for variable_id, vars in self._variables.items(): + # because the way our cfg works, each use site of each variable could theoretically have a different type + # we just take the first assuming that they are all the same... + var_type = vars[0].type + name_identifier = self._get_name_identifier(variable_id.name) + + counter_postfix = f"{self._counter_separator}{counter[(name_identifier, var_type)]}" + counter[(name_identifier, var_type)] += 1 + prefix = self._hungarian_prefix(var_type) + + new_name: str + if prefix is not None: + new_name = f"{prefix}{self._type_separator}{name_identifier.capitalize()}{counter_postfix}" + else: + new_name = f"{name_identifier}{counter_postfix}" + + self._variable_rename_map[variable_id] = new_name + + def rename_variable(self, variable: Variable) -> Variable | None: + new_name = self._variable_rename_map.get(identifier(variable)) + if new_name is None: + return None + else: + return variable.copy(name=new_name) def _get_name_identifier(self, name: str) -> str: """Return identifier by purging non alpha chars + capitalize the char afterwards. If string is too short, return generic""" if len(name) < 2: - return "var" + return self._var_name + x = string.capwords("".join([c if c.isalnum() else " " for c in name])) - x = x[0].lower() + x[1:] + x = x[0].lower() + x[1:] # important! We want to be able to choose later if the first letter should be capitalized return "".join(filter(str.isalpha, x)) + def _hungarian_prefix(self, var_type: Type) -> str | None: + """Return hungarian prefix to a given variable type.""" + match var_type: + case Pointer(): + if self._pointer_base: + return f"{self._hungarian_prefix(var_type.type)}p" + else: + return "p" + case CustomType(): + if var_type.is_boolean: + return "b" + elif var_type.size == 0: + return "v" + case _ if isinstance(var_type, Integer | Float): + sign = "u" if isinstance(var_type, Integer) and not var_type.is_signed else "" + prefix = self.type_prefix[type(var_type)].get(var_type.size, "unk") + return f"{sign}{prefix}" + + return None + + def _get_variables_to_rename(self) -> dict[VariableIdentifier, list[Variable]]: + collector = VariableCollector() + collector.visit_ast(self._task.ast) - def getVariableName(self, var: Variable) -> str: - """Return hungarian notation to a given variable. If no prefix exists, make the first char lower case.""" - newName = self._get_name_identifier(var._name) - if (prefix := self._hungarian_prefix(var.type)): - return f"{prefix}{self._type_separator}{newName.capitalize()}{self._counter_separator}{self._get_counter(var.name)}" - return f"{newName}{self._counter_separator}{self._get_counter(var.name)}" + def include_variable(item: Variable): + return item not in self._task.function_parameters and not isinstance(item, GlobalVariable) - def _hungarian_prefix(self, var_type: Type) -> str: - """Return hungarian prefix to a given variable type.""" - if isinstance(var_type, Pointer): - if self._pointer_base: - return f"{self._hungarian_prefix(var_type.type)}p" - return "p" - if isinstance(var_type, CustomType): - if var_type.is_boolean: - return "b" - elif var_type.size == 0: - return "v" - else: - return "" - if isinstance(var_type, (Integer, Float)): - sign = "u" if isinstance(var_type, Integer) and not var_type.is_signed else "" - prefix = self.type_prefix[type(var_type)].get(var_type.size, "unk") - return f"{sign}{prefix}" - return "" + variables: dict[VariableIdentifier, List[Variable]] = defaultdict(list) + for variable in collector.variables: + if include_variable(variable): + variables[identifier(variable)].append(variable) + return variables class VariableNameGeneration(PipelineStage): @@ -137,18 +151,29 @@ class VariableNameGeneration(PipelineStage): name: str = "variable-name-generation" - def __init__(self): - self._notation: str = None - def run(self, task: DecompilerTask): """Rename variable names to the given scheme.""" - self._notation = task.options.getstring(f"{self.name}.notation", fallback="default") - renamer: RenamingScheme = None + notation = task.options.getstring(f"{self.name}.notation", fallback=NamingConvention.default) - match self._notation: + scheme: RenamingScheme + match notation: + case NamingConvention.default: + scheme = NoRenamingScheme() case NamingConvention.system_hungarian: - renamer = HungarianScheme(task) - case _: # Implicit default convention, will not rename anything + scheme = HungarianScheme(task) + case _: + logging.warning("Unknown naming convention: %s", notation) return - renamer.renameVariables() + self._rename_with_scheme(task, scheme) + + @staticmethod + def _rename_with_scheme(task: DecompilerTask, rename_scheme: RenamingScheme): + rename_visitor = SubstituteVisitor(lambda o: rename_scheme.rename_variable(o) if isinstance(o, Variable) else None) + + for node in task.ast.nodes: + for obj in node.get_dataflow_objets(task.ast.condition_map): + new_obj = rename_visitor.visit(obj) + if new_obj is not None: + # while this should not happen, in theory, there is nothing preventing this case... + logging.warning("Variable name renaming couldn't rename %s", new_obj) diff --git a/decompiler/util/default.json b/decompiler/util/default.json index b62389a7..4c78bd40 100644 --- a/decompiler/util/default.json +++ b/decompiler/util/default.json @@ -238,6 +238,16 @@ "is_hidden_from_cli": false, "argument_name": "--variable-generation-notation" }, + { + "dest": "variable-name-generation.variable_name", + "default": "var", + "title": "Variable Base Name for hungarian notation", + "type": "string", + "description": "", + "is_hidden_from_gui": false, + "is_hidden_from_cli": false, + "argument_name": "--variable-generation-variable-name" + }, { "dest": "variable-name-generation.pointer_base", "default": true, diff --git a/tests/pipeline/controlflowanalysis/test_variable_name_generation.py b/tests/pipeline/controlflowanalysis/test_variable_name_generation.py index 93ee9cc4..f2b35ef7 100644 --- a/tests/pipeline/controlflowanalysis/test_variable_name_generation.py +++ b/tests/pipeline/controlflowanalysis/test_variable_name_generation.py @@ -30,10 +30,46 @@ VOID = CustomType.void() ALL_TYPES = [I8, I16, I32, I64, I128, UI8, UI16, UI32, UI64, UI128, HALF, FLOAT, DOUBLE, LONG_DOUBLE, QUADRUPLE, OCTUPLE, BOOL, VOID] -EXPECTED_BASE_NAMES = ["chVar0", "sVar0", "iVar0", "lVar0", "i128Var0", "uchVar0", "usVar0", "uiVar0", "ulVar0", "ui128Var0", "hVar0", - "fVar0", "dVar0", "ldVar0", "qVar0", "oVar0", "bVar0", "vVar0"] -EXPECTED_POINTER_NAMES = ["chpVar0", "spVar0", "ipVar0", "lpVar0", "i128pVar0", "uchpVar0", "uspVar0", "uipVar0", "ulpVar0", "ui128pVar0", - "hpVar0", "fpVar0", "dpVar0", "ldpVar0", "qpVar0", "opVar0", "bpVar0", "vpVar0"] +EXPECTED_BASE_NAMES = [ + "chVar0", + "sVar0", + "iVar0", + "lVar0", + "i128Var0", + "uchVar0", + "usVar0", + "uiVar0", + "ulVar0", + "ui128Var0", + "hVar0", + "fVar0", + "dVar0", + "ldVar0", + "qVar0", + "oVar0", + "bVar0", + "vVar0", +] +EXPECTED_POINTER_NAMES = [ + "chpVar0", + "spVar0", + "ipVar0", + "lpVar0", + "i128pVar0", + "uchpVar0", + "uspVar0", + "uipVar0", + "ulpVar0", + "ui128pVar0", + "hpVar0", + "fpVar0", + "dpVar0", + "ldpVar0", + "qpVar0", + "opVar0", + "bpVar0", + "vpVar0", +] def _generate_options(notation: str = "system_hungarian", pointer_base: bool = True, type_sep: str = "", counter_sep: str = "") -> Options: @@ -60,9 +96,10 @@ def _run_vng(ast: AbstractSyntaxTree, options: Options = _generate_options()): def test_default_notation_1(): - ast = AbstractSyntaxTree(CodeNode(Assignment(var := Variable("var_0", I32), Constant(0)), LogicCondition.initialize_true(LogicCondition.generate_new_context())), {}) + true_value = LogicCondition.initialize_true(LogicCondition.generate_new_context()) + ast = AbstractSyntaxTree(CodeNode([assignment := Assignment(Variable("var_0", I32), Constant(0))], true_value), {}) _run_vng(ast, _generate_options(notation="default")) - assert var.name == "var_0" + assert assignment.destination.name == "var_0" @pytest.mark.parametrize( @@ -71,65 +108,73 @@ def test_default_notation_1(): + [(Variable("var_" + str(i), Pointer(typ)), EXPECTED_POINTER_NAMES[i]) for i, typ in enumerate(ALL_TYPES)], ) def test_hungarian_notation(variable, name): - node = CodeNode([Assignment(variable, Constant(42))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) - ast = AbstractSyntaxTree(node, {}) + true_value = LogicCondition.initialize_true(LogicCondition.generate_new_context()) + ast = AbstractSyntaxTree(CodeNode([assignment := Assignment(variable, Constant(42))], true_value), {}) _run_vng(ast) - assert node.instructions[0].destination.name == name + assert assignment.destination.name == name @pytest.mark.parametrize("type_sep, counter_sep", [("", ""), ("_", "_")]) def test_hungarian_notation_separators(type_sep: str, counter_sep: str): - node = CodeNode([Assignment(Variable("var_0", I32), Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) - ast = AbstractSyntaxTree(node, {}) + true_value = LogicCondition.initialize_true(LogicCondition.generate_new_context()) + ast = AbstractSyntaxTree(CodeNode([assignment := Assignment(Variable("var_0", I32), Constant(0))], true_value), {}) _run_vng(ast, _generate_options(type_sep=type_sep, counter_sep=counter_sep)) - for instr in node.instructions: - assert instr.destination.name == f"i{type_sep}Var{counter_sep}0" + assert assignment.destination.name == f"i{type_sep}Var{counter_sep}0" def test_custom_type(): - node = CodeNode([Assignment(Variable("var_0", CustomType("size_t", 64)), Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) - ast = AbstractSyntaxTree(node, {}) + true_value = LogicCondition.initialize_true(LogicCondition.generate_new_context()) + ast = AbstractSyntaxTree(CodeNode([assignment := Assignment(Variable("var_0", CustomType("size_t", 64)), Constant(0))], true_value), {}) _run_vng(ast, _generate_options()) - assert node.instructions[0].destination.name == "var0" # if there is no type, the first char should be lower + assert assignment.destination.name == "var0" # if there is no type, the first char should be lower def test_bninja_invalid_type(): - node = CodeNode([Assignment(Variable("var_0", Integer(104, True)), Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) - ast = AbstractSyntaxTree(node, {}) + true_value = LogicCondition.initialize_true(LogicCondition.generate_new_context()) + ast = AbstractSyntaxTree(CodeNode([assignment := Assignment(Variable("var_0", Integer(104, True)), Constant(0))], true_value), {}) _run_vng(ast, _generate_options()) - for instr in node.instructions: - assert instr.destination.name == "unkVar0" + assert assignment.destination.name == "unkVar0" def test_same_variable(): """Variables can be copies of the same one. The renamer should only rename a variable once. (More times would destroy the actual name)""" var1 = Variable("tmp_42", Float(64)) - node = CodeNode([ - Assignment(var1, Constant(0)), - Assignment(var1, Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) + node = CodeNode( + [Assignment(var1, Constant(0)), Assignment(var1, Constant(0))], + LogicCondition.initialize_true(LogicCondition.generate_new_context()), + ) ast = AbstractSyntaxTree(node, {}) _run_vng(ast, _generate_options()) assert node.instructions[0].destination.name == "dTmp0" + assert node.instructions[1].destination.name == "dTmp0" + def test_same_variable_idx(): """Variables with the same counter should not be renamed into the same thing""" var1 = Variable("x_1", Integer.int32_t()) var2 = Variable("y_1", Integer.int32_t()) - node = CodeNode([ - Assignment(var1, Constant(0)), - Assignment(var2, Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) + node = CodeNode( + [Assignment(var1, Constant(0)), Assignment(var2, Constant(0))], + LogicCondition.initialize_true(LogicCondition.generate_new_context()), + ) ast = AbstractSyntaxTree(node, {}) _run_vng(ast, _generate_options()) assert node.instructions[0].destination.name != node.instructions[1].destination.name + def test_different_custom_names_0(): - node = CodeNode([Assignment(Variable("tmp_42", Float(64)), Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) + node = CodeNode( + [Assignment(Variable("tmp_42", Float(64)), Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context()) + ) ast = AbstractSyntaxTree(node, {}) _run_vng(ast, _generate_options()) assert node.instructions[0].destination.name == "dTmp0" + def test_different_custom_names_1(): - node = CodeNode([Assignment(Variable("loop_break", Float(64)), Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context())) + node = CodeNode( + [Assignment(Variable("loop_break", Float(64)), Constant(0))], LogicCondition.initialize_true(LogicCondition.generate_new_context()) + ) ast = AbstractSyntaxTree(node, {}) _run_vng(ast, _generate_options()) - assert node.instructions[0].destination.name == "dLoopbreak0" + assert node.instructions[0].destination.name == "dLoopBreak0" From 17aa000c0a48823dda28d5541796a4636c61fc5b Mon Sep 17 00:00:00 2001 From: Spartak Ehrlich Date: Wed, 12 Jun 2024 16:39:29 +0200 Subject: [PATCH 2/4] Fix: ArrayType,config,tests --- .../controlflowanalysis/variable_name_generation.py | 13 +++++++------ decompiler/util/default.json | 10 ---------- .../test_variable_name_generation.py | 2 +- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/variable_name_generation.py b/decompiler/pipeline/controlflowanalysis/variable_name_generation.py index 39efc016..4f71f274 100644 --- a/decompiler/pipeline/controlflowanalysis/variable_name_generation.py +++ b/decompiler/pipeline/controlflowanalysis/variable_name_generation.py @@ -7,7 +7,7 @@ from typing import Counter, List from decompiler.pipeline.stage import PipelineStage -from decompiler.structures.pseudo import CustomType, Float, GlobalVariable, Integer, Pointer, Type, Variable +from decompiler.structures.pseudo import ArrayType, CustomType, Float, GlobalVariable, Integer, Pointer, Type, Variable from decompiler.structures.visitors.ast_dataflowobjectvisitor import BaseAstDataflowObjectVisitor from decompiler.structures.visitors.substitute_visitor import SubstituteVisitor from decompiler.task import DecompilerTask @@ -80,7 +80,7 @@ def __init__(self, task: DecompilerTask) -> None: var_type = vars[0].type name_identifier = self._get_name_identifier(variable_id.name) - counter_postfix = f"{self._counter_separator}{counter[(name_identifier, var_type)]}" + counter_postfix = f"{self._counter_separator}{counter[(name_identifier, var_type)]}" # array[0x12] != array[0x13] kriegt aber selben Namen, da Typen unterschiedlich sind counter[(name_identifier, var_type)] += 1 prefix = self._hungarian_prefix(var_type) @@ -112,17 +112,18 @@ def _get_name_identifier(self, name: str) -> str: def _hungarian_prefix(self, var_type: Type) -> str | None: """Return hungarian prefix to a given variable type.""" match var_type: - case Pointer(): + case Pointer() | ArrayType(): if self._pointer_base: - return f"{self._hungarian_prefix(var_type.type)}p" + pprefix = self._hungarian_prefix(var_type.type) + return f"{pprefix}p" if pprefix is not None else "unkp" else: return "p" case CustomType(): if var_type.is_boolean: return "b" - elif var_type.size == 0: + if var_type.size == 0: return "v" - case _ if isinstance(var_type, Integer | Float): + case Integer() | Float(): sign = "u" if isinstance(var_type, Integer) and not var_type.is_signed else "" prefix = self.type_prefix[type(var_type)].get(var_type.size, "unk") return f"{sign}{prefix}" diff --git a/decompiler/util/default.json b/decompiler/util/default.json index 4c78bd40..b2b86499 100644 --- a/decompiler/util/default.json +++ b/decompiler/util/default.json @@ -213,16 +213,6 @@ "is_hidden_from_cli": false, "argument_name": "--for-loop-exclude-conditions" }, - { - "dest": "readability-based-refinement.rename_while_loop_variables", - "default": true, - "type": "boolean", - "title": "Rename while-loop variables", - "description": "Rename while-loop counter variables to counter, counter1, ...", - "is_hidden_from_gui": false, - "is_hidden_from_cli": false, - "argument_name": "--rename-while-loop-variables" - }, { "dest": "variable-name-generation.notation", "default": "system_hungarian", diff --git a/tests/pipeline/controlflowanalysis/test_variable_name_generation.py b/tests/pipeline/controlflowanalysis/test_variable_name_generation.py index f2b35ef7..8aa011aa 100644 --- a/tests/pipeline/controlflowanalysis/test_variable_name_generation.py +++ b/tests/pipeline/controlflowanalysis/test_variable_name_generation.py @@ -177,4 +177,4 @@ def test_different_custom_names_1(): ) ast = AbstractSyntaxTree(node, {}) _run_vng(ast, _generate_options()) - assert node.instructions[0].destination.name == "dLoopBreak0" + assert node.instructions[0].destination.name == "dLoopbreak0" From f4b4e79fee8f445198efe96f7f6302c0f40dbeb5 Mon Sep 17 00:00:00 2001 From: Spartak Ehrlich Date: Thu, 13 Jun 2024 09:51:56 +0200 Subject: [PATCH 3/4] Fix: Prefix+Arrays --- .../variable_name_generation.py | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/variable_name_generation.py b/decompiler/pipeline/controlflowanalysis/variable_name_generation.py index 4f71f274..2baba234 100644 --- a/decompiler/pipeline/controlflowanalysis/variable_name_generation.py +++ b/decompiler/pipeline/controlflowanalysis/variable_name_generation.py @@ -69,7 +69,7 @@ def __init__(self, task: DecompilerTask) -> None: self._variables = self._get_variables_to_rename() - counter = Counter[tuple[str, Type]]() + counter = Counter[tuple[str, str]]() self._variable_rename_map: dict[VariableIdentifier, str] = {} variable_id: VariableIdentifier @@ -79,17 +79,12 @@ def __init__(self, task: DecompilerTask) -> None: # we just take the first assuming that they are all the same... var_type = vars[0].type name_identifier = self._get_name_identifier(variable_id.name) - - counter_postfix = f"{self._counter_separator}{counter[(name_identifier, var_type)]}" # array[0x12] != array[0x13] kriegt aber selben Namen, da Typen unterschiedlich sind - counter[(name_identifier, var_type)] += 1 - prefix = self._hungarian_prefix(var_type) - new_name: str - if prefix is not None: - new_name = f"{prefix}{self._type_separator}{name_identifier.capitalize()}{counter_postfix}" - else: - new_name = f"{name_identifier}{counter_postfix}" + counter_postfix = f"{self._counter_separator}{counter[(name_identifier, prefix)]}" + counter[(name_identifier, prefix)] += 1 + + new_name: str = f"{prefix}{self._type_separator}{name_identifier.capitalize()}{counter_postfix}" self._variable_rename_map[variable_id] = new_name @@ -112,12 +107,13 @@ def _get_name_identifier(self, name: str) -> str: def _hungarian_prefix(self, var_type: Type) -> str | None: """Return hungarian prefix to a given variable type.""" match var_type: - case Pointer() | ArrayType(): + case Pointer(): if self._pointer_base: - pprefix = self._hungarian_prefix(var_type.type) - return f"{pprefix}p" if pprefix is not None else "unkp" + return f"{self._hungarian_prefix(var_type.type)}p" else: return "p" + case ArrayType(): + return f"arr{self._hungarian_prefix(var_type.type)}" case CustomType(): if var_type.is_boolean: return "b" @@ -128,7 +124,7 @@ def _hungarian_prefix(self, var_type: Type) -> str | None: prefix = self.type_prefix[type(var_type)].get(var_type.size, "unk") return f"{sign}{prefix}" - return None + return "unk" def _get_variables_to_rename(self) -> dict[VariableIdentifier, list[Variable]]: collector = VariableCollector() From 8395db65beac4bd1bc129004aec4826d1fde5466 Mon Sep 17 00:00:00 2001 From: Spartak Ehrlich Date: Wed, 19 Jun 2024 10:09:24 +0200 Subject: [PATCH 4/4] Fix: Custom test --- .../controlflowanalysis/test_variable_name_generation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pipeline/controlflowanalysis/test_variable_name_generation.py b/tests/pipeline/controlflowanalysis/test_variable_name_generation.py index 8aa011aa..15f434e3 100644 --- a/tests/pipeline/controlflowanalysis/test_variable_name_generation.py +++ b/tests/pipeline/controlflowanalysis/test_variable_name_generation.py @@ -126,7 +126,7 @@ def test_custom_type(): true_value = LogicCondition.initialize_true(LogicCondition.generate_new_context()) ast = AbstractSyntaxTree(CodeNode([assignment := Assignment(Variable("var_0", CustomType("size_t", 64)), Constant(0))], true_value), {}) _run_vng(ast, _generate_options()) - assert assignment.destination.name == "var0" # if there is no type, the first char should be lower + assert assignment.destination.name == "unkVar0" def test_bninja_invalid_type():