Author: Armin Rigo <ar...@tunes.org>
Branch: cffi-1.0
Changeset: r1993:06675c9b1ad6
Date: 2015-05-12 16:01 +0200
http://bitbucket.org/cffi/cffi/changeset/06675c9b1ad6/

Log:    in-progress

diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py
--- a/cffi/cffi_opcode.py
+++ b/cffi/cffi_opcode.py
@@ -1,4 +1,3 @@
-import struct
 
 class CffiOp(object):
     def __init__(self, op, arg):
@@ -14,12 +13,19 @@
 
     def as_bytes(self):
         assert self.op is not None
-        return struct.pack(">i", (self.arg << 8) | self.op)
+        return format_four_bytes((self.arg << 8) | self.op)
 
     def __str__(self):
         classname = CLASS_NAME.get(self.op, self.op)
         return '(%s %s)' % (classname, self.arg)
 
+def format_four_bytes(num):
+    return '\\x%02X\\x%02X\\x%02X\\x%02X' % (
+        (num >> 24) & 0xFF,
+        (num >> 16) & 0xFF,
+        (num >>  8) & 0xFF,
+        (num      ) & 0xFF)
+
 OP_PRIMITIVE       = 1
 OP_POINTER         = 3
 OP_ARRAY           = 5
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -3,12 +3,40 @@
 from .cffi_opcode import *
 
 
+class GlobalExpr:
+    def __init__(self, name, address, type_op, size=0, check_value=0):
+        self.name = name
+        self.address = address
+        self.type_op = type_op
+        self.size = size
+        self.check_value = check_value
+
+    def as_c_expr(self):
+        return '  { "%s", %s, %s, %s },' % (
+            self.name, self.address, self.type_op.as_c_expr(), self.size)
+
+    def as_python_expr(self):
+        return "b'%s%s',%d" % (self.type_op.as_bytes(), self.name,
+                               self.check_value)
+
+class TypenameExpr:
+    def __init__(self, name, type_index):
+        self.name = name
+        self.type_index = type_index
+
+    def as_c_expr(self):
+        return '  { "%s", %d },' % (self.name, self.type_index)
+
+    def as_python_expr(self):
+        return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)
+
+
 class Recompiler:
 
-    def __init__(self, ffi, module_name):
-        assert isinstance(module_name, bytes)
+    def __init__(self, ffi, module_name, target_is_python=False):
         self.ffi = ffi
         self.module_name = module_name
+        self.target_is_python = target_is_python
 
     def collect_type_table(self):
         self._typesdict = {}
@@ -66,6 +94,7 @@
         # consistency check
         for op in self.cffi_types:
             assert isinstance(op, CffiOp)
+        self.cffi_types = tuple(self.cffi_types)    # don't change any more
 
     def _do_collect_type(self, tp):
         if not isinstance(tp, model.BaseTypeByIdentity):
@@ -106,12 +135,48 @@
 
     # ----------
 
+    ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
+
+    def collect_step_tables(self):
+        # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.
+        self._lsts = {}
+        for step_name in self.ALL_STEPS:
+            self._lsts[step_name] = []
+        self._seen_struct_unions = set()
+        self._generate("ctx")
+        self._add_missing_struct_unions()
+        #
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            lst.sort(key=lambda entry: entry.name)
+            self._lsts[step_name] = tuple(lst)    # don't change any more
+        #
+        # check for a possible internal inconsistency: _cffi_struct_unions
+        # should have been generated with exactly self._struct_unions
+        lst = self._lsts["struct_union"]
+        for tp, i in self._struct_unions.items():
+            assert i < len(lst)
+            assert lst[i].startswith('  { "%s"' % tp.name)
+        assert len(lst) == len(self._struct_unions)
+        # same with enums
+        lst = self._lsts["enum"]
+        for tp, i in self._enums.items():
+            assert i < len(lst)
+            assert lst[i].startswith('  { "%s"' % tp.name)
+        assert len(lst) == len(self._enums)
+
+    # ----------
+
     def _prnt(self, what=''):
         self._f.write(what + '\n')
 
-    def _gettypenum(self, type):
-        # a KeyError here is a bug.  please report it! :-)
-        return self._typesdict[type]
+    def write_source_to_f(self, f, preamble):
+        if self.target_is_python:
+            assert preamble is None
+            self.write_py_source_to_f(f)
+        else:
+            assert preamble is not None
+            self.write_c_source_to_f(f)
 
     def _rel_readlines(self, filename):
         g = open(os.path.join(os.path.dirname(__file__), filename), 'r')
@@ -139,7 +204,6 @@
         #
         # the declaration of '_cffi_types'
         prnt('static void *_cffi_types[] = {')
-        self.cffi_types = tuple(self.cffi_types)    # don't change any more
         typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
         for i, op in enumerate(self.cffi_types):
             comment = ''
@@ -157,44 +221,21 @@
         self._generate("decl")
         #
         # the declaration of '_cffi_globals' and '_cffi_typenames'
