Skip to content

Commit

Permalink
ctypesgen: remove unnamed zero bit sized structure members (#2079)
Browse files Browse the repository at this point in the history
from generated files, as they are not supported by Ctypes.

Addresses issue reported in #2073
  • Loading branch information
nilason authored and neteler committed Mar 2, 2022
1 parent 1c4b29f commit fa203ce
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 4 deletions.
158 changes: 158 additions & 0 deletions python/libgrass_interface_generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,164 @@ file generation.

```

#### Ctypes "unnamed structure member with 0 bit size"-patch

Using unnamed zero bit sized structure members, e.g.:

```c
struct timespec {
time_t tv_sec;
int :8*(sizeof(time_t)-sizeof(long))*(__BYTE_ORDER==4321);
long tv_nsec;
int :8*(sizeof(time_t)-sizeof(long))*(__BYTE_ORDER!=4321);
};
```

is not supported by Ctypes. Ctypesgen generates this code to:

```py
struct_timespec._fields_ = [
('tv_sec', time_t),
('unnamed_1', c_int, ((8 * (sizeof(time_t) - sizeof(c_long))) * (1234 == 4321))),
('tv_nsec', c_long),
('unnamed_2', c_int, ((8 * (sizeof(time_t) - sizeof(c_long))) * (1234 != 4321))),
]
```

which therefore causes `ValueError: number of bits invalid for bit field`
(if the bit size expression equals 0).

Reported with: https://github.com/OSGeo/grass/pull/2073

This patch removes the zero bit sized unnamed structure members from the
generated files.

```diff
--- ctypesgen/printer_python/printer.py.orig
+++ ctypesgen/printer_python/printer.py
@@ -46,6 +46,7 @@

self.file = open(outpath, "w") if outpath else sys.stdout
self.options = options
+ self.has_unnamed_struct_member = False

if self.options.strip_build_path and self.options.strip_build_path[-1] != os.path.sep:
self.options.strip_build_path += os.path.sep
@@ -82,9 +83,14 @@
self.print_group(self.options.inserted_files, "inserted files", self.insert_file)
self.strip_prefixes()

- def __del__(self):
+ if self.has_unnamed_struct_member and outpath:
+ self._add_remove_zero_bitfields()
+
self.file.close()

+ if self.has_unnamed_struct_member and outpath and sys.executable:
+ os.system("{0} {1}".format(sys.executable, outpath))
+
def print_group(self, list, name, function):
if list:
self.file.write("# Begin %s\n" % name)
@@ -231,6 +237,7 @@
mem = list(struct.members[mi])
if mem[0] is None:
while True:
+ self.has_unnamed_struct_member = True
name = "%s%i" % (anon_prefix, n)
n += 1
if name not in names:
@@ -243,7 +250,10 @@

self.file.write("%s_%s.__slots__ = [\n" % (struct.variety, struct.tag))
for name, ctype in struct.members:
- self.file.write(" '%s',\n" % name)
+ skip_unnamed = (
+ "#unnamedbitfield_{0} ".format(struct.tag) if name.startswith(anon_prefix) else ""
+ )
+ self.file.write(" {0}'{1}',\n".format(skip_unnamed, name))
self.file.write("]\n")

if len(unnamed_fields) > 0:
@@ -255,9 +265,15 @@
self.file.write("%s_%s._fields_ = [\n" % (struct.variety, struct.tag))
for name, ctype in struct.members:
if isinstance(ctype, CtypesBitfield):
+ skip_unnamed = (
+ "#unnamedbitfield_{0} ".format(struct.tag)
+ if name.startswith(anon_prefix)
+ else ""
+ )
self.file.write(
- " ('%s', %s, %s),\n"
- % (name, ctype.py_string(), ctype.bitfield.py_string(False))
+ " {0}('{1}', {2}, {3}),\n".format(
+ skip_unnamed, name, ctype.py_string(), ctype.bitfield.py_string(False)
+ )
)
else:
self.file.write(" ('%s', %s),\n" % (name, ctype.py_string()))
@@ -458,3 +474,57 @@
)

inserted_file.close()
+
+ def _add_remove_zero_bitfields(self):
+ self.file.write(
+ "#REMOVE_START\n"
+ "def main():\n"
+ " zero_bitfield_list = list()\n"
+ " filename = os.path.abspath(__file__)\n"
+ "\n"
+ ' with open(filename, "r") as f:\n'
+ " regex = re.compile(\n"
+ r' r"([\s]*)(\#unnamedbitfield)_"'
+ "\n"
+ r' r"(?P<struct_name>[a-zA-Z_].[a-zA-Z0-9_]*)\s(?P<expr>.*)\,"'
+ "\n"
+ " )\n"
+ " for line in f:\n"
+ " m = regex.match(line)\n"
+ " if m:\n"
+ ' struct_name = m.group("struct_name")\n'
+ ' bitfield_expression = tuple(eval(m.group("expr")))\n'
+ "\n"
+ " if len(bitfield_expression) == 3 and bitfield_expression[2] == 0:\n"
+ " member = bitfield_expression[0]\n"
+ " zero_bitfield_list.append((struct_name, member))\n"
+ "\n"
+ ' with open(filename, "r+") as f:\n'
+ " filedata = f.read()\n"
+ "\n"
+ " for (struct_name, member) in zero_bitfield_list:\n"
+ " pat = re.compile(\n"
+ r""" r"( *)#unnamedbitfield_{0}( '| \('){1}.*\n".format("""
+ "\n"
+ " struct_name, member\n"
+ " )\n"
+ " )\n"
+ ' filedata = pat.sub("", filedata)\n'
+ "\n"
+ r' regex = re.compile(r"#REMOVE_START.*#REMOVE_END\n", re.DOTALL)'
+ "\n"
+ ' filedata = regex.sub("", filedata)\n'
+ "\n"
+ r""" regex = re.compile(r"#unnamedbitfield_[^'\(]*")"""
+ "\n"
+ ' filedata = regex.sub("", filedata)\n'
+ "\n"
+ " f.seek(0)\n"
+ " f.write(filedata)\n"
+ " f.truncate()\n"
+ "\n"
+ "\n"
+ 'if __name__ == "__main__":\n'
+ " main()\n"
+ "#REMOVE_END\n"
+ )

