From 13594f9bfeca92f1f111b2144c5aa47d6e7c3473 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Wed, 17 Dec 2025 00:03:48 +0800 Subject: [PATCH] improve ffigen 2 --- ffigen/ffigen/converters.py | 5 ++-- ffigen/ffigen/library.py | 18 ++++++------ ffigen/ffigen/meta/parser.py | 15 ++++++---- ffigen/ffigen/meta/schema.py | 3 +- ffigen/ffigen/schema.py | 17 ++++++++++- ffigen/ffigen/struct.py | 56 ++++++++++++++---------------------- ffigen/gen_periphery.py | 2 +- 7 files changed, 59 insertions(+), 57 deletions(-) diff --git a/ffigen/ffigen/converters.py b/ffigen/ffigen/converters.py index d935fd8e..40819607 100644 --- a/ffigen/ffigen/converters.py +++ b/ffigen/ffigen/converters.py @@ -165,9 +165,8 @@ def set_vmath_converter(T: str, py_T: str): def has_vmath_converter() -> bool: return _has_vmath_converter -def set_enum_converters(enums: list[str]): - for T in enums: - _CONVERTERS[T] = EnumConverter(T) +def set_enum_converter(T: str): + _CONVERTERS[T] = EnumConverter(T) def get_converter(T: str) -> Converter: if T in _CONVERTERS: diff --git a/ffigen/ffigen/library.py b/ffigen/ffigen/library.py index 92633e8d..7177302d 100644 --- a/ffigen/ffigen/library.py +++ b/ffigen/ffigen/library.py @@ -3,7 +3,7 @@ from .writer import Writer from .enum import gen_enum from .struct import gen_struct from .function import gen_function -from .converters import is_vmath_type, has_vmath_converter +from .converters import is_vmath_type, has_vmath_converter, set_enum_converter from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -31,6 +31,11 @@ class Library: def build(self, *, glue_dir='.', stub_dir='.', includes: list[str] | None = None): self.remove_unsupported() + for k in self.aliases.keys(): + node = self.unalias(k) + if isinstance(node, c_ast.Enum): + set_enum_converter(k) + w, pyi_w = Writer(), Writer() if has_vmath_converter(): @@ -50,15 +55,6 @@ class Library: w.write('') w.write('#define ADD_ENUM(name) py_newint(py_emplacedict(mod, py_name(#name)), name)') w.write('') - w.write('static bool struct__address__(int argc, py_Ref argv) {') - w.indent() - w.write('PY_CHECK_ARGC(1);') - w.write('void* ud = py_touserdata(argv);') - w.write('py_newint(py_retval(), (py_i64)ud);') - w.write('return true;') - w.dedent() - w.write('}') - w.write('') for k in self.aliases.keys(): node = self.unalias(k) @@ -135,6 +131,7 @@ class Library: continue self.structs.append(Struct( name=struct['name'], + typedef_name=struct['name'], desc=struct['description'], fields=[StructField( type=field['type'], @@ -182,6 +179,7 @@ class Library: assert fields is not None self.structs.append(Struct( name=type.name, + typedef_name=type.typedef_name, fields=[StructField( type=field_type, name=field_name diff --git a/ffigen/ffigen/meta/parser.py b/ffigen/ffigen/meta/parser.py index f7ecf970..6456f5e8 100644 --- a/ffigen/ffigen/meta/parser.py +++ b/ffigen/ffigen/meta/parser.py @@ -31,13 +31,13 @@ class Header: return self.type_aliases[k] = v - def build_enum(self, node: c_ast.Enum): + def build_enum(self, node: c_ast.Enum, alias_name: str | None = None): enum = Enum(node.name) for item in node.values.enumerators: enum.values.append(item.name) self.types.append(enum) - def build_struct(self, node): + def build_struct(self, node: c_ast.Struct | c_ast.Union, alias_name: str | None = None): if isinstance(node, c_ast.Struct): cls = Struct elif isinstance(node, c_ast.Union): @@ -53,17 +53,17 @@ class Header: fields[name] = type except UnsupportedNode: pass - self.types.append(cls(node.name, fields)) + self.types.append(cls(node.name, fields, alias_name)) else: - self.types.append(cls(node.name, None)) + self.types.append(cls(node.name, None, alias_name)) def build_type(self, node, alias_name): if isinstance(node, c_ast.Enum): - self.build_enum(node) + self.build_enum(node, alias_name) if alias_name: self.add_type_alias(alias_name, node) elif isinstance(node, (c_ast.Struct, c_ast.Union)): - self.build_struct(node) + self.build_struct(node, alias_name) if alias_name: self.add_type_alias(alias_name, node) elif isinstance(node, c_ast.IdentifierType): @@ -132,6 +132,9 @@ class Header: assert isinstance(node, c_ast.TypeDecl), type(node) name = node.declname ret = node.type.names[0] + is_const = node.quals and 'const' in node.quals + if is_const: + ret = 'const ' + ret func = Function(name, ret + '*' * level) if args is not None: for param in args.params: diff --git a/ffigen/ffigen/meta/schema.py b/ffigen/ffigen/meta/schema.py index 3e04714d..3157f4af 100644 --- a/ffigen/ffigen/meta/schema.py +++ b/ffigen/ffigen/meta/schema.py @@ -7,9 +7,10 @@ class Pointer: self.level = level class NamedFields: - def __init__(self, name: str, fields: dict[str, str] | None): + def __init__(self, name: str, fields: dict[str, str] | None, typedef_name: str | None = None): self.name = name self.fields = fields + self.typedef_name = typedef_name def is_opaque(self): return self.fields is None diff --git a/ffigen/ffigen/schema.py b/ffigen/ffigen/schema.py index 9c555df4..2e396860 100644 --- a/ffigen/ffigen/schema.py +++ b/ffigen/ffigen/schema.py @@ -15,10 +15,25 @@ class EnumValue: @dataclass class Struct: - name: str + name: str | None = None + typedef_name: str | None = None desc: str | None = None fields: list[StructField] | None = None + @property + def code_name(self): + if self.typedef_name: + return self.typedef_name + assert self.name is not None + return f'struct {self.name}' + + @property + def identifier(self): + if self.name: + return self.name + assert self.typedef_name is not None + return self.typedef_name + @dataclass class Enum: name: str diff --git a/ffigen/ffigen/struct.py b/ffigen/ffigen/struct.py index c89a3661..7ab476cd 100644 --- a/ffigen/ffigen/struct.py +++ b/ffigen/ffigen/struct.py @@ -2,21 +2,21 @@ from .writer import Writer from .converters import get_converter, Converter from .schema import Struct, StructField -def gen_getter(w: Writer, name: str, cvt: Converter, field: StructField): - w.write(f'static bool {name}__get_{field.name}(int argc, py_Ref argv) {{') +def gen_getter(w: Writer, struct: Struct, cvt: Converter, field: StructField): + w.write(f'static bool {struct.identifier}__get_{field.name}(int argc, py_Ref argv) {{') w.indent() w.write(f'PY_CHECK_ARGC(1);') - w.write(f'{name}* self = py_touserdata(argv);') + w.write(f'{struct.code_name}* self = py_touserdata(argv);') cvt.c2py(w, 'py_retval()', f'self->{field.name}') w.write('return true;') w.dedent() w.write('}') -def gen_setter(w: Writer, name: str, cvt: Converter, field: StructField): - w.write(f'static bool {name}__set_{field.name}(int argc, py_Ref argv) {{') +def gen_setter(w: Writer, struct: Struct, cvt: Converter, field: StructField): + w.write(f'static bool {struct.identifier}__set_{field.name}(int argc, py_Ref argv) {{') w.indent() w.write(f'PY_CHECK_ARGC(2);') - w.write(f'{name}* self = py_touserdata(argv);') + w.write(f'{struct.code_name}* self = py_touserdata(argv);') cvt.py2c(w, f'self->{field.name}', 'py_arg(1)') w.write('py_newnone(py_retval());') w.write('return true;') @@ -24,10 +24,11 @@ def gen_setter(w: Writer, name: str, cvt: Converter, field: StructField): w.write('}') def gen_struct(w: Writer, pyi_w: Writer, struct: Struct): - name = struct.name + name = struct.code_name + identifier = struct.identifier converters = [get_converter(field.type) for field in struct.fields or []] # default __new__ - w.write(f'static bool {name}__new__(int argc, py_Ref argv) {{') + w.write(f'static bool {identifier}__new__(int argc, py_Ref argv) {{') w.indent() w.write(f'py_Type cls = py_totype(argv);') w.write(f'py_newobject(py_retval(), cls, 0, sizeof({name}));') @@ -36,7 +37,7 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct): w.write('}') # default __init__ - w.write(f'static bool {name}__init__(int argc, py_Ref argv) {{') + w.write(f'static bool {identifier}__init__(int argc, py_Ref argv) {{') w.indent() w.write(f'{name}* self = py_touserdata(argv);') w.write(f'if(argc == 1) {{') @@ -62,46 +63,33 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct): w.dedent() w.write('}') - # default __copy__ - w.write(f'static bool {name}__copy__(int argc, py_Ref argv) {{') - w.indent() - w.write(f'PY_CHECK_ARGC(1);') - w.write(f'{name}* self = py_touserdata(argv);') - w.write(f'{name}* res = py_newobject(py_retval(), py_typeof(argv), 0, sizeof({name}));') - w.write(f'*res = *self;') - w.write('return true;') - w.dedent() - w.write('}') - for i, field in enumerate(struct.fields): cvt = converters[i] - gen_getter(w, name, cvt, field) + gen_getter(w, struct, cvt, field) if not cvt.is_const(): - gen_setter(w, name, cvt, field) + gen_setter(w, struct, cvt, field) - w.write(f'static py_Type register__{name}(py_GlobalRef mod) {{') + w.write(f'static py_Type register__{identifier}(py_GlobalRef mod) {{') w.indent() - w.write(f'py_Type type = py_newtype("{name}", tp_object, mod, NULL);') + w.write(f'py_Type type = py_newtype("{identifier}", tp_object, mod, NULL);') - w.write(f'py_bindmagic(type, __new__, {name}__new__);') - w.write(f'py_bindmagic(type, __init__, {name}__init__);') - w.write(f'py_bindmethod(type, "__address__", struct__address__);') - w.write(f'py_bindmethod(type, "copy", {name}__copy__);') + w.write(f'py_bindmethod(type, "__new__", {identifier}__new__);') + w.write(f'py_bindmethod(type, "__init__", {identifier}__init__);') for i, field in enumerate(struct.fields): cvt = converters[i] if cvt.is_const(): setter = 'NULL' else: - setter = f'{name}__set_{field.name}' - w.write(f'py_bindproperty(type, "{field.name}", {name}__get_{field.name}, {setter});') + setter = f'{identifier}__set_{field.name}' + w.write(f'py_bindproperty(type, "{field.name}", {identifier}__get_{field.name}, {setter});') w.write(f'return type;') w.dedent() w.write('}') # pyi - pyi_w.write(f'class {name}:') + pyi_w.write(f'class {identifier}:') pyi_w.indent() py_args = [] @@ -116,10 +104,8 @@ def gen_struct(w: Writer, pyi_w: Writer, struct: Struct): pyi_w.write(f'def __init__(self): ...') pyi_w.write(f'@overload') pyi_w.write(f'def __init__(self, {", ".join(py_args)}): ...') - pyi_w.write(f'def __address__(self) -> int: ...') - pyi_w.write(f"def copy(self) -> '{name}': ...") pyi_w.write('') pyi_w.dedent() - w.write(f'static py_Type tp_user_{name};') - return f'tp_user_{name} = register__{name}(mod);' \ No newline at end of file + w.write(f'static py_Type tp_user_{identifier};') + return f'tp_user_{identifier} = register__{identifier}(mod);' \ No newline at end of file diff --git a/ffigen/gen_periphery.py b/ffigen/gen_periphery.py index 06d7759c..d323da1f 100644 --- a/ffigen/gen_periphery.py +++ b/ffigen/gen_periphery.py @@ -1,7 +1,7 @@ import pcpp import pycparser from ffigen.library import Library -from ffigen.converters import set_vmath_converter, set_enum_converters +from ffigen.converters import set_vmath_converter, set_enum_converter from ffigen.meta import Header import os