-        ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
         nums = {}
-        self._lsts = {}
-        for step_name in ALL_STEPS:
-            self._lsts[step_name] = []
-        self._seen_struct_unions = set()
-        self._generate("ctx")
-        self._add_missing_struct_unions()
-        for step_name in ALL_STEPS:
+        for step_name in self.ALL_STEPS:
             lst = self._lsts[step_name]
             nums[step_name] = len(lst)
             if nums[step_name] > 0:
-                lst.sort()  # sort by name, which is at the start of each line
                 prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
                     step_name, step_name))
                 if step_name == 'field':
-                    self._fix_final_field_list(lst)
-                for line in lst:
-                    prnt(line)
-                if all(line.startswith('#') for line in lst):
-                    prnt('  { 0 }')
+                    XXXX
+                    lst = list(self._fix_final_field_list(lst))
+                for entry in lst:
+                    prnt(entry.as_c_expr())
                 prnt('};')
                 prnt()
         #
-        # check for a possible internal inconsistency: _cffi_struct_unions
-        # should have been generated with exactly self._struct_unions
-        lst = self._lsts["struct_union"]
-        for tp, i in self._struct_unions.items():
-            assert i < len(lst)
-            assert lst[i].startswith('  { "%s"' % tp.name)
-        assert len(lst) == len(self._struct_unions)
-        # same with enums
-        lst = self._lsts["enum"]
-        for tp, i in self._enums.items():
-            assert i < len(lst)
-            assert lst[i].startswith('  { "%s"' % tp.name)
-        assert len(lst) == len(self._enums)
-        #
         # the declaration of '_cffi_includes'
         if self.ffi._included_ffis:
             prnt('static const char * const _cffi_includes[] = {')
@@ -211,12 +252,12 @@
         # the declaration of '_cffi_type_context'
         prnt('static const struct _cffi_type_context_s _cffi_type_context = {')
         prnt('  _cffi_types,')
-        for step_name in ALL_STEPS:
+        for step_name in self.ALL_STEPS:
             if nums[step_name] > 0:
                 prnt('  _cffi_%ss,' % step_name)
             else:
                 prnt('  NULL,  /* no %ss */' % step_name)
-        for step_name in ALL_STEPS:
+        for step_name in self.ALL_STEPS:
             if step_name != "field":
                 prnt('  %d,  /* num_%ss */' % (nums[step_name], step_name))
         if self.ffi._included_ffis:
@@ -266,12 +307,16 @@
         self.ffi._recompiler_module_name = self.module_name
 
     def _to_py(self, x):
+        if isinstance(x, str):
+            x = x.encode('ascii')
         if isinstance(x, bytes):
-            r = repr(x)
-            if not r.startswith('b'):
-                r = 'b' + r
-            return r
-        raise TypeError(type(x).__name__)
+            return "b'%s'" % (x,)
+        if isinstance(x, (list, tuple)):
+            rep = [self._to_py(item) for item in x]
+            if len(rep) == 1:
+                rep.append('')
+            return "(%s)" % (','.join(rep),)
+        return x.as_python_expr()
 
     def write_py_source_to_f(self, f):
         self._f = f
@@ -289,13 +334,20 @@
         prnt('    _types = %s,' % (self._to_py(''.join(types_lst)),))
         typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
         #
-        #.......
+        for step_name in self.ALL_STEPS:
+            lst = self._lsts[step_name]
+            if len(lst) > 0:
+                prnt('    _%ss = %s,' % (step_name, self._to_py(lst)))
         #
         # the footer
         prnt(')')
 
     # ----------
 
+    def _gettypenum(self, type):
+        # a KeyError here is a bug.  please report it! :-)
+        return self._typesdict[type]
+
     def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
         extraarg = ''
         if isinstance(tp, model.PrimitiveType):
@@ -389,8 +441,7 @@
 
     def _typedef_ctx(self, tp, name):
         type_index = self._typesdict[tp]
-        self._lsts["typename"].append(
-            '  { "%s", %d },' % (name, type_index))
+        self._lsts["typename"].append(TypenameExpr(name, type_index))
 
     def _generate_cpy_typedef_ctx(self, tp, name):
         self._typedef_ctx(tp, name)
@@ -531,15 +582,17 @@
             return
         type_index = self._typesdict[tp.as_raw_function()]
         numargs = len(tp.args)
-        if numargs == 0:
-            meth_kind = 'N'   # 'METH_NOARGS'
+        if self.target_is_python:
+            meth_kind = OP_DLOPEN
+        elif numargs == 0:
+            meth_kind = OP_CPYTHON_BLTN_N   # 'METH_NOARGS'
         elif numargs == 1:
-            meth_kind = 'O'   # 'METH_O'
+            meth_kind = OP_CPYTHON_BLTN_O   # 'METH_O'
         else:
