diff options
author | Armin Rigo <arigo@tunes.org> | 2017-02-27 08:46:05 +0100 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2017-02-27 08:46:05 +0100 |
commit | 7cc4e2309282e5da740ba1c682563cd07342d062 (patch) | |
tree | 8e8e2f4e8162a39a810c7c653a70799031e2c811 | |
parent | bca987808120f90c3dff357400c3a9cf48498251 (diff) | |
download | cffi-7cc4e2309282e5da740ba1c682563cd07342d062.tar.gz |
Issue #310: pycparser was recently made stricter and no longer parses
``typedef int __dotdotdot__ foo_t;``. I think this fixes it
-rw-r--r-- | cffi/cparser.py | 59 | ||||
-rw-r--r-- | testing/cffi0/test_verify.py | 4 |
2 files changed, 37 insertions, 26 deletions
diff --git a/cffi/cparser.py b/cffi/cparser.py index 87d71b1..3d5caed 100644 --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -34,6 +34,9 @@ _r_extern_python = re.compile(r'\bextern\s*"' r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') _r_star_const_space = re.compile( # matches "* const " r"[*]\s*((const|volatile|restrict)\b\s*)+") +_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" + r"\.\.\.") +_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") def _get_parser(): global _parser_cache @@ -180,6 +183,10 @@ def _preprocess(csource): assert csource[p:p+3] == '...' csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, csource[p+3:]) + # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" + csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) + # Replace "float ..." or "double..." with "__dotdotdotfloat__" + csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) # Replace all remaining "..." with the same name, "__dotdotdot__", # which is declared with a typedef for the purpose of C parsing. return csource.replace('...', ' __dotdotdot__ '), macros @@ -252,7 +259,8 @@ class Parser(object): typenames += sorted(ctn) # csourcelines = ['typedef int %s;' % typename for typename in typenames] - csourcelines.append('typedef int __dotdotdot__;') + csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' + ' __dotdotdot__;') csourcelines.append(csource) csource = '\n'.join(csourcelines) if lock is not None: @@ -311,6 +319,8 @@ class Parser(object): for decl in iterator: if decl.name == '__dotdotdot__': break + else: + assert 0 # try: self._inside_extern_python = '__cffi_extern_python_stop' @@ -322,15 +332,15 @@ class Parser(object): raise CDefError("typedef does not declare any name", decl) quals = 0 - if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) - and decl.type.type.names[-1] == '__dotdotdot__'): + if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and + decl.type.type.names[-1].startswith('__dotdotdot')): realtype = self._get_unknown_type(decl) elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and isinstance(decl.type.type.type, pycparser.c_ast.IdentifierType) and - decl.type.type.type.names == ['__dotdotdot__']): - realtype = model.unknown_ptr_type(decl.name) + decl.type.type.type.names[-1].startswith('__dotdotdot')): + realtype = self._get_unknown_ptr_type(decl) else: realtype, quals = self._get_type_and_quals( decl.type, name=decl.name, partial_length_ok=True) @@ -832,24 +842,25 @@ class Parser(object): def _get_unknown_type(self, decl): typenames = decl.type.type.names - assert typenames[-1] == '__dotdotdot__' - if len(typenames) == 1: + if typenames == ['__dotdotdot__']: return model.unknown_type(decl.name) - if (typenames[:-1] == ['float'] or - typenames[:-1] == ['double']): - # not for 'long double' so far - result = model.UnknownFloatType(decl.name) - else: - for t in typenames[:-1]: - if t not in ['int', 'short', 'long', 'signed', - 'unsigned', 'char']: - raise FFIError(':%d: bad usage of "..."' % - decl.coord.line) - result = model.UnknownIntegerType(decl.name) - - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef %s... %s'" % ( - ' '.join(typenames[:-1]), decl.name) - - return result + if typenames == ['__dotdotdotint__']: + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef int... %s'" % decl.name + return model.UnknownIntegerType(decl.name) + + if typenames == ['__dotdotdotfloat__']: + # note: not for 'long double' so far + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef float... %s'" % decl.name + return model.UnknownFloatType(decl.name) + + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) + + def _get_unknown_ptr_type(self, decl): + if decl.type.type.type.names == ['__dotdotdot__']: + return model.unknown_ptr_type(decl.name) + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py index e600908..25430bd 100644 --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -2246,10 +2246,10 @@ def test_dont_support_int_dotdotdot(): assert str(e.value) == ("feature not supported with ffi.verify(), but only " "with ffi.set_source(): 'typedef int... t1'") ffi = FFI() - ffi.cdef("typedef unsigned long... t1;") + ffi.cdef("typedef double ... t1;") e = py.test.raises(VerificationError, ffi.verify, "") assert str(e.value) == ("feature not supported with ffi.verify(), but only " - "with ffi.set_source(): 'typedef unsigned long... t1'") + "with ffi.set_source(): 'typedef float... t1'") def test_const_fields(): ffi = FFI() |