# HG changeset patch # User Armin Rigo <arigo@tunes.org> # Date 1590010186 -7200 # Wed May 20 23:29:46 2020 +0200 # Node ID 3011cd0272f3b4cd116521e2d395867c6def5d55 # Parent 6476ac8a033c2fefade6ca51c2fbc4c345100a28 #453 Special-case typedefs of arrays with '...' length, so that they can be used through recompiler.py as if they had a specified length in most cases. diff --git a/cffi/cparser.py b/cffi/cparser.py --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -395,7 +395,8 @@ realtype = self._get_unknown_ptr_type(decl) else: realtype, quals = self._get_type_and_quals( - decl.type, name=decl.name, partial_length_ok=True) + decl.type, name=decl.name, partial_length_ok=True, + typedef_example="*(%s *)0" % (decl.name,)) self._declare('typedef ' + decl.name, realtype, quals=quals) elif decl.__class__.__name__ == 'Pragma': pass # skip pragma, only in pycparser 2.15 @@ -562,7 +563,8 @@ return model.NamedPointerType(type, declname, quals) return model.PointerType(type, quals) - def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False): + def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, + typedef_example=None): # first, dereference typedefs, if we have it already parsed, we're good if (isinstance(typenode, pycparser.c_ast.TypeDecl) and isinstance(typenode.type, pycparser.c_ast.IdentifierType) and @@ -579,8 +581,18 @@ else: length = self._parse_constant( typenode.dim, partial_length_ok=partial_length_ok) + # a hack: in 'typedef int foo_t[...][...];', don't use '...' as + # the length but use directly the C expression that would be + # generated by recompiler.py. This lets the typedef be used in + # many more places within recompiler.py + if typedef_example is not None: + if length == '...': + length = '_cffi_array_len(%s)' % (typedef_example,) + typedef_example = "*" + typedef_example + # tp, quals = self._get_type_and_quals(typenode.type, - partial_length_ok=partial_length_ok) + partial_length_ok=partial_length_ok, + typedef_example=typedef_example) return model.ArrayType(tp, length), quals # if isinstance(typenode, pycparser.c_ast.PtrDecl): diff --git a/cffi/model.py b/cffi/model.py --- a/cffi/model.py +++ b/cffi/model.py @@ -307,11 +307,14 @@ self.c_name_with_marker = ( self.item.c_name_with_marker.replace('&', brackets)) + def length_is_unknown(self): + return isinstance(self.length, str) + def resolve_length(self, newlength): return ArrayType(self.item, newlength) def build_backend_type(self, ffi, finishlist): - if self.length == '...': + if self.length_is_unknown(): raise CDefError("cannot render the type %r: unknown length" % (self,)) self.item.get_cached_btype(ffi, finishlist) # force the item BType @@ -430,7 +433,7 @@ fsize = fieldsize[i] ftype = self.fldtypes[i] # - if isinstance(ftype, ArrayType) and ftype.length == '...': + if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): # fix the length to match the total size BItemType = ftype.item.get_cached_btype(ffi, finishlist) nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py --- a/cffi/vengine_cpy.py +++ b/cffi/vengine_cpy.py @@ -762,7 +762,7 @@ if isinstance(tp, model.ArrayType): tp_ptr = model.PointerType(tp.item) self._generate_cpy_const(False, name, tp, vartp=tp_ptr, - size_too = (tp.length == '...')) + size_too = tp.length_is_unknown()) else: tp_ptr = model.PointerType(tp) self._generate_cpy_const(False, name, tp_ptr, category='var') @@ -774,7 +774,7 @@ value = getattr(library, name) if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the # sense that "a=..." is forbidden - if tp.length == '...': + if tp.length_is_unknown(): assert isinstance(value, tuple) (value, size) = value BItemType = self.ffi._get_cached_btype(tp.item) diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py --- a/cffi/vengine_gen.py +++ b/cffi/vengine_gen.py @@ -565,7 +565,7 @@ def _generate_gen_variable_decl(self, tp, name): if isinstance(tp, model.ArrayType): - if tp.length == '...': + if tp.length_is_unknown(): prnt = self._prnt funcname = '_cffi_sizeof_%s' % (name,) self.export_symbols.append(funcname) @@ -584,7 +584,7 @@ def _loaded_gen_variable(self, tp, name, module, library): if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the # sense that "a=..." is forbidden - if tp.length == '...': + if tp.length_is_unknown(): funcname = '_cffi_sizeof_%s' % (name,) BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] function = module.load_function(BFunc, funcname) diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -2122,6 +2122,40 @@ p = ffi.new("vmat_t", 4) assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int") +def test_typedef_array_dotdotdot_usage(): + ffi = FFI() + ffi.cdef(""" + typedef int foo_t[...]; + typedef int mat_t[...][...]; + struct s { foo_t a; foo_t *b; foo_t **c; }; + int myfunc(foo_t a, foo_t *b, foo_t **c); + struct sm { mat_t a; mat_t *b; mat_t **c; }; + int myfuncm(mat_t a, mat_t *b, mat_t **c); + """) + lib = verify(ffi, "test_typedef_array_dotdotdot_usage", """ + typedef int foo_t[50]; + typedef int mat_t[6][7]; + struct s { foo_t a; foo_t *b; foo_t **c; }; + static int myfunc(foo_t a, foo_t *b, foo_t **c) { return (**c)[49]; } + struct sm { mat_t a; mat_t *b; mat_t **c; }; + static int myfuncm(mat_t a, mat_t *b, mat_t **c) { return (**c)[5][6]; } + """) + assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") + p = ffi.new("struct s *") + assert ffi.sizeof(p[0]) == 50 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") + p.a[49] = 321 + p.b = ffi.addressof(p, 'a') + p.c = ffi.addressof(p, 'b') + assert lib.myfunc(ffi.NULL, ffi.NULL, p.c) == 321 + # + assert ffi.sizeof("mat_t") == 42 * ffi.sizeof("int") + p = ffi.new("struct sm *") + assert ffi.sizeof(p[0]) == 42 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") + p.a[5][6] = -321 + p.b = ffi.addressof(p, 'a') + p.c = ffi.addressof(p, 'b') + assert lib.myfuncm(ffi.NULL, ffi.NULL, p.c) == -321 + def test_call_with_custom_field_pos(): ffi = FFI() ffi.cdef("""