-            meth_kind = 'V'   # 'METH_VARARGS'
+            meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
         self._lsts["global"].append(
-            '  { "%s", _cffi_f_%s, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_%s, %d), 0 
},'
-            % (name, name, meth_kind, type_index))
+            GlobalExpr(name, '_cffi_f_%s' % name,
+                       CffiOp(meth_kind, type_index)))
 
     # ----------
     # named structs or unions
@@ -685,12 +738,12 @@
 
     def _fix_final_field_list(self, lst):
         count = 0
-        for i in range(len(lst)):
-            struct_fields = lst[i]
+        for struct_fields in lst:
             pname = struct_fields.split('\n')[0]
             define_macro = '#define _cffi_FIELDS_FOR_%s  %d' % (pname, count)
-            lst[i] = define_macro + struct_fields[len(pname):]
-            count += lst[i].count('\n  { "')
+            result = define_macro + struct_fields[len(pname):]
+            count += result.count('\n  { "')
+            yield result
 
     def _generate_cpy_struct_collecttype(self, tp, name):
         self._struct_collecttype(tp)
@@ -777,12 +830,12 @@
 
     def _generate_cpy_constant_ctx(self, tp, name):
         if isinstance(tp, model.PrimitiveType) and tp.is_integer_type():
-            type_op = '_CFFI_OP(_CFFI_OP_CONSTANT_INT, -1)'
+            type_op = CffiOp(OP_CONSTANT_INT, -1)
         else:
             type_index = self._typesdict[tp]
-            type_op = '_CFFI_OP(_CFFI_OP_CONSTANT, %d)' % type_index
+            type_op = CffiOp(OP_CONSTANT, type_index)
         self._lsts["global"].append(
-            '  { "%s", _cffi_const_%s, %s, 0 },' % (name, name, type_op))
+            GlobalExpr(name, '_cffi_const_%s' % name, type_op))
 
     # ----------
     # enums
@@ -796,11 +849,10 @@
 
     def _enum_ctx(self, tp, cname):
         type_index = self._typesdict[tp]
-        type_op = '_CFFI_OP(_CFFI_OP_ENUM, -1)'
+        type_op = CffiOp(OP_ENUM, -1)
         for enumerator in tp.enumerators:
             self._lsts["global"].append(
-                '  { "%s", _cffi_const_%s, %s, 0 },' %
-                (enumerator, enumerator, type_op))
+                GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op))
         #
         if cname is not None and '$' not in cname:
             size = "sizeof(%s)" % cname
@@ -831,9 +883,10 @@
         self._generate_cpy_const(True, name, check_value=check_value)
 
     def _generate_cpy_macro_ctx(self, tp, name):
+        type_op = CffiOp(OP_CONSTANT_INT, -1)
         self._lsts["global"].append(
-            '  { "%s", _cffi_const_%s,'
-            ' _CFFI_OP(_CFFI_OP_CONSTANT_INT, -1), 0 },' % (name, name))
+            GlobalExpr(name, '_cffi_const_%s' % name, type_op,
+                       check_value=tp))
 
     # ----------
     # global variables
@@ -853,13 +906,13 @@
     def _generate_cpy_variable_ctx(self, tp, name):
         tp = self._global_type(tp, name)
         type_index = self._typesdict[tp]
+        type_op = CffiOp(OP_GLOBAL_VAR, type_index)
         if tp.sizeof_enabled():
             size = "sizeof(%s)" % (name,)
         else:
-            size = "0"
+            size = 0
         self._lsts["global"].append(
-            '  { "%s", &%s, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, %d), %s },'
-            % (name, name, type_index, size))
+            GlobalExpr(name, '&%s' % name, type_op, size))
 
     # ----------
     # emitting the opcodes for individual types
@@ -928,13 +981,12 @@
             super(NativeIO, self).write(s)
 
 def _make_c_or_py_source(ffi, module_name, preamble, target_file):
-    recompiler = Recompiler(ffi, module_name)
+    recompiler = Recompiler(ffi, module_name,
+                            target_is_python=(preamble is None))
     recompiler.collect_type_table()
+    recompiler.collect_step_tables()
     f = NativeIO()
-    if preamble is not None:
-        recompiler.write_c_source_to_f(f, preamble)
-    else:
-        recompiler.write_py_source_to_f(f)
+    recompiler.write_source_to_f(f, preamble)
     output = f.getvalue()
     try:
         with open(target_file, 'r') as f1:
diff --git a/testing/cffi1/test_dlopen.py b/testing/cffi1/test_dlopen.py
--- a/testing/cffi1/test_dlopen.py
+++ b/testing/cffi1/test_dlopen.py
@@ -13,6 +13,7 @@
 import _cffi_backend
 
 ffi = _cffi_backend.FFI(b'test_simple',
-    _types = b'\x00\x00\x01\r\x00\x00\x07\x01\x00\x00\x00\x0f',
+    _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F',
+    _globals = (b'\xFF\xFF\xFF\x1FBB',42,b'\x00\x00\x00\x23close',0),
 )
 """
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to