diff options
author | Armin Rigo <arigo@tunes.org> | 2012-08-04 17:46:09 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2012-08-04 17:46:09 +0200 |
commit | e2024b64eb37c607c0742caa3bb1af682e7855f5 (patch) | |
tree | 87499ea81aab472fb763bd2f5c146223217ee0ce /cffi/verifier.py | |
parent | 599300811cc1015773147b625d20f3e033865e2f (diff) | |
download | cffi-e2024b64eb37c607c0742caa3bb1af682e7855f5.tar.gz |
Merge the two verifiers into two VEngine classes. There is still a little bit
of code duplication but not too much.
Diffstat (limited to 'cffi/verifier.py')
-rw-r--r-- | cffi/verifier.py | 466 |
1 files changed, 36 insertions, 430 deletions
diff --git a/cffi/verifier.py b/cffi/verifier.py index 322169b..00aa5f5 100644 --- a/cffi/verifier.py +++ b/cffi/verifier.py @@ -1,26 +1,25 @@ import sys, os, binascii, imp, shutil -from . import model, ffiplatform from . import __version__ +from . import ffiplatform class Verifier(object): _status = '?' - def __init__(self, ffi, preamble, **kwds): - import _cffi_backend - if ffi._backend is not _cffi_backend: - raise NotImplementedError( - "verify() is only available for the _cffi_backend") - # + def __init__(self, ffi, preamble, force_generic_engine=False, **kwds): self.ffi = ffi self.preamble = preamble self.kwds = kwds + vengine_class = _locate_engine_class(ffi, force_generic_engine) + self._vengine = vengine_class(self) # - key = '\x00'.join(['2', sys.version[:3], __version__, preamble] + + key = '\x00'.join(['1', sys.version[:3], __version__, preamble] + ffi._cdefsources) - k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff).lstrip('0').rstrip('L') - k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff).lstrip('0').rstrip('L') - modulename = '_cffi_%s%s' % (k1, k2) + k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) + k1 = k1.lstrip('0x').rstrip('L') + k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) + k2 = k2.lstrip('0').rstrip('L') + modulename = '_cffi_%s%s%s' % (self._vengine._class_key, k1, k2) suffix = _get_so_suffix() self.sourcefilename = os.path.join(_TMPDIR, modulename + '.c') self.modulefilename = os.path.join(_TMPDIR, modulename + suffix) @@ -71,7 +70,7 @@ class Verifier(object): return ffiplatform.get_extension(sourcename, modname, **self.kwds) def generates_python_module(self): - return False + return self._vengine._gen_python_module # ---------- @@ -84,36 +83,23 @@ class Verifier(object): if f is not None: f.close() self.modulefilename = filename + self._vengine.collect_types() self._status = 'module' - def _prnt(self, what=''): - print >> self._f, what - def _write_source(self, file=None): must_close = (file is None) if must_close: _ensure_dir(self.sourcefilename) file = open(self.sourcefilename, 'w') - self._f = file + self._vengine._f = file try: - self._write_source_to_f() + self._vengine.write_source_to_f() finally: - del self._f + del self._vengine._f if must_close: file.close() self._status = 'source' - def _write_source_to_f(self): - prnt = self._prnt - # first paste some standard set of lines that are mostly '#include' - prnt(cffimod_header) - # then paste the C source given by the user, verbatim. - prnt(self.preamble) - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate("decl") - def _compile_module(self): # compile this C source tmpdir = os.path.dirname(self.sourcefilename) @@ -128,411 +114,31 @@ class Verifier(object): self._status = 'module' def _load_library(self): - # XXX review all usages of 'self' here! - # import it with the CFFI backend - backend = self.ffi._backend - module = backend.load_library(self.modulefilename) - # - # call loading_cpy_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - # - # the C code will need the <ctype> objects. Collect them in - # order in a list. - #revmapping = dict([(value, key) - # for (key, value) in self._typesdict.items()]) - #lst = [revmapping[i] for i in range(len(revmapping))] - #lst = map(self.ffi._get_cached_btype, lst) - # - # build the FFILibrary class and instance and call _cffi_setup(). - # this will set up some fields like '_cffi_types', and only then - # it will invoke the chained list of functions that will really - # build (notably) the constant objects, as <cdata> if they are - # pointers, and store them as attributes on the 'library' object. - class FFILibrary(object): - _cffi_module = module - library = FFILibrary() - #module._cffi_setup(lst, ffiplatform.VerificationError, library) - # - # finally, call the loaded_cpy_xxx() functions. This will perform - # the final adjustments, like copying the Python->C wrapper - # functions from the module to the 'library' object, and setting - # up the FFILibrary class with properties for the global C variables. - self._load(module, 'loaded', library=library) - return library - - def _generate(self, step_name): - for name, tp in self.ffi._parser._declarations.iteritems(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise ffiplatform.VerificationError( - "not implemented in verify(): %r" % name) - method(tp, realname) + return self._vengine.load_library() - def _load(self, module, step_name, **kwds): - for name, tp in self.ffi._parser._declarations.iteritems(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) - method(tp, realname, module, **kwds) - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - # typedefs: generates no code so far - - _generate_cpy_typedef_decl = _generate_nothing - _loading_cpy_typedef = _loaded_noop - _loaded_cpy_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_cpy_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no _cffi_f_%s wrapper) - self._generate_cpy_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - argnames = [] - for i, type in enumerate(tp.args): - indirection = '' - if isinstance(type, model.StructOrUnion): - indirection = '*' - argnames.append('%sx%d' % (indirection, i)) - arglist = [type.get_c_name(' %s' % arg) - for type, arg in zip(tp.args, argnames)] - arglist = ', '.join(arglist) or 'void' - funcdecl = ' _cffi_f_%s(%s)' % (name, arglist) - prnt(tp.result.get_c_name(funcdecl)) - prnt('{') - # - if not isinstance(tp.result, model.VoidType): - result_code = 'return ' - else: - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) - prnt('}') - prnt() - - _loading_cpy_function = _loaded_noop - - def _loaded_cpy_function(self, tp, name, module, library): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - newfunction = self._load_constant(False, tp, name, module) - else: - indirections = [] - if any(isinstance(type, model.StructOrUnion) for type in tp.args): - indirect_args = [] - for i, type in enumerate(tp.args): - if isinstance(type, model.StructOrUnion): - type = model.PointerType(type) - indirections.append((i, type)) - indirect_args.append(type) - tp = model.FunctionPtrType(tuple(indirect_args), - tp.result, tp.ellipsis) - BFunc = self.ffi._get_cached_btype(tp) - wrappername = '_cffi_f_%s' % name - newfunction = module.load_function(BFunc, wrappername) - for i, type in indirections: - newfunction = self._make_struct_wrapper(newfunction, i, type) - setattr(library, name, newfunction) - - def _make_struct_wrapper(self, oldfunc, i, tp): - backend = self.ffi._backend - BType = self.ffi._get_cached_btype(tp) - def newfunc(*args): - args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] - return oldfunc(*args) - return newfunc - - # ---------- - # named structs - - def _generate_cpy_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - - def _loading_cpy_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - - def _loaded_cpy_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - for i in range(len(tp.fldnames)): - fname = tp.fldnames[i] - ftype = tp.fldtypes[i] - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()): - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. Note the parentheses - # around the '*tmp' below. In most cases they are not needed - # but don't hurt --- except test_struct_array_field. - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('(*tmp)'), fname)) - prnt('}') - prnt('ssize_t %s(ssize_t i)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - if tp.partial: - prnt(' static ssize_t nums[] = {') - prnt(' 1, sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname in tp.fldnames: - prnt(' offsetof(%s, %s),' % (cname, fname)) - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' return nums[i];') - else: - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - conditions = [ - 'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)), - 'offsetof(struct _cffi_aligncheck, y) != %d' % ( - ffi.alignof(BStruct),)] - for fname, ftype in zip(tp.fldnames, tp.fldtypes): - BField = ffi._get_cached_btype(ftype) - conditions += [ - 'offsetof(%s, %s) != %d' % ( - cname, fname, ffi.offsetof(BStruct, fname)), - 'sizeof(((%s *)0)->%s) != %d' % ( - cname, fname, ffi.sizeof(BField))] - prnt(' if (%s ||' % conditions[0]) - for i in range(1, len(conditions)-1): - prnt(' %s ||' % conditions[i]) - prnt(' %s) {' % conditions[-1]) - prnt(' return -1;') - prnt(' }') - prnt(' else {') - prnt(' return 0;') - prnt(' }') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - BFunc = self.ffi.typeof("ssize_t(*)(ssize_t)") - function = module.load_function(BFunc, layoutfuncname) - layout = function(0) - if layout < 0: - raise ffiplatform.VerificationError( - "incompatible layout for %s" % cname) - elif layout == 0: - assert not tp.partial - else: - totalsize = function(1) - totalalignment = function(2) - fieldofs = [] - fieldsize = [] - num = 3 - while True: - x = function(num) - if x < 0: break - fieldofs.append(x) - fieldsize.append(function(num+1)) - num += 2 - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_cpy_anonymous_decl(self, tp, name): - self._generate_struct_or_union_decl(tp, '', name) - - def _loading_cpy_anonymous(self, tp, name, module): - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_cpy_anonymous(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_cpy_const(self, is_int, name, tp=None, category='const'): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - if is_int: - assert category == 'const' - prnt('int %s(long long *out_value)' % funcname) - prnt('{') - prnt(' *out_value = (long long)(%s);' % (name,)) - prnt(' return (%s) <= 0;' % (name,)) - prnt('}') - else: - assert tp is not None - prnt(tp.get_c_name(' %s(void)' % funcname),) - prnt('{') - if category == 'var': - ampersand = '&' - else: - ampersand = '' - prnt(' return (%s%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_cpy_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - _loading_cpy_constant = _loaded_noop - - def _load_constant(self, is_int, tp, name, module): - funcname = '_cffi_const_%s' % name - if is_int: - BFunc = self.ffi.typeof("int(*)(long long*)") - function = module.load_function(BFunc, funcname) - p = self.ffi.new("long long*") - negative = function(p) - value = int(p[0]) - if value < 0 and not negative: - value += (1 << (8*self.ffi.sizeof("long long"))) - else: - BFunc = self.ffi.typeof(tp.get_c_name('(*)(void)')) - function = module.load_function(BFunc, funcname) - value = function() - return value - - def _loaded_cpy_constant(self, tp, name, module, library): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - value = self._load_constant(is_int, tp, name, module) - setattr(library, name, value) - - # ---------- - # enums - - def _generate_cpy_enum_decl(self, tp, name): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator) - return - # - funcname = '_cffi_enum_%s' % name - prnt = self._prnt - prnt('int %s(char *out_error)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - prnt(' if (%s != %d) {' % (enumerator, enumvalue)) - prnt(' snprintf(out_error, 255, "in enum %s: ' - '%s has the real value %d, not %d",') - prnt(' "%s", "%s", (int)%s, %d);' % ( - name, enumerator, enumerator, enumvalue)) - prnt(' return -1;') - prnt(' }') - prnt(' return 0;') - prnt('}') - prnt() - - _loading_cpy_enum = _loaded_noop - - def _loading_cpy_enum(self, tp, name, module): - if tp.partial: - enumvalues = [self._load_constant(True, tp, enumerator, module) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial = False - else: - BFunc = self.ffi.typeof("int(*)(char*)") - funcname = '_cffi_enum_%s' % name - function = module.load_function(BFunc, funcname) - p = self.ffi.new("char[]", 256) - if function(p) < 0: - raise ffiplatform.VerificationError(self.ffi.string(p)) - - def _loaded_cpy_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_decl(self, tp, name): - assert tp == '...' - self._generate_cpy_const(True, name) - - _loading_cpy_macro = _loaded_noop - - def _loaded_cpy_macro(self, tp, name, module, library): - value = self._load_constant(True, tp, name, module) - setattr(library, name, value) +# ____________________________________________________________ - # ---------- - # global variables +_FORCE_GENERIC_ENGINE = False # for tests - def _generate_cpy_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - self._generate_cpy_const(False, name, tp_ptr) +def _locate_engine_class(ffi, force_generic_engine): + if _FORCE_GENERIC_ENGINE: + force_generic_engine = True + if not force_generic_engine: + if '__pypy__' in sys.builtin_module_names: + force_generic_engine = True else: - tp_ptr = model.PointerType(tp) - self._generate_cpy_const(False, name, tp_ptr, category='var') - - _loading_cpy_variable = _loaded_noop - - def _loaded_cpy_variable(self, tp, name, module, library): - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - tp_ptr = model.PointerType(tp.item) - value = self._load_constant(False, tp_ptr, name, module) - setattr(library, name, value) - return - # remove ptr=<cdata 'int *'> from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - funcname = '_cffi_var_%s' % name - BFunc = self.ffi.typeof(tp.get_c_name('*(*)(void)')) - function = module.load_function(BFunc, funcname) - ptr = function() - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(library.__class__, name, property(getter, setter)) - -cffimod_header = r''' -#include <stdio.h> -#include <stddef.h> -#include <stdint.h> -#include <stdarg.h> -#include <errno.h> -#include <sys/types.h> /* XXX for ssize_t */ -''' + try: + import _cffi_backend + except ImportError: + _cffi_backend = '?' + if ffi._backend is not _cffi_backend: + force_generic_engine = True + if force_generic_engine: + from . import vengine_gen + return vengine_gen.VGenericEngine + else: + from . import vengine_cpy + return vengine_cpy.VCPythonEngine # ____________________________________________________________ |