```

#### Loader and preamble patch

Replaces sed introduced with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def __init__(self, outpath, options, data):

self.file = open(outpath, "w") if outpath else sys.stdout
self.options = options
self.has_unnamed_struct_member = False

if self.options.strip_build_path and self.options.strip_build_path[-1] != os.path.sep:
self.options.strip_build_path += os.path.sep
Expand Down Expand Up @@ -82,9 +83,14 @@ def __init__(self, outpath, options, data):
self.print_group(self.options.inserted_files, "inserted files", self.insert_file)
self.strip_prefixes()

def __del__(self):
if self.has_unnamed_struct_member and outpath:
self._add_remove_zero_bitfields()

self.file.close()

if self.has_unnamed_struct_member and outpath and sys.executable:
os.system("{0} {1}".format(sys.executable, outpath))

def print_group(self, list, name, function):
if list:
self.file.write("# Begin %s\n" % name)
Expand Down Expand Up @@ -231,6 +237,7 @@ def print_struct_members(self, struct):
mem = list(struct.members[mi])
if mem[0] is None:
while True:
self.has_unnamed_struct_member = True
name = "%s%i" % (anon_prefix, n)
n += 1
if name not in names:
Expand All @@ -243,7 +250,10 @@ def print_struct_members(self, struct):

self.file.write("%s_%s.__slots__ = [\n" % (struct.variety, struct.tag))
for name, ctype in struct.members:
self.file.write(" '%s',\n" % name)
skip_unnamed = (
"#unnamedbitfield_{0} ".format(struct.tag) if name.startswith(anon_prefix) else ""
)
self.file.write(" {0}'{1}',\n".format(skip_unnamed, name))
self.file.write("]\n")

if len(unnamed_fields) > 0:
Expand All @@ -255,9 +265,15 @@ def print_struct_members(self, struct):
self.file.write("%s_%s._fields_ = [\n" % (struct.variety, struct.tag))
for name, ctype in struct.members:
if isinstance(ctype, CtypesBitfield):
skip_unnamed = (
"#unnamedbitfield_{0} ".format(struct.tag)
if name.startswith(anon_prefix)
else ""
)
self.file.write(
" ('%s', %s, %s),\n"
% (name, ctype.py_string(), ctype.bitfield.py_string(False))
" {0}('{1}', {2}, {3}),\n".format(
skip_unnamed, name, ctype.py_string(), ctype.bitfield.py_string(False)
)
)
else:
self.file.write(" ('%s', %s),\n" % (name, ctype.py_string()))
Expand Down Expand Up @@ -458,3 +474,57 @@ def insert_file(self, filename):
)

inserted_file.close()

def _add_remove_zero_bitfields(self):
self.file.write(
"#REMOVE_START\n"
"def main():\n"
" zero_bitfield_list = list()\n"
" filename = os.path.abspath(__file__)\n"
"\n"
' with open(filename, "r") as f:\n'
" regex = re.compile(\n"
r' r"([\s]*)(\#unnamedbitfield)_"'
"\n"
r' r"(?P<struct_name>[a-zA-Z_].[a-zA-Z0-9_]*)\s(?P<expr>.*)\,"'
"\n"
" )\n"
" for line in f:\n"
" m = regex.match(line)\n"
" if m:\n"
' struct_name = m.group("struct_name")\n'
' bitfield_expression = tuple(eval(m.group("expr")))\n'
"\n"
" if len(bitfield_expression) == 3 and bitfield_expression[2] == 0:\n"
" member = bitfield_expression[0]\n"
" zero_bitfield_list.append((struct_name, member))\n"
"\n"
' with open(filename, "r+") as f:\n'
" filedata = f.read()\n"
"\n"
" for (struct_name, member) in zero_bitfield_list:\n"
" pat = re.compile(\n"
r""" r"( *)#unnamedbitfield_{0}( '| \('){1}.*\n".format("""
"\n"
" struct_name, member\n"
" )\n"
" )\n"
' filedata = pat.sub("", filedata)\n'
"\n"
r' regex = re.compile(r"#REMOVE_START.*#REMOVE_END\n", re.DOTALL)'
"\n"
' filedata = regex.sub("", filedata)\n'
"\n"
r""" regex = re.compile(r"#unnamedbitfield_[^'\(]*")"""
"\n"
' filedata = regex.sub("", filedata)\n'
"\n"
" f.seek(0)\n"
" f.write(filedata)\n"
" f.truncate()\n"
"\n"
"\n"
'if __name__ == "__main__":\n'
" main()\n"
"#REMOVE_END\n"
)

0 comments on commit fa203ce

Please sign in to comment.