From 3d5c41ee4cd76bd5b0ff5d28946c428bff1e689c Mon Sep 17 00:00:00 2001 From: nimlgen Date: Fri, 26 Jan 2024 07:17:49 +0000 Subject: [PATCH 01/14] fix namegen for anon/unnamed --- ctypeslib/codegen/handler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ctypeslib/codegen/handler.py b/ctypeslib/codegen/handler.py index 38ee1f5..3e62aba 100644 --- a/ctypeslib/codegen/handler.py +++ b/ctypeslib/codegen/handler.py @@ -5,6 +5,7 @@ from ctypeslib.codegen import typedesc from ctypeslib.codegen.util import log_entity +import re import logging log = logging.getLogger('handler') @@ -149,6 +150,13 @@ def get_unique_name(self, cursor, field_name=None): #import code #code.interact(local=locals()) return '' + + # rewrite with unnamed/anonymous parts. + pattern = r'\s+\(unnamed at .*/(.*?).h:(\d+):\d+\)' + name = re.sub(pattern, lambda x: f"_{x.group(1)}_h_{x.group(2)}", name) + pattern = r'\s+\(anonymous at .*/(.*?).h:(\d+):\d+\)' + name = re.sub(pattern, lambda x: f"_{x.group(1)}_h_{x.group(2)}", name) + if cursor.kind in [CursorKind.STRUCT_DECL,CursorKind.UNION_DECL, CursorKind.CLASS_DECL, CursorKind.CXX_BASE_SPECIFIER]: names= {CursorKind.STRUCT_DECL: 'struct', From 4348caa786c2f0dfada204f50222f622bb00cde6 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:15:00 +0000 Subject: [PATCH 02/14] funcs --- ctypeslib/codegen/codegenerator.py | 4 ++-- ctypeslib/codegen/cursorhandler.py | 7 +++++-- test/test_macro.py | 8 ++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 7d9d9fa..78c0dcb 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -204,8 +204,8 @@ def Macro(self, macro): # is not code-generable ? # codegen should decide what codegen can do. if macro.args: - print("# def %s%s: # macro" % (macro.name, macro.args), file=self.stream) - print("# return %s " % macro.body, file=self.stream) + print("def %s%s: # macro" % (macro.name, macro.args), file=self.stream) + print(" return %s " % macro.body, file=self.stream) elif util.contains_undefined_identifier(macro): # we can't handle that, we comment it out if isinstance(macro.body, typedesc.UndefinedIdentifier): diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index cc3e9ec..a027aef 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -555,10 +555,13 @@ def _literal_handling(self, cursor): pass elif token.kind == TokenKind.KEYWORD: # noqa log.debug("Got a MACRO_DEFINITION referencing a KEYWORD token.kind: %s", token.kind.name) - value = typedesc.UndefinedIdentifier(value) + value=None # HACK: remove casts + # value = typedesc.UndefinedIdentifier(value) elif token.kind in [TokenKind.COMMENT, TokenKind.PUNCTUATION]: # noqa # log.debug("Ignored MACRO_DEFINITION token.kind: %s", token.kind.name) - pass + if final_value[-1] == "(" and value == ")": + final_value.pop() + value = None # add token if value is not None: diff --git a/test/test_macro.py b/test/test_macro.py index a38ac7b..150c5f5 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -713,6 +713,14 @@ def test_enum_token_pasting(self): self.assertIn("ret", self.namespace) self.assertEqual(self.namespace.ret, "mytextvalue") + def test_simple_func_define(self): + self.convert('''#define ELF32_ST_TYPE(val) ((val) & 0xf)''') + self.assertIn("ELF32_ST_TYPE", self.namespace) + + def test_simple_func_define_cast(self): + self.convert('''#define ELF32_ST_TYPE(val) ((unsigned char)(val) & 0xf)''') + self.assertIn("ELF32_ST_TYPE", self.namespace) + if __name__ == "__main__": import logging From 347d19cbcd1fc4fc7a2db852db651776b4a48560 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:07:33 +0000 Subject: [PATCH 03/14] better funcs --- ctypeslib/codegen/codegenerator.py | 9 +++++++-- ctypeslib/codegen/cursorhandler.py | 13 +++++++++++-- ctypeslib/codegen/typedesc.py | 3 ++- test/test_macro.py | 11 +++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 78c0dcb..5da3039 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -204,8 +204,13 @@ def Macro(self, macro): # is not code-generable ? # codegen should decide what codegen can do. if macro.args: - print("def %s%s: # macro" % (macro.name, macro.args), file=self.stream) - print(" return %s " % macro.body, file=self.stream) + all_known = all(x.name in self.names for x in macro.unknowns) + if all_known: + print("def %s%s: # macro" % (macro.name, macro.args), file=self.stream) + print(" return %s " % macro.body, file=self.stream) + else: + print("# def %s%s: # macro" % (macro.name, macro.args), file=self.stream) + print("# return %s " % macro.body, file=self.stream) elif util.contains_undefined_identifier(macro): # we can't handle that, we comment it out if isinstance(macro.body, typedesc.UndefinedIdentifier): diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index a027aef..c8c6a1c 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -1115,9 +1115,10 @@ def MACRO_DEFINITION(self, cursor): value = True # args should be filled when () are in tokens, args = None + unknowns = None if isinstance(tokens, list): # TODO, if there is an UndefinedIdentifier, we need to scrap the whole thing to comments. - # unknowns = [_ for _ in tokens if isinstance(_, typedesc.UndefinedIdentifier)] + unknowns = [_ for _ in tokens if isinstance(_, typedesc.UndefinedIdentifier)] # if len(unknowns) > 0: # value = tokens # elif len(tokens) == 2: @@ -1132,6 +1133,14 @@ def MACRO_DEFINITION(self, cursor): if any(filter(lambda x: isinstance(x, typedesc.UndefinedIdentifier), tokens)): # function macro or an expression. str_tokens = [str(_) for _ in tokens[1:tokens.index(')')+1]] + + for x in tokens[1:tokens.index(')')+1]: + if not isinstance(x, typedesc.UndefinedIdentifier): continue + rm = [] + for i in range(len(unknowns)): + if unknowns[i].name == x.name: rm.append(i) + for x in rm[::-1]: unknowns.pop(x) + args = ''.join(str_tokens).replace(',', ', ') str_tokens = [str(_) for _ in tokens[tokens.index(')')+1:]] value = ''.join(str_tokens) @@ -1157,7 +1166,7 @@ def MACRO_DEFINITION(self, cursor): if name in ['NULL', '__thread'] or value in ['__null', '__thread']: value = None log.debug('MACRO: #define %s%s %s', name, args or '', value) - obj = typedesc.Macro(name, args, value) + obj = typedesc.Macro(name, args, value, unknowns) try: self.register(name, obj) except DuplicateDefinitionException: diff --git a/ctypeslib/codegen/typedesc.py b/ctypeslib/codegen/typedesc.py index ca2d191..90e3d91 100644 --- a/ctypeslib/codegen/typedesc.py +++ b/ctypeslib/codegen/typedesc.py @@ -70,13 +70,14 @@ class Macro(T): """a C preprocessor definition with arguments""" - def __init__(self, name, args, body): + def __init__(self, name, args, body, unknowns=None): """all arguments are strings, args is the literal argument list *with* the parens around it: Example: Macro("CD_INDRIVE", "(status)", "((int)status > 0)")""" self.name = name self.args = args self.body = body + self.unknowns = unknowns class File(T): diff --git a/test/test_macro.py b/test/test_macro.py index 150c5f5..26aa497 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -721,6 +721,17 @@ def test_simple_func_define_cast(self): self.convert('''#define ELF32_ST_TYPE(val) ((unsigned char)(val) & 0xf)''') self.assertIn("ELF32_ST_TYPE", self.namespace) + def test_simple_func_define_unknown_type(self): + self.convert('''#define ELF32_ST_TYPE(val) MW((unsigned char)(val) & 0xf)''') + self.assertNotIn("ELF32_ST_TYPE", self.namespace) + + def test_simple_func_define_2_funcs(self): + self.convert(''' + #define one(val) ((val) & 0xf) + #define two(val) (one(val) + one(val * 2))''') + self.assertIn("one", self.namespace) + self.assertIn("two", self.namespace) + if __name__ == "__main__": import logging From a39d54e0cdfcb2ddc4bf654de147053dacd72154 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Tue, 13 Aug 2024 19:26:54 +0000 Subject: [PATCH 04/14] more funcs to pass --- ctypeslib/codegen/codegenerator.py | 2 +- ctypeslib/codegen/cursorhandler.py | 17 ++++++++++++++--- test/test_macro.py | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 5da3039..0331ee0 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -204,7 +204,7 @@ def Macro(self, macro): # is not code-generable ? # codegen should decide what codegen can do. if macro.args: - all_known = all(x.name in self.names for x in macro.unknowns) + all_known = all(x.name in self.names for x in macro.unknowns) and '?' not in macro.body if all_known: print("def %s%s: # macro" % (macro.name, macro.args), file=self.stream) print(" return %s " % macro.body, file=self.stream) diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index c8c6a1c..5ab4382 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -547,7 +547,10 @@ def _literal_handling(self, cursor): if self.is_registered(value): # FIXME: if Macro is not a simple value replace, it should not be registered in the first place # parse that, try to see if there is another Macro in there. - value = self.get_registered(value).body + if isinstance(self.get_registered(value), typedesc.Typedef): + value = None + else: + value = self.get_registered(value).body log.debug("Found MACRO_DEFINITION token identifier : %s", value) else: value = typedesc.UndefinedIdentifier(value) @@ -559,6 +562,8 @@ def _literal_handling(self, cursor): # value = typedesc.UndefinedIdentifier(value) elif token.kind in [TokenKind.COMMENT, TokenKind.PUNCTUATION]: # noqa # log.debug("Ignored MACRO_DEFINITION token.kind: %s", token.kind.name) + replacer = {"||": " or ", "&&": " and "} + value = replacer.get(value, value) if final_value[-1] == "(" and value == ")": final_value.pop() value = None @@ -1136,9 +1141,15 @@ def MACRO_DEFINITION(self, cursor): for x in tokens[1:tokens.index(')')+1]: if not isinstance(x, typedesc.UndefinedIdentifier): continue - rm = [] + rm, found = [], 0 for i in range(len(unknowns)): - if unknowns[i].name == x.name: rm.append(i) + if unknowns[i].name == x.name: + rm.append(i) + found += 1 + if found < 2: + log.debug(f'MACRO: skipping gen of {tokens}') + unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) + for x in rm[::-1]: unknowns.pop(x) args = ''.join(str_tokens).replace(',', ', ') diff --git a/test/test_macro.py b/test/test_macro.py index 26aa497..ba7a251 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -732,6 +732,20 @@ def test_simple_func_define_2_funcs(self): self.assertIn("one", self.namespace) self.assertIn("two", self.namespace) + def test_simple_func_define_ors(self): + self.convert('''#define one(val) ((val) || 0xf)''') + self.assertIn("one", self.namespace) + + def test_simple_func_define_tif(self): + self.convert('''#define one(val) ((val) ? 0xf : 0xd)''') + self.assertNotIn("one", self.namespace) + + def test_simple_func_define_cast_2(self): + # TODO: ignored for now... + self.convert(''' + typedef int NvUint; + #define one ((NvUint)(~0))''') + self.assertNotIn("one", self.namespace) if __name__ == "__main__": import logging From 7fe5b8f496b6e7bbd6148c0b9c080e59ec6f0030 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Tue, 13 Aug 2024 19:48:08 +0000 Subject: [PATCH 05/14] fix define to define --- ctypeslib/codegen/cursorhandler.py | 23 ++++++++++++++++++++--- test/test_macro.py | 7 +++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index 5ab4382..172a07d 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -481,9 +481,11 @@ def _literal_handling(self, cursor): value = self._clean_string_literal(cursor, value) return value final_value = [] + skip_till = None # code.interact(local=locals()) log.debug('cursor.type:%s', cursor.type.kind.name) for i, token in enumerate(tokens): + if skip_till is not None and i <= skip_till: continue value = token.spelling log.debug('token:%s tk.kd:%11s tk.cursor.kd:%15s cursor.kd:%15s', token.spelling, token.kind.name, token.cursor.kind.name, @@ -549,6 +551,20 @@ def _literal_handling(self, cursor): # parse that, try to see if there is another Macro in there. if isinstance(self.get_registered(value), typedesc.Typedef): value = None + elif isinstance(self.get_registered(value), typedesc.Macro): + if self.get_registered(value).args is None: value = self.get_registered(value).body + else: + pass + # value = self.get_registered(value).body + # skip_till = None + + # bal = 0 + # for j in range(i + 1, len(tokens)): + # if tokens[i].spelling == '(': bal+=1 + # if tokens[i].spelling == ')': bal-=1 + # if bal == 0: skip_till = j + # assert skip_till > i + 1 + # print(value) else: value = self.get_registered(value).body log.debug("Found MACRO_DEFINITION token identifier : %s", value) @@ -1157,13 +1173,14 @@ def MACRO_DEFINITION(self, cursor): value = ''.join(str_tokens) else: value = ''.join((str(_) for _ in tokens[1:tokens.index(')') + 1])) - elif len(tokens) > 2: + elif len(tokens) > 2 and len(unknowns) == 0 and ':' not in tokens: # #define key a b c - value = list(tokens[1:]) + # value = list(tokens[1:]) + value = ' '.join(tokens[1:]) else: # FIXME no reach ?! # just merge the list of tokens - value = ' '.join(tokens[1:]) + value = list(tokens[1:]) elif isinstance(tokens, str): # #define only value = True diff --git a/test/test_macro.py b/test/test_macro.py index ba7a251..384432f 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -726,6 +726,13 @@ def test_simple_func_define_unknown_type(self): self.assertNotIn("ELF32_ST_TYPE", self.namespace) def test_simple_func_define_2_funcs(self): + self.convert(''' + #define one(val) ((val) & 0xf) + #define two(val) one (val)''') + self.assertIn("one", self.namespace) + self.assertIn("two", self.namespace) + + def test_simple_func_define_2_funcs_2(self): self.convert(''' #define one(val) ((val) & 0xf) #define two(val) (one(val) + one(val * 2))''') From 1fe4631db436e1f659639290baf9fad06e097e75 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Wed, 14 Aug 2024 06:58:30 +0000 Subject: [PATCH 06/14] handling many inputs in funcs --- ctypeslib/codegen/cursorhandler.py | 2 +- test/test_macro.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index 172a07d..9c0d39d 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -1166,7 +1166,7 @@ def MACRO_DEFINITION(self, cursor): log.debug(f'MACRO: skipping gen of {tokens}') unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) - for x in rm[::-1]: unknowns.pop(x) + for x in rm[::-1]: unknowns.pop(x) args = ''.join(str_tokens).replace(',', ', ') str_tokens = [str(_) for _ in tokens[tokens.index(')')+1:]] diff --git a/test/test_macro.py b/test/test_macro.py index 384432f..58ffb07 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -739,6 +739,28 @@ def test_simple_func_define_2_funcs_2(self): self.assertIn("one", self.namespace) self.assertIn("two", self.namespace) + def test_simple_func_define_2_funcs_3(self): + self.convert(''' + # define UVM_IOCTL_BASE(i) i + #define UVM_RESERVE_VA UVM_IOCTL_BASE(1)''') + self.assertIn("UVM_IOCTL_BASE", self.namespace) + self.assertIn("UVM_RESERVE_VA", self.namespace) + + def test_simple_func_define_several(self): + self.convert(''' + #define CL_RELEASE_VERSION_CURRENT 6 + #define CL_MAJOR_VERSION_CURRENT 0 + #define CL_MINOR_VERSION_CURRENT 0 + #define CL_VERSION_CODE(rel, major, minor) ( ( ( (rel) << 16) ) | ( ( (major) << 8 ) ) | ( (minor) ) ) + #define CL_VERSION_CURRENT CL_VERSION_CODE(CL_RELEASE_VERSION_CURRENT, \ + CL_MAJOR_VERSION_CURRENT, \ + CL_MINOR_VERSION_CURRENT) + + #define CL_VERSION_RELEASE(version) ( ((version) >> 16) & 0xff )''') + self.assertIn("CL_VERSION_CODE", self.namespace) + self.assertIn("CL_VERSION_CURRENT", self.namespace) + self.assertIn("CL_VERSION_RELEASE", self.namespace) + def test_simple_func_define_ors(self): self.convert('''#define one(val) ((val) || 0xf)''') self.assertIn("one", self.namespace) From 4084b03c04017ac2e8f8fd2d0cc051d127a84524 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Wed, 14 Aug 2024 07:55:31 +0000 Subject: [PATCH 07/14] toposort --- ctypeslib/codegen/clangparser.py | 40 ++++++++++++++++++++++++++++-- ctypeslib/codegen/cursorhandler.py | 20 ++++++++++----- ctypeslib/codegen/util.py | 24 ++++++++++++++++++ test/test_macro.py | 13 ++++++++++ 4 files changed, 89 insertions(+), 8 deletions(-) diff --git a/ctypeslib/codegen/clangparser.py b/ctypeslib/codegen/clangparser.py index 5c92a06..af784db 100644 --- a/ctypeslib/codegen/clangparser.py +++ b/ctypeslib/codegen/clangparser.py @@ -419,5 +419,41 @@ def get_result(self): if isinstance(i, interesting): result.append(i) - log.debug("parsed items order: %s", result) - return result + # toposort them + name2i = {} + for i,r in enumerate(result): + name2i[r.name] = i + if isinstance(r, typedesc.Enumeration): + for v in r.values: + name2i[v.name] = i + + edges = [list() for i in range(len(result))] + for i,r in enumerate(result): + x = util.all_undefined_identifier(r) + for x in util.all_undefined_identifier(r): + if x.name in name2i: + edges[name2i[x.name]].append(i) + + inDegree = [0] * len(result) + for i in range(len(result)): + for j in edges[i]: + inDegree[j] += 1 + + frontier = [] + for i in range(len(result)): + if inDegree[i] == 0: + frontier.append(i) + + order = [] + while frontier: + i = min(frontier) + order.append(i) + frontier.remove(i) + for j in edges[i]: + inDegree[j] -= 1 + if inDegree[j] == 0: + frontier.append(j) + + final_result = [result[i] for i in order] + log.debug("parsed items order: %s", final_result) + return final_result diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index 9c0d39d..4b43c58 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -1151,10 +1151,19 @@ def MACRO_DEFINITION(self, cursor): elif tokens[1] == '(': # #107, differentiate between function-like macro and expression in () # valid tokens for us are are '()[0-9],.e' and terminating LluU - if any(filter(lambda x: isinstance(x, typedesc.UndefinedIdentifier), tokens)): + args_tokens = [str(_) for _ in tokens[1:tokens.index(')')+1]] + body_tokens = [str(_) for _ in tokens[tokens.index(')')+1:]] + + bal = 0 + body_tokens_good = len(body_tokens) > 0 + for t in body_tokens: + if t == '(': bal += 1 + if t == ')': bal -= 1 + if bal < 0: body_tokens_good = False + body_tokens_good &= (bal == 0) + + if any(filter(lambda x: isinstance(x, typedesc.UndefinedIdentifier), tokens)) and body_tokens_good: # function macro or an expression. - str_tokens = [str(_) for _ in tokens[1:tokens.index(')')+1]] - for x in tokens[1:tokens.index(')')+1]: if not isinstance(x, typedesc.UndefinedIdentifier): continue rm, found = [], 0 @@ -1168,9 +1177,8 @@ def MACRO_DEFINITION(self, cursor): for x in rm[::-1]: unknowns.pop(x) - args = ''.join(str_tokens).replace(',', ', ') - str_tokens = [str(_) for _ in tokens[tokens.index(')')+1:]] - value = ''.join(str_tokens) + args = ''.join(args_tokens).replace(',', ', ') + value = ''.join(body_tokens) else: value = ''.join((str(_) for _ in tokens[1:tokens.index(')') + 1])) elif len(tokens) > 2 and len(unknowns) == 0 and ':' not in tokens: diff --git a/ctypeslib/codegen/util.py b/ctypeslib/codegen/util.py index e3e250f..ca421f6 100644 --- a/ctypeslib/codegen/util.py +++ b/ctypeslib/codegen/util.py @@ -170,6 +170,30 @@ def _list_contains_undefined_identifier(l): return False +def all_undefined_identifier(macro): + rres = [] + if isinstance(macro, typedesc.Macro) and macro.unknowns is not None: rres += macro.unknowns + if not hasattr(macro, 'body'): return rres + + # body is undefined + if isinstance(macro.body, typedesc.UndefinedIdentifier): + return rres + [macro.body] + + def _list_contains_undefined_identifier(l): + res = [] + for b in l: + if isinstance(b, typedesc.UndefinedIdentifier): + res += [b] + if isinstance(b, list): + res += all_undefined_identifier(b) + return res + + # or one item is undefined + if isinstance(macro.body, list): + return _list_contains_undefined_identifier(macro.body) + + return rres + def token_is_string(token): # we need at list 2 delimiters in there diff --git a/test/test_macro.py b/test/test_macro.py index 58ffb07..15c8d87 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -761,6 +761,19 @@ def test_simple_func_define_several(self): self.assertIn("CL_VERSION_CURRENT", self.namespace) self.assertIn("CL_VERSION_RELEASE", self.namespace) + def test_simple_func_broken_body(self): + self.convert(''' + enum { + IOSQE_FIXED_FILE_BIT + }; + #define IOSQE_FIXED_FILE (1U << IOSQE_FIXED_FILE_BIT)''') + self.assertIn("IOSQE_FIXED_FILE_BIT", self.namespace) + self.assertIn("IOSQE_FIXED_FILE", self.namespace) + + def test_simple_func_broken_body_2(self): + self.convert('''extern int mprotect (void *__addr, int __len, int __prot);''') + self.assertIn("mprotect", self.namespace) + def test_simple_func_define_ors(self): self.convert('''#define one(val) ((val) || 0xf)''') self.assertIn("one", self.namespace) From c715d7a0a3575d79dbc7efe27204f9203bd36d4e Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Wed, 14 Aug 2024 08:27:43 +0000 Subject: [PATCH 08/14] fixes --- ctypeslib/codegen/codegenerator.py | 11 ++++++----- ctypeslib/codegen/cursorhandler.py | 10 +++++++++- test/test_macro.py | 4 ++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 0331ee0..a39f83f 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -203,20 +203,21 @@ def Macro(self, macro): # 2. or get a flag in macro that tells us if something contains undefinedIdentifier # is not code-generable ? # codegen should decide what codegen can do. + all_known = (all(x.name in self.names for x in macro.unknowns) if macro.unknowns is not None else True) + all_known &= ('?' not in macro.body if isinstance(macro.body, list) or isinstance(macro.body, str) else True) if macro.args: - all_known = all(x.name in self.names for x in macro.unknowns) and '?' not in macro.body if all_known: print("def %s%s: # macro" % (macro.name, macro.args), file=self.stream) print(" return %s " % macro.body, file=self.stream) else: print("# def %s%s: # macro" % (macro.name, macro.args), file=self.stream) print("# return %s " % macro.body, file=self.stream) - elif util.contains_undefined_identifier(macro): + elif util.contains_undefined_identifier(macro) or not all_known: # we can't handle that, we comment it out - if isinstance(macro.body, typedesc.UndefinedIdentifier): - print("# %s = %s # macro" % (macro.name, macro.body.name), file=self.stream) - else: # we assume it's a list + if isinstance(macro.body, list): print("# %s = %s # macro" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream) + else: + print("# %s = %s # macro" % (macro.name, macro.body), file=self.stream) elif isinstance(macro.body, bool): print("%s = %s # macro" % (macro.name, macro.body), file=self.stream) self.macros += 1 diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index 4b43c58..c7ceca6 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -574,7 +574,8 @@ def _literal_handling(self, cursor): pass elif token.kind == TokenKind.KEYWORD: # noqa log.debug("Got a MACRO_DEFINITION referencing a KEYWORD token.kind: %s", token.kind.name) - value=None # HACK: remove casts + replacer = {"void": "void"} + value=replacer.get(value, None) # value = typedesc.UndefinedIdentifier(value) elif token.kind in [TokenKind.COMMENT, TokenKind.PUNCTUATION]: # noqa # log.debug("Ignored MACRO_DEFINITION token.kind: %s", token.kind.name) @@ -1179,6 +1180,13 @@ def MACRO_DEFINITION(self, cursor): args = ''.join(args_tokens).replace(',', ', ') value = ''.join(body_tokens) + elif not body_tokens_good: + value = ''.join((str(_) for _ in tokens[1:])) + if value.find("u64") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) + if value.find("u32") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) + if value.find("i64") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) + if value.find("i32") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) + if value.find("void*") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) else: value = ''.join((str(_) for _ in tokens[1:tokens.index(')') + 1])) elif len(tokens) > 2 and len(unknowns) == 0 and ':' not in tokens: diff --git a/test/test_macro.py b/test/test_macro.py index 15c8d87..a737d6d 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -774,6 +774,10 @@ def test_simple_func_broken_body_2(self): self.convert('''extern int mprotect (void *__addr, int __len, int __prot);''') self.assertIn("mprotect", self.namespace) + def test_simple_func_broken_body_3(self): + self.convert('''#define NV2080_GPUMON_PID_INVALID ((NvU32)(~0))''') + self.assertNotIn("NV2080_GPUMON_PID_INVALID", self.namespace) + def test_simple_func_define_ors(self): self.convert('''#define one(val) ((val) || 0xf)''') self.assertIn("one", self.namespace) From 010a6123bef80e6d7a092b26f0e3afb19d9f82e9 Mon Sep 17 00:00:00 2001 From: nimlgen Date: Wed, 14 Aug 2024 06:23:33 -0700 Subject: [PATCH 09/14] ioctl support --- atom.mk | 1 + ctypeslib/codegen/codegenerator.py | 16 ++++++++++++++-- ctypeslib/codegen/cursorhandler.py | 8 ++++++-- ctypeslib/data/ioctl.tpl | 12 ++++++++++++ pyproject.toml | 1 + test/test_macro.py | 11 +++++++++++ 6 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 ctypeslib/data/ioctl.tpl diff --git a/atom.mk b/atom.mk index 4bf612c..69a8ab3 100644 --- a/atom.mk +++ b/atom.mk @@ -17,6 +17,7 @@ LOCAL_COPY_FILES := \ ctypeslib/codegen/util.py:usr/lib/python/site-packages/ctypeslib/codegen/ \ ctypeslib/data/fundamental_type_name.tpl:usr/lib/python/site-packages/ctypeslib/data/ \ ctypeslib/data/headers.tpl:usr/lib/python/site-packages/ctypeslib/data/ \ + ctypeslib/data/ioctl.tpl:usr/lib/python/site-packages/ctypeslib/data/ \ ctypeslib/data/pointer_type.tpl:usr/lib/python/site-packages/ctypeslib/data/ \ ctypeslib/data/string_cast.tpl:usr/lib/python/site-packages/ctypeslib/data/ \ ctypeslib/data/structure_type.tpl:usr/lib/python/site-packages/ctypeslib/data/ \ diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index a39f83f..f3955e4 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -95,6 +95,12 @@ def enable_structure_type(self): headers = pkgutil.get_data("ctypeslib", "data/structure_type.tpl").decode() print(headers, file=self.imports) + def enable_ioctl(self): + self.enable_ioctl = lambda: True + headers = pkgutil.get_data("ctypeslib", "data/ioctl.tpl").decode() + self.names += ["_IO", "_IOR", "_IOW", "_IOWR"] + print(headers, file=self.imports) + def enable_string_cast(self): """ If a structure type is used, declare our ctypes.Structure extension type @@ -203,7 +209,11 @@ def Macro(self, macro): # 2. or get a flag in macro that tells us if something contains undefinedIdentifier # is not code-generable ? # codegen should decide what codegen can do. - all_known = (all(x.name in self.names for x in macro.unknowns) if macro.unknowns is not None else True) + unknowns = util.all_undefined_identifier(macro) + for x in unknowns: + if x.name in {"_IO", "_IOW", "_IOR", "_IOWR"}: self.enable_ioctl() + + all_known = all(x.name in self.names for x in unknowns) all_known &= ('?' not in macro.body if isinstance(macro.body, list) or isinstance(macro.body, str) else True) if macro.args: if all_known: @@ -212,12 +222,14 @@ def Macro(self, macro): else: print("# def %s%s: # macro" % (macro.name, macro.args), file=self.stream) print("# return %s " % macro.body, file=self.stream) - elif util.contains_undefined_identifier(macro) or not all_known: + elif not all_known: # we can't handle that, we comment it out if isinstance(macro.body, list): print("# %s = %s # macro" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream) else: print("# %s = %s # macro" % (macro.name, macro.body), file=self.stream) + elif isinstance(macro.body, list): + print("%s = %s # macro from list" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream) elif isinstance(macro.body, bool): print("%s = %s # macro" % (macro.name, macro.body), file=self.stream) self.macros += 1 diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index c7ceca6..c658517 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -546,6 +546,10 @@ def _literal_handling(self, cursor): elif token.kind == TokenKind.IDENTIFIER: # noqa # log.debug("Ignored MACRO_DEFINITION token identifier : %s", value) # Identifier in Macro... Not sure what to do with that. + if i > 0 and tokens[i-1].spelling == "struct": + final_value.pop() + value = "struct_" + value + if self.is_registered(value): # FIXME: if Macro is not a simple value replace, it should not be registered in the first place # parse that, try to see if there is another Macro in there. @@ -574,7 +578,7 @@ def _literal_handling(self, cursor): pass elif token.kind == TokenKind.KEYWORD: # noqa log.debug("Got a MACRO_DEFINITION referencing a KEYWORD token.kind: %s", token.kind.name) - replacer = {"void": "void"} + replacer = {"void": "void", "struct": "struct"} value=replacer.get(value, None) # value = typedesc.UndefinedIdentifier(value) elif token.kind in [TokenKind.COMMENT, TokenKind.PUNCTUATION]: # noqa @@ -1214,7 +1218,7 @@ def MACRO_DEFINITION(self, cursor): try: self.register(name, obj) except DuplicateDefinitionException: - log.info('Redefinition of %s %s->%s', name, self.parser.all[name].args, value) + log.info('Redefinition of %s %s->%s', name, getattr(self.parser.all[name], 'args', ''), value) # HACK self.parser.all[name] = obj self.set_location(obj, cursor) diff --git a/ctypeslib/data/ioctl.tpl b/ctypeslib/data/ioctl.tpl new file mode 100644 index 0000000..e9cf83d --- /dev/null +++ b/ctypeslib/data/ioctl.tpl @@ -0,0 +1,12 @@ + +import fcntl, functools + +def _do_ioctl(__idir, __base, __nr, __user_struct, __fd, **kwargs): + ret = fcntl.ioctl(__fd, (__idir<<30) | (ctypes.sizeof(made := __user_struct(**kwargs))<<16) | (__base<<8) | __nr, made) + if ret != 0: raise RuntimeError(f"ioctl returned {ret}") + return made + +def _IO(base, nr): return functools.partial(_do_ioctl, 0, ord(base) if isinstance(base, str) else base, nr, None) +def _IOW(base, nr, type): return functools.partial(_do_ioctl, 1, ord(base) if isinstance(base, str) else base, nr, type) +def _IOR(base, nr, type): return functools.partial(_do_ioctl, 2, ord(base) if isinstance(base, str) else base, nr, type) +def _IOWR(base, nr, type): return functools.partial(_do_ioctl, 3, ord(base) if isinstance(base, str) else base, nr, type) diff --git a/pyproject.toml b/pyproject.toml index 41e6747..a156516 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ include-package-data = false ctypeslib = [ "data/fundamental_type_name.tpl", "data/headers.tpl", + "data/ioctl.tpl", "data/pointer_type.tpl", "data/string_cast.tpl", "data/structure_type.tpl", diff --git a/test/test_macro.py b/test/test_macro.py index a737d6d..21b991b 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -793,6 +793,17 @@ def test_simple_func_define_cast_2(self): #define one ((NvUint)(~0))''') self.assertNotIn("one", self.namespace) + def test_simple_func_ioctls(self): + self.convert('''#define AMDKFD_IO(nr) _IO(AMDKFD_IOCTL_BASE, nr) + #define AMDKFD_IOR(nr, type) _IOR(AMDKFD_IOCTL_BASE, nr, type) + #define AMDKFD_IOW(nr, type) _IOW(AMDKFD_IOCTL_BASE, nr, type) + #define AMDKFD_IOWR(nr, type) _IOWR(AMDKFD_IOCTL_BASE, nr, type) + + #define AMDKFD_IOC_GET_VERSION \ + AMDKFD_IOR(0x01, struct kfd_ioctl_get_version_args)''') + self.assertNotIn("one", self.namespace) + + if __name__ == "__main__": import logging logging.basicConfig(level=logging.DEBUG) From ab178f6eb875848cc5c6dfeda9175b34d839de93 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:18:42 +0000 Subject: [PATCH 10/14] fix forbidden chars --- ctypeslib/codegen/codegenerator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index f3955e4..9236616 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -214,7 +214,8 @@ def Macro(self, macro): if x.name in {"_IO", "_IOW", "_IOR", "_IOWR"}: self.enable_ioctl() all_known = all(x.name in self.names for x in unknowns) - all_known &= ('?' not in macro.body if isinstance(macro.body, list) or isinstance(macro.body, str) else True) + if isinstance(macro.body, list) or isinstance(macro.body, str): + for b in ['?', ':']: all_known &= b not in macro.body if macro.args: if all_known: print("def %s%s: # macro" % (macro.name, macro.args), file=self.stream) From f828f0e019c268e71e69a16d78ff44fcbce7035b Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:17:08 +0000 Subject: [PATCH 11/14] correct redefine --- ctypeslib/codegen/codegenerator.py | 4 ++-- ctypeslib/codegen/cursorhandler.py | 19 ++++--------------- ctypeslib/codegen/typedesc.py | 3 ++- test/test_macro.py | 8 ++++++++ 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 9236616..440b1bb 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -226,11 +226,11 @@ def Macro(self, macro): elif not all_known: # we can't handle that, we comment it out if isinstance(macro.body, list): - print("# %s = %s # macro" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream) + print("# %s = %s # macro" % (macro.name, "".join([str(_) for _ in macro.body])), file=self.stream) else: print("# %s = %s # macro" % (macro.name, macro.body), file=self.stream) elif isinstance(macro.body, list): - print("%s = %s # macro from list" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream) + print("%s = %s # macro (from list)" % (macro.name, "".join([str(_) for _ in macro.body])), file=self.stream) elif isinstance(macro.body, bool): print("%s = %s # macro" % (macro.name, macro.body), file=self.stream) self.macros += 1 diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index c658517..ed4da1c 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -556,19 +556,7 @@ def _literal_handling(self, cursor): if isinstance(self.get_registered(value), typedesc.Typedef): value = None elif isinstance(self.get_registered(value), typedesc.Macro): - if self.get_registered(value).args is None: value = self.get_registered(value).body - else: - pass - # value = self.get_registered(value).body - # skip_till = None - - # bal = 0 - # for j in range(i + 1, len(tokens)): - # if tokens[i].spelling == '(': bal+=1 - # if tokens[i].spelling == ')': bal-=1 - # if bal == 0: skip_till = j - # assert skip_till > i + 1 - # print(value) + if self.get_registered(value).args is None: value = self.get_registered(value).toknes else: value = self.get_registered(value).body log.debug("Found MACRO_DEFINITION token identifier : %s", value) @@ -578,7 +566,7 @@ def _literal_handling(self, cursor): pass elif token.kind == TokenKind.KEYWORD: # noqa log.debug("Got a MACRO_DEFINITION referencing a KEYWORD token.kind: %s", token.kind.name) - replacer = {"void": "void", "struct": "struct"} + replacer = {"void": "void", "struct": "struct", "sizeof": "sizeof"} value=replacer.get(value, None) # value = typedesc.UndefinedIdentifier(value) elif token.kind in [TokenKind.COMMENT, TokenKind.PUNCTUATION]: # noqa @@ -1191,6 +1179,7 @@ def MACRO_DEFINITION(self, cursor): if value.find("i64") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) if value.find("i32") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) if value.find("void*") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) + if value.find("sizeof") >= 0: unknowns.append(typedesc.UndefinedIdentifier("mark_as_broken")) else: value = ''.join((str(_) for _ in tokens[1:tokens.index(')') + 1])) elif len(tokens) > 2 and len(unknowns) == 0 and ':' not in tokens: @@ -1214,7 +1203,7 @@ def MACRO_DEFINITION(self, cursor): if name in ['NULL', '__thread'] or value in ['__null', '__thread']: value = None log.debug('MACRO: #define %s%s %s', name, args or '', value) - obj = typedesc.Macro(name, args, value, unknowns) + obj = typedesc.Macro(name, args, value, tokens[1:], unknowns) try: self.register(name, obj) except DuplicateDefinitionException: diff --git a/ctypeslib/codegen/typedesc.py b/ctypeslib/codegen/typedesc.py index 90e3d91..cd5960e 100644 --- a/ctypeslib/codegen/typedesc.py +++ b/ctypeslib/codegen/typedesc.py @@ -70,13 +70,14 @@ class Macro(T): """a C preprocessor definition with arguments""" - def __init__(self, name, args, body, unknowns=None): + def __init__(self, name, args, body, toknes, unknowns=None): """all arguments are strings, args is the literal argument list *with* the parens around it: Example: Macro("CD_INDRIVE", "(status)", "((int)status > 0)")""" self.name = name self.args = args self.body = body + self.toknes = toknes self.unknowns = unknowns diff --git a/test/test_macro.py b/test/test_macro.py index 21b991b..f78df45 100644 --- a/test/test_macro.py +++ b/test/test_macro.py @@ -786,6 +786,14 @@ def test_simple_func_define_tif(self): self.convert('''#define one(val) ((val) ? 0xf : 0xd)''') self.assertNotIn("one", self.namespace) + def test_simple_func_define_order(self): + self.convert('''#define ION_FLAG_SECURE (1 << ION_HEAP_ID_RESERVED) + #define ION_SECURE ION_FLAG_SECURE + enum {ION_HEAP_ID_RESERVED = 31};''') + self.assertIn("ION_SECURE", self.namespace) + self.assertIn("ION_FLAG_SECURE", self.namespace) + print(self.text_output) + def test_simple_func_define_cast_2(self): # TODO: ignored for now... self.convert(''' From 44f1e123d63ae05f1336b09cabd49c403a812c28 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:39:15 +0000 Subject: [PATCH 12/14] correct sizeof --- ctypeslib/codegen/codegenerator.py | 4 ++-- ctypeslib/codegen/cursorhandler.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 440b1bb..1ac0637 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -226,11 +226,11 @@ def Macro(self, macro): elif not all_known: # we can't handle that, we comment it out if isinstance(macro.body, list): - print("# %s = %s # macro" % (macro.name, "".join([str(_) for _ in macro.body])), file=self.stream) + print("# %s = %s # macro" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream) else: print("# %s = %s # macro" % (macro.name, macro.body), file=self.stream) elif isinstance(macro.body, list): - print("%s = %s # macro (from list)" % (macro.name, "".join([str(_) for _ in macro.body])), file=self.stream) + print("%s = %s # macro (from list)" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream) elif isinstance(macro.body, bool): print("%s = %s # macro" % (macro.name, macro.body), file=self.stream) self.macros += 1 diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index ed4da1c..7aab442 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -566,7 +566,7 @@ def _literal_handling(self, cursor): pass elif token.kind == TokenKind.KEYWORD: # noqa log.debug("Got a MACRO_DEFINITION referencing a KEYWORD token.kind: %s", token.kind.name) - replacer = {"void": "void", "struct": "struct", "sizeof": "sizeof"} + replacer = {"void": "void", "struct": "struct", "sizeof": "ctypes.sizeof"} value=replacer.get(value, None) # value = typedesc.UndefinedIdentifier(value) elif token.kind in [TokenKind.COMMENT, TokenKind.PUNCTUATION]: # noqa @@ -579,7 +579,8 @@ def _literal_handling(self, cursor): # add token if value is not None: - final_value.append(value) + if isinstance(value, list): final_value += value + else: final_value.append(value) # return the EXPR # code.interact(local=locals()) # FIXME, that will break. We need constant type return From 506979597c39dcd4167ed958835f139efed4cae0 Mon Sep 17 00:00:00 2001 From: uuuvn <83587632+uuuvn@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:39:59 +0200 Subject: [PATCH 13/14] Ordered set (#1) --- ctypeslib/codegen/clangparser.py | 5 +++-- ctypeslib/codegen/codegenerator.py | 7 ++++--- ctypeslib/oset.py | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 ctypeslib/oset.py diff --git a/ctypeslib/codegen/clangparser.py b/ctypeslib/codegen/clangparser.py index af784db..fef5287 100644 --- a/ctypeslib/codegen/clangparser.py +++ b/ctypeslib/codegen/clangparser.py @@ -11,6 +11,7 @@ from ctypeslib.codegen import typedesc from ctypeslib.codegen import typehandler from ctypeslib.codegen import util +from ctypeslib.oset import OSet from ctypeslib.codegen.handler import DuplicateDefinitionException from ctypeslib.codegen.handler import InvalidDefinitionError from ctypeslib.codegen.handler import InvalidTranslationUnitException @@ -70,7 +71,7 @@ class Clang_Parser: def __init__(self, flags): self.all = collections.OrderedDict() # a shortcut to identify registered decl in cases of records - self.all_set = set() + self.all_set = OSet() self.cpp_data = {} self._unhandled = [] self.fields = {} @@ -83,7 +84,7 @@ def __init__(self, flags): self.cursorkind_handler = cursorhandler.CursorHandler(self) self.typekind_handler = typehandler.TypeHandler(self) self.__filter_location = None - self.__processed_location = set() + self.__processed_location = OSet() def init_parsing_options(self): """Set the Translation Unit to skip functions bodies per default.""" diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 1ac0637..100403b 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -22,6 +22,7 @@ from ctypeslib.codegen import config from ctypeslib.codegen import typedesc from ctypeslib.codegen import util +from ctypeslib.oset import OSet from ctypeslib.library import Library log = logging.getLogger("codegen") @@ -50,8 +51,8 @@ def __init__(self, output, cfg): self.macros = 0 self.cross_arch_code_generation = cfg.cross_arch # what record dependency were generated - self.head_generated = set() - self.body_generated = set() + self.head_generated = OSet() + self.body_generated = OSet() # pylint: disable=method-hidden def enable_fundamental_type_wrappers(self): @@ -570,7 +571,7 @@ def Structure(self, struct): return self.enable_structure_type() self._structures += 1 - depends = set() + depends = OSet() # We only print a empty struct. if struct.members is None: log.info("No members for: %s", struct.name) diff --git a/ctypeslib/oset.py b/ctypeslib/oset.py new file mode 100644 index 0000000..bd976f5 --- /dev/null +++ b/ctypeslib/oset.py @@ -0,0 +1,18 @@ +class OSet: + def __init__(self): + self.dict = {} + def add(self, v): + self.dict[v] = None + def update(self, other): + self.dict.update({k: None for k in other}) + def remove(self, v): + del self.dict[v] + def discard(self, v): + if v in self.dict: + del self.dict[v] + def __contains__(self, item): + return item in self.dict + def __iter__(self): + return iter(self.dict.keys()) + def __len__(self): + return len(self.dict) From 4deef848b311a46457b1978a1da37662ebb2ce33 Mon Sep 17 00:00:00 2001 From: nimlgen <138685161+nimlgen@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:30:48 +0000 Subject: [PATCH 14/14] 314 --- ctypeslib/data/structure_type.tpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ctypeslib/data/structure_type.tpl b/ctypeslib/data/structure_type.tpl index 5c08ffb..099f805 100644 --- a/ctypeslib/data/structure_type.tpl +++ b/ctypeslib/data/structure_type.tpl @@ -1,4 +1,6 @@ class AsDictMixin: + import sys + if sys.version_info >= (3, 14): _layout_ = 'ms' @classmethod def as_dict(cls, self): result = {}