summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2015-01-11 15:04:41 +0100
committerArmin Rigo <arigo@tunes.org>2015-01-11 15:04:41 +0100
commit9215b25edf36171343d4acac02f2237e38e188ed (patch)
treed5dbbd4096f845530b344bdc5fe13d5234f5ec96
parent5328afc70e4e27f10865d4e596aca6d875284d90 (diff)
downloadcffi-9215b25edf36171343d4acac02f2237e38e188ed.tar.gz
Fix (thanks gkcn on irc): in cdef() we can say "#define FOO 42", but
this declaration is completely ignored in verify(). Instead, we need to check that the real value is 42, and store the name FOO on the returned library object.
-rw-r--r--cffi/cparser.py12
-rw-r--r--cffi/vengine_cpy.py55
-rw-r--r--cffi/vengine_gen.py96
-rw-r--r--testing/test_parsing.py8
-rw-r--r--testing/test_verify.py12
5 files changed, 122 insertions, 61 deletions
diff --git a/cffi/cparser.py b/cffi/cparser.py
index 5375242..49ca9b9 100644
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -229,12 +229,18 @@ class Parser(object):
pyvalue = int(int_str, 0)
self._add_constants(key, pyvalue)
+ self._declare('macro ' + key, pyvalue)
elif value == '...':
self._declare('macro ' + key, value)
else:
- raise api.CDefError('only supports the syntax "#define '
- '%s ..." (literally) or "#define '
- '%s 0x1FF" for now' % (key, key))
+ raise api.CDefError(
+ 'only supports one of the following syntax:\n'
+ ' #define %s ... (literally dot-dot-dot)\n'
+ ' #define %s NUMBER (with NUMBER an integer'
+ ' constant, decimal/hex/octal)\n'
+ 'got:\n'
+ ' #define %s %s'
+ % (key, key, key, value))
def _parse_decl(self, decl):
node = decl.type
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
index c4cc398..1c4668c 100644
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -592,7 +592,8 @@ class VCPythonEngine(object):
# constants, likely declared with '#define'
def _generate_cpy_const(self, is_int, name, tp=None, category='const',
- vartp=None, delayed=True, size_too=False):
+ vartp=None, delayed=True, size_too=False,
+ check_value=None):
prnt = self._prnt
funcname = '_cffi_%s_%s' % (category, name)
prnt('static int %s(PyObject *lib)' % funcname)
@@ -604,6 +605,9 @@ class VCPythonEngine(object):
else:
assert category == 'const'
#
+ if check_value is not None:
+ self._check_int_constant_value(name, check_value)
+ #
if not is_int:
if category == 'var':
realexpr = '&' + name
@@ -651,6 +655,27 @@ class VCPythonEngine(object):
# ----------
# enums
+ def _check_int_constant_value(self, name, value, err_prefix=''):
+ prnt = self._prnt
+ if value <= 0:
+ prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % (
+ name, name, value))
+ else:
+ prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
+ name, name, value))
+ prnt(' char buf[64];')
+ prnt(' if ((%s) <= 0)' % name)
+ prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name)
+ prnt(' else')
+ prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' %
+ name)
+ prnt(' PyErr_Format(_cffi_VerificationError,')
+ prnt(' "%s%s has the real value %s, not %s",')
+ prnt(' "%s", "%s", buf, "%d");' % (
+ err_prefix, name, value))
+ prnt(' return -1;')
+ prnt(' }')
+
def _enum_funcname(self, prefix, name):
# "$enum_$1" => "___D_enum____D_1"
name = name.replace('$', '___D_')
@@ -667,25 +692,8 @@ class VCPythonEngine(object):
prnt('static int %s(PyObject *lib)' % funcname)
prnt('{')
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
- if enumvalue <= 0:
- prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % (
- enumerator, enumerator, enumvalue))
- else:
- prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
- enumerator, enumerator, enumvalue))
- prnt(' char buf[64];')
- prnt(' if ((%s) <= 0)' % enumerator)
- prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % enumerator)
- prnt(' else')
- prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' %
- enumerator)
- prnt(' PyErr_Format(_cffi_VerificationError,')
- prnt(' "enum %s: %s has the real value %s, '
- 'not %s",')
- prnt(' "%s", "%s", buf, "%d");' % (
- name, enumerator, enumvalue))
- prnt(' return -1;')
- prnt(' }')
+ self._check_int_constant_value(enumerator, enumvalue,
+ "enum %s: " % name)
prnt(' return %s;' % self._chained_list_constants[True])
self._chained_list_constants[True] = funcname + '(lib)'
prnt('}')
@@ -709,8 +717,11 @@ class VCPythonEngine(object):
# macros: for now only for integers
def _generate_cpy_macro_decl(self, tp, name):
- assert tp == '...'
- self._generate_cpy_const(True, name)
+ if tp == '...':
+ check_value = None
+ else:
+ check_value = tp # an integer
+ self._generate_cpy_const(True, name, check_value=check_value)
_generate_cpy_macro_collecttype = _generate_nothing
_generate_cpy_macro_method = _generate_nothing
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
index 1271d4f..8e1412c 100644
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -355,11 +355,20 @@ class VGenericEngine(object):
# ----------
# constants, likely declared with '#define'
- def _generate_gen_const(self, is_int, name, tp=None, category='const'):
+ def _generate_gen_const(self, is_int, name, tp=None, category='const',
+ check_value=None):
prnt = self._prnt
funcname = '_cffi_%s_%s' % (category, name)
self.export_symbols.append(funcname)
- if is_int:
+ if check_value is not None:
+ assert is_int
+ assert category == 'const'
+ prnt('int %s(char *out_error)' % funcname)
+ prnt('{')
+ self._check_int_constant_value(name, check_value)
+ prnt(' return 0;')
+ prnt('}')
+ elif is_int:
assert category == 'const'
prnt('int %s(long long *out_value)' % funcname)
prnt('{')
@@ -368,6 +377,7 @@ class VGenericEngine(object):
prnt('}')
else:
assert tp is not None
+ assert check_value is None
prnt(tp.get_c_name(' %s(void)' % funcname, name),)
prnt('{')
if category == 'var':
@@ -384,9 +394,13 @@ class VGenericEngine(object):
_loading_gen_constant = _loaded_noop
- def _load_constant(self, is_int, tp, name, module):
+ def _load_constant(self, is_int, tp, name, module, check_value=None):
funcname = '_cffi_const_%s' % name
- if is_int:
+ if check_value is not None:
+ assert is_int
+ self._load_known_int_constant(module, funcname)
+ value = check_value
+ elif is_int:
BType = self.ffi._typeof_locked("long long*")[0]
BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0]
function = module.load_function(BFunc, funcname)
@@ -397,6 +411,7 @@ class VGenericEngine(object):
BLongLong = self.ffi._typeof_locked("long long")[0]
value += (1 << (8*self.ffi.sizeof(BLongLong)))
else:
+ assert check_value is None
BFunc = self.ffi._typeof_locked(tp.get_c_name('(*)(void)', name))[0]
function = module.load_function(BFunc, funcname)
value = function()
@@ -411,6 +426,36 @@ class VGenericEngine(object):
# ----------
# enums
+ def _check_int_constant_value(self, name, value):
+ prnt = self._prnt
+ if value <= 0:
+ prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % (
+ name, name, value))
+ else:
+ prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
+ name, name, value))
+ prnt(' char buf[64];')
+ prnt(' if ((%s) <= 0)' % name)
+ prnt(' sprintf(buf, "%%ld", (long)(%s));' % name)
+ prnt(' else')
+ prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' %
+ name)
+ prnt(' sprintf(out_error, "%s has the real value %s, not %s",')
+ prnt(' "%s", buf, "%d");' % (name[:100], value))
+ prnt(' return -1;')
+ prnt(' }')
+
+ def _load_known_int_constant(self, module, funcname):
+ BType = self.ffi._typeof_locked("char[]")[0]
+ BFunc = self.ffi._typeof_locked("int(*)(char*)")[0]
+ function = module.load_function(BFunc, funcname)
+ p = self.ffi.new(BType, 256)
+ if function(p) < 0:
+ error = self.ffi.string(p)
+ if sys.version_info >= (3,):
+ error = str(error, 'utf-8')
+ raise ffiplatform.VerificationError(error)
+
def _enum_funcname(self, prefix, name):
# "$enum_$1" => "___D_enum____D_1"
name = name.replace('$', '___D_')
@@ -428,24 +473,7 @@ class VGenericEngine(object):
prnt('int %s(char *out_error)' % funcname)
prnt('{')
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
- if enumvalue <= 0:
- prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % (
- enumerator, enumerator, enumvalue))
- else:
- prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
- enumerator, enumerator, enumvalue))
- prnt(' char buf[64];')
- prnt(' if ((%s) <= 0)' % enumerator)
- prnt(' sprintf(buf, "%%ld", (long)(%s));' % enumerator)
- prnt(' else')
- prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' %
- enumerator)
- prnt(' sprintf(out_error,'
- ' "%s has the real value %s, not %s",')
- prnt(' "%s", buf, "%d");' % (
- enumerator[:100], enumvalue))
- prnt(' return -1;')
- prnt(' }')
+ self._check_int_constant_value(enumerator, enumvalue)
prnt(' return 0;')
prnt('}')
prnt()
@@ -457,16 +485,8 @@ class VGenericEngine(object):
tp.enumvalues = tuple(enumvalues)
tp.partial_resolved = True
else:
- BType = self.ffi._typeof_locked("char[]")[0]
- BFunc = self.ffi._typeof_locked("int(*)(char*)")[0]
funcname = self._enum_funcname(prefix, name)
- function = module.load_function(BFunc, funcname)
- p = self.ffi.new(BType, 256)
- if function(p) < 0:
- error = self.ffi.string(p)
- if sys.version_info >= (3,):
- error = str(error, 'utf-8')
- raise ffiplatform.VerificationError(error)
+ self._load_known_int_constant(module, funcname)
def _loaded_gen_enum(self, tp, name, module, library):
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
@@ -477,13 +497,21 @@ class VGenericEngine(object):
# macros: for now only for integers
def _generate_gen_macro_decl(self, tp, name):
- assert tp == '...'
- self._generate_gen_const(True, name)
+ if tp == '...':
+ check_value = None
+ else:
+ check_value = tp # an integer
+ self._generate_gen_const(True, name, check_value=check_value)
_loading_gen_macro = _loaded_noop
def _loaded_gen_macro(self, tp, name, module, library):
- value = self._load_constant(True, tp, name, module)
+ if tp == '...':
+ check_value = None
+ else:
+ check_value = tp # an integer
+ value = self._load_constant(True, tp, name, module,
+ check_value=check_value)
setattr(library, name, value)
type(library)._cffi_dir.append(name)
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
index e53f207..adcf468 100644
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -163,8 +163,12 @@ def test_define_not_supported_for_now():
ffi = FFI(backend=FakeBackend())
e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"')
assert str(e.value) == (
- 'only supports the syntax "#define FOO ..." (literally)'
- ' or "#define FOO 0x1FF" for now')
+ 'only supports one of the following syntax:\n'
+ ' #define FOO ... (literally dot-dot-dot)\n'
+ ' #define FOO NUMBER (with NUMBER an integer'
+ ' constant, decimal/hex/octal)\n'
+ 'got:\n'
+ ' #define FOO "blah"')
def test_unnamed_struct():
ffi = FFI(backend=FakeBackend())
diff --git a/testing/test_verify.py b/testing/test_verify.py
index 39224fa..99fa598 100644
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -2139,3 +2139,15 @@ def test_use_local_dir():
this_dir = os.path.dirname(__file__)
pycache_files = os.listdir(os.path.join(this_dir, '__pycache__'))
assert any('test_use_local_dir' in s for s in pycache_files)
+
+def test_define_known_value():
+ ffi = FFI()
+ ffi.cdef("#define FOO 0x123")
+ lib = ffi.verify("#define FOO 0x123")
+ assert lib.FOO == 0x123
+
+def test_define_wrong_value():
+ ffi = FFI()
+ ffi.cdef("#define FOO 123")
+ e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124")
+ assert str(e.value).endswith("FOO has the real value 124, not 123")