Skip to content

Commit 46e2d14

Browse files
committed
feat: add support for typedefs in C-to-Python struct parsing
1 parent 1e1ed75 commit 46e2d14

2 files changed

Lines changed: 58 additions & 4 deletions

File tree

libdestruct/c/struct_parser.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,16 @@ def typedef_to_pair(typedef: c_ast.Typedef) -> tuple[str, type[obj]]:
196196
if not isinstance(typedef, c_ast.Typedef):
197197
raise TypeError("Definition must be a typedef.")
198198

199-
if not isinstance(typedef.type, c_ast.TypeDecl):
200-
raise TypeError("Definition must be a type declaration.")
201-
202199
name = "".join(typedef.name)
203-
definition = type_decl_to_type(typedef.type)
200+
201+
if isinstance(typedef.type, c_ast.PtrDecl):
202+
definition = ptr_to_type(typedef.type)
203+
elif isinstance(typedef.type, c_ast.ArrayDecl):
204+
definition = arr_to_type(typedef.type)
205+
elif isinstance(typedef.type, c_ast.TypeDecl):
206+
definition = type_decl_to_type(typedef.type)
207+
else:
208+
raise TypeError("Unsupported typedef target type.")
204209

205210
return name, definition
206211

test/scripts/struct_parser_unit_test.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
# Licensed under the MIT license. See LICENSE file in the project root for details.
55
#
66

7+
import struct as pystruct
78
import unittest
89

910
from libdestruct.c.struct_parser import definition_to_type
11+
from libdestruct import inflater
1012

1113

1214
class StructParserTest(unittest.TestCase):
@@ -39,5 +41,52 @@ def test_nested_struct_definition(self):
3941
self.assertIn("b", t.__annotations__)
4042

4143

44+
class TypedefTest(unittest.TestCase):
45+
"""Typedef support in C struct parser."""
46+
47+
def test_simple_typedef(self):
48+
t = definition_to_type("""
49+
typedef unsigned int uint32_t;
50+
struct S { uint32_t x; };
51+
""")
52+
self.assertIn("x", t.__annotations__)
53+
54+
def test_typedef_of_struct(self):
55+
t = definition_to_type("""
56+
typedef struct { int x; } Point;
57+
struct S { Point p; };
58+
""")
59+
self.assertIn("p", t.__annotations__)
60+
61+
def test_typedef_of_pointer(self):
62+
t = definition_to_type("""
63+
typedef int *intptr;
64+
struct S { intptr p; };
65+
""")
66+
self.assertIn("p", t.__annotations__)
67+
68+
def test_typedef_chain(self):
69+
t = definition_to_type("""
70+
typedef unsigned int u32;
71+
typedef u32 mytype;
72+
struct S { mytype x; };
73+
""")
74+
self.assertIn("x", t.__annotations__)
75+
76+
def test_typedef_inflate_and_read(self):
77+
t = definition_to_type("""
78+
typedef unsigned int uint32_t;
79+
struct S { uint32_t x; int y; };
80+
""")
81+
memory = bytearray(8)
82+
memory[0:4] = pystruct.pack("<I", 0xDEADBEEF)
83+
memory[4:8] = pystruct.pack("<i", -42)
84+
85+
lib = inflater(memory)
86+
s = lib.inflate(t, 0)
87+
self.assertEqual(s.x.value, 0xDEADBEEF)
88+
self.assertEqual(s.y.value, -42)
89+
90+
4291
if __name__ == "__main__":
4392
unittest.main()

0 commit comments

Comments
 (0)