mirror of
https://github.com/isledecomp/isle.git
synced 2026-01-27 10:11:15 +00:00
feat: Reuse enums instead of recreating them every time
This commit is contained in:
parent
c8dc77cbf4
commit
56b8c96d6a
@ -1,5 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, Callable, TypeVar
|
||||||
|
|
||||||
# Disable spurious warnings in vscode / pylance
|
# Disable spurious warnings in vscode / pylance
|
||||||
# pyright: reportMissingModuleSource=false
|
# pyright: reportMissingModuleSource=false
|
||||||
@ -29,6 +29,7 @@
|
|||||||
CategoryPath,
|
CategoryPath,
|
||||||
DataType,
|
DataType,
|
||||||
DataTypeConflictHandler,
|
DataTypeConflictHandler,
|
||||||
|
Enum,
|
||||||
EnumDataType,
|
EnumDataType,
|
||||||
StructureDataType,
|
StructureDataType,
|
||||||
StructureInternal,
|
StructureInternal,
|
||||||
@ -47,7 +48,9 @@ def __init__(self, api: FlatProgramAPI, extraction: PdbFunctionExtractor):
|
|||||||
self.extraction = extraction
|
self.extraction = extraction
|
||||||
# tracks the structs/classes we have already started to import, otherwise we run into infinite recursion
|
# tracks the structs/classes we have already started to import, otherwise we run into infinite recursion
|
||||||
self.handled_structs: set[str] = set()
|
self.handled_structs: set[str] = set()
|
||||||
self.struct_call_stack: list[str] = []
|
|
||||||
|
# tracks the enums we have already handled for the sake of efficiency
|
||||||
|
self.handled_enums: dict[str, Enum] = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def types(self):
|
def types(self):
|
||||||
@ -166,9 +169,13 @@ def _import_enum(self, type_pdb: dict[str, Any]) -> DataType:
|
|||||||
field_list = self.extraction.compare.cv.types.keys.get(type_pdb["field_type"])
|
field_list = self.extraction.compare.cv.types.keys.get(type_pdb["field_type"])
|
||||||
assert field_list is not None, f"Failed to find field list for enum {type_pdb}"
|
assert field_list is not None, f"Failed to find field list for enum {type_pdb}"
|
||||||
|
|
||||||
result = EnumDataType(
|
result = self._get_or_create_enum_data_type(
|
||||||
CategoryPath("/imported"), type_pdb["name"], underlying_type.getLength()
|
type_pdb["name"], underlying_type.getLength()
|
||||||
)
|
)
|
||||||
|
# clear existing variant if there are any
|
||||||
|
for existing_variant in result.getNames():
|
||||||
|
result.remove(existing_variant)
|
||||||
|
|
||||||
variants: list[dict[str, Any]] = field_list["variants"]
|
variants: list[dict[str, Any]] = field_list["variants"]
|
||||||
for variant in variants:
|
for variant in variants:
|
||||||
result.add(variant["name"], variant["value"])
|
result.add(variant["name"], variant["value"])
|
||||||
@ -259,30 +266,73 @@ def _get_or_create_namespace(self, class_name_with_namespace: str):
|
|||||||
parent_namespace = create_ghidra_namespace(self.api, colon_split)
|
parent_namespace = create_ghidra_namespace(self.api, colon_split)
|
||||||
self.api.createClass(parent_namespace, class_name)
|
self.api.createClass(parent_namespace, class_name)
|
||||||
|
|
||||||
|
def _get_or_create_enum_data_type(
|
||||||
|
self, enum_type_name: str, enum_type_size: int
|
||||||
|
) -> Enum:
|
||||||
|
|
||||||
|
if (known_enum := self.handled_enums.get(enum_type_name, None)) is not None:
|
||||||
|
return known_enum
|
||||||
|
|
||||||
|
result = self._get_or_create_data_type(
|
||||||
|
enum_type_name,
|
||||||
|
"enum",
|
||||||
|
Enum,
|
||||||
|
lambda: EnumDataType(
|
||||||
|
CategoryPath("/imported"), enum_type_name, enum_type_size
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.handled_enums[enum_type_name] = result
|
||||||
|
return result
|
||||||
|
|
||||||
def _get_or_create_struct_data_type(
|
def _get_or_create_struct_data_type(
|
||||||
self, class_name_with_namespace: str, class_size: int
|
self, class_name_with_namespace: str, class_size: int
|
||||||
) -> StructureInternal:
|
) -> StructureInternal:
|
||||||
|
return self._get_or_create_data_type(
|
||||||
|
class_name_with_namespace,
|
||||||
|
"class/struct",
|
||||||
|
StructureInternal,
|
||||||
|
lambda: StructureDataType(
|
||||||
|
CategoryPath("/imported"), class_name_with_namespace, class_size
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
T = TypeVar("T", bound=DataType)
|
||||||
|
|
||||||
|
def _get_or_create_data_type(
|
||||||
|
self,
|
||||||
|
type_name: str,
|
||||||
|
readable_name_of_type_category: str,
|
||||||
|
expected_type: type[T],
|
||||||
|
new_instance_callback: Callable[[], T],
|
||||||
|
) -> T:
|
||||||
|
"""
|
||||||
|
Checks if a data type provided under the given name exists in Ghidra.
|
||||||
|
Creates one using `new_instance_callback` if there is not.
|
||||||
|
Also verifies the data type.
|
||||||
|
|
||||||
|
Note that the return value of `addDataType()` is not the same instance as the input
|
||||||
|
even if there is no name collision.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
data_type = get_ghidra_type(self.api, class_name_with_namespace)
|
data_type = get_ghidra_type(self.api, type_name)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Found existing data type %s under category path %s",
|
"Found existing %s type %s under category path %s",
|
||||||
class_name_with_namespace,
|
readable_name_of_type_category,
|
||||||
|
type_name,
|
||||||
data_type.getCategoryPath(),
|
data_type.getCategoryPath(),
|
||||||
)
|
)
|
||||||
except TypeNotFoundInGhidraError:
|
except TypeNotFoundInGhidraError:
|
||||||
# Create a new struct data type
|
|
||||||
data_type = StructureDataType(
|
|
||||||
CategoryPath("/imported"), class_name_with_namespace, class_size
|
|
||||||
)
|
|
||||||
data_type = (
|
data_type = (
|
||||||
self.api.getCurrentProgram()
|
self.api.getCurrentProgram()
|
||||||
.getDataTypeManager()
|
.getDataTypeManager()
|
||||||
.addDataType(data_type, DataTypeConflictHandler.KEEP_HANDLER)
|
.addDataType(new_instance_callback(), DataTypeConflictHandler.KEEP_HANDLER)
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Created new %s data type %s", readable_name_of_type_category, type_name
|
||||||
)
|
)
|
||||||
logger.info("Created new data type %s", class_name_with_namespace)
|
|
||||||
assert isinstance(
|
assert isinstance(
|
||||||
data_type, StructureInternal
|
data_type, expected_type
|
||||||
), f"Found type sharing its name with a class/struct, but is not a struct: {class_name_with_namespace}"
|
), f"Found existing type named {type_name} that is not a {readable_name_of_type_category}"
|
||||||
return data_type
|
return data_type
|
||||||
|
|
||||||
def _delete_and_recreate_struct_data_type(
|
def _delete_and_recreate_struct_data_type(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user