[pypy-commit] creflect default: Functions!

arigo noreply at buildbot.pypy.org
Tue Nov 18 00:03:58 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r60:a5c04855d4c6
Date: 2014-11-18 00:04 +0100
http://bitbucket.org/cffi/creflect/changeset/a5c04855d4c6/

Log:	Functions!

diff --git a/creflect/codegen.py b/creflect/codegen.py
--- a/creflect/codegen.py
+++ b/creflect/codegen.py
@@ -4,7 +4,14 @@
     def __init__(self, parent):
         self.decllines = []
         self.codelines = []
-        self.crx_type_vars = [] if parent is None else parent.crx_type_vars
+        if parent is None:
+            self.crx_type_vars = []
+            self.crx_type_cache = {}
+            self.crx_top_level = self
+        else:
+            self.crx_type_vars = parent.crx_type_vars
+            self.crx_type_cache = parent.crx_type_cache
+            self.crx_top_level = parent.crx_top_level
 
     def writedecl(self, line):
         self.decllines.append(line)
@@ -18,15 +25,31 @@
             self.writeline("    " + line)
         self.writeline("}")
 
-    def add_crx_type_var(self):
-        name = 't%d' % (len(self.crx_type_vars) + 1)
-        self.crx_type_vars.append('*%s' % name)
-        return name
+    def _get_next_name(self, prefix):
+        return '%s%d' % (prefix, len(self.crx_type_vars) + 1)
 
+    def write_crx_type_var(self, expr):
+        if expr in self.crx_type_cache:
+            return self.crx_type_cache[expr]
+        #
+        t1 = self._get_next_name('t')
+        self.crx_type_vars.append('*%s' % t1)
+        self.crx_type_cache[expr] = t1
+        self.writeline('%s = %s;' % (t1, expr))
+        return t1
 
-def transform_cdef(csource, creflect_func_name):
+    def add_array_crx_types(self, array_size):
+        if array_size > 0:
+            a1 = self._get_next_name('a')
+            self.crx_type_vars.append('*%s[%d]' % (a1, array_size))
+            return a1
+        else:
+            return '0'
+
+
+def transform_cdef(csource, crx_func_name):
     outerblock = CodeBlock(parent=None)
-    outerblock.writeline('void %s(crx_builder_t *cb)' % (creflect_func_name,))
+    outerblock.crx_func_name = crx_func_name
 
     funcblock = CodeBlock(outerblock)
 
@@ -37,6 +60,7 @@
         funcblock.writedecl("crx_type_t %s;" % (
             ', '.join(funcblock.crx_type_vars),))
 
+    outerblock.writeline('void %s(crx_builder_t *cb)' % (crx_func_name,))
     outerblock.write_subblock(funcblock)
     outerblock.writeline('')     # for the final \n below
     return '\n'.join(outerblock.codelines)
diff --git a/creflect/cparser.py b/creflect/cparser.py
--- a/creflect/cparser.py
+++ b/creflect/cparser.py
@@ -94,8 +94,8 @@
         node = decl.type
         if isinstance(node, pycparser.c_ast.FuncDecl):
             tp = self._get_type(node)
-            assert isinstance(tp, model.RawFunctionType)
-            tp = self._get_type_pointer(tp)
+            assert isinstance(tp, model.FunctionType)
+            tp = model.PointerType(tp)
             self._declare('function ' + decl.name, tp)
         else:
             const = 'const' in decl.quals
@@ -135,11 +135,6 @@
             realtype = self._get_type(decl.type, approx_name = '$' + decl.name)
         self.declarations.append(model.TypeDefDecl(decl.name, realtype))
 
-    def _get_type_pointer(self, type, const):
-        if isinstance(type, model.RawFunctionType):
-            return type.as_function_pointer()
-        return model.PointerType(type, const)
-
     def _get_type(self, typenode, approx_name=None, partial_length_ok=False):
         # first, dereference typedefs, if we have it already parsed, we're good
         if (isinstance(typenode, pycparser.c_ast.TypeDecl) and
@@ -165,7 +160,7 @@
             if approx_name:
                 approx_name = '$' + approx_name
             realtype = self._get_type(typenode.type, approx_name)
-            return self._get_type_pointer(realtype, const)
+            return model.PointerType(realtype, const)
         #
         if isinstance(typenode, pycparser.c_ast.TypeDecl):
             const = 'const' in typenode.quals
@@ -191,7 +186,7 @@
         #
         if isinstance(typenode, pycparser.c_ast.FuncDecl):
             # a function type
-            return self._parse_function_type(typenode, name)
+            return self._parse_function_type(typenode, approx_name)
         #
         # nested anonymous structs or unions end up here
         if isinstance(typenode, pycparser.c_ast.Struct):
@@ -204,6 +199,36 @@
         raise api.FFIError(":%d: bad or unsupported type declaration" %
                 typenode.coord.line)
 
+    def _parse_function_type(self, typenode, funcname=None):
+        params = list(getattr(typenode.args, 'params', []))
+        ellipsis = (
+            len(params) > 0 and
+            isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and
+            isinstance(params[-1].type.type,
+                       pycparser.c_ast.IdentifierType) and
+            params[-1].type.type.names == ['__dotdotdot__'])
+        if ellipsis:
+            params.pop()
+            if not params:
+                raise api.CDefError(
+                    "%s: a function with only '(...)' as argument"
+                    " is not correct C" % (funcname or 'in expression'))
+        elif (len(params) == 1 and
+            isinstance(params[0].type, pycparser.c_ast.TypeDecl) and
+            isinstance(params[0].type.type, pycparser.c_ast.IdentifierType)
+                and list(params[0].type.type.names) == ['void']):
+            del params[0]
+        args = [self._as_func_arg(self._get_type(argdeclnode.type))
+                for argdeclnode in params]
+        result = self._get_type(typenode.type)
+        return model.FunctionType(tuple(args), result, ellipsis)
+
+    def _as_func_arg(self, type):
+        if isinstance(type, (model.ArrayType, model.FunctionType)):
+            return model.PointerType(type.item)
+        else:
+            return type
+
     def _get_struct_union_enum_type(self, kind, type, const, approx_name=None):
         name = type.name or approx_name
         if not name or name.startswith('$$$'):
diff --git a/creflect/creflect.h b/creflect/creflect.h
--- a/creflect/creflect.h
+++ b/creflect/creflect.h
@@ -10,7 +10,7 @@
 } crx_field_t;
 
 typedef struct crx_type_s crx_type_t;   /* opaque */
-typedef void (*crx_trampoline_t)(void *fn, void *args[], void *res);
+typedef void (*crx_trampoline_fn)(void *fn, void *args[], void *res);
 
 #define CRX_SELF struct _crx_builder_s *
 typedef struct _crx_builder_s {
@@ -24,7 +24,7 @@
     crx_type_t *(*get_float_type)(CRX_SELF, size_t,
                                const char *);
     crx_type_t *(*get_function_type)(CRX_SELF, crx_type_t *,
-                                     crx_type_t *[], int, crx_trampoline_t *);
+                                     crx_type_t *[], int, crx_trampoline_fn);
     crx_type_t *(*get_ellipsis_function_type)(CRX_SELF, crx_type_t *,
                                               crx_type_t *[], int);
     crx_type_t *(*get_pointer_type)(CRX_SELF, crx_type_t *);
diff --git a/creflect/model.py b/creflect/model.py
--- a/creflect/model.py
+++ b/creflect/model.py
@@ -2,13 +2,12 @@
 
 
 class BaseType(object):
-    is_array_type = False
 
-    def _get_c_name(self):
-        return self.c_name_with_marker.replace('&', '')
+    def get_c_name(self, replace_with=''):
+        return self.c_name_with_marker.replace('&', replace_with).rstrip()
 
     def __repr__(self):
-        return '<%s>' % (self._get_c_name(),)
+        return '<%s>' % (self.get_c_name(),)
 
     def _get_items(self):
         return [(name, getattr(self, name))
@@ -17,15 +16,17 @@
     def inspect_type(self, block, inspect):
         t1 = self.inspect_nonconst_type(block, inspect)
         if self.const:
-            t2 = block.add_crx_type_var()
-            block.writeline('%s = cb->get_const_type(cb, %s);' % (t2, t1))
-            return t2
+            return block.write_crx_type_var('cb->get_const_type(cb, %s)' % t1)
         else:
             return t1
 
     def inspect_nonconst_type(self, block, inspect):
         raise NotImplementedError
 
+    def _flush_inspect(self, inspect):
+        if inspect is not None and inspect.started:
+            inspect.assign_to_p1('0')
+
     def __eq__(self, other):
         return (self.__class__ == other.__class__ and
                 self._get_items() == other._get_items())
@@ -43,13 +44,12 @@
     def __init__(self, const):
         self.const = const
         self.c_name_with_marker = 'void &'
+        if const:
+            self.c_name_with_marker = 'const ' + self.c_name_with_marker
 
     def inspect_nonconst_type(self, block, inspect):
-        if inspect.started:
-            inspect.assign_to_p1('0')
-        t1 = block.add_crx_type_var()
-        block.writeline('%s = cb->get_void_type(cb);' % (t1,))
-        return t1
+        self._flush_inspect(inspect)
+        return block.write_crx_type_var('cb->get_void_type(cb)')
 
 void_type = VoidType(const=False)
 const_void_type = VoidType(const=True)
@@ -65,15 +65,15 @@
         'long':               'i',
         'long long':          'i',
         'signed char':        'i',
-        'unsigned char':      'i',
-        'unsigned short':     'i',
-        'unsigned int':       'i',
-        'unsigned long':      'i',
-        'unsigned long long': 'i',
+        'unsigned char':      'u',
+        'unsigned short':     'u',
+        'unsigned int':       'u',
+        'unsigned long':      'u',
+        'unsigned long long': 'u',
         'float':              'f',
         'double':             'f',
         'long double':        'f',
-        '_Bool':              'i',
+        '_Bool':              'u',
         }
 
     def __init__(self, name, const):
@@ -87,80 +87,147 @@
     def is_char_type(self):
         return self.ALL_PRIMITIVE_TYPES[self.name] == 'c'
     def is_integer_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] in 'iu'
+    def is_signed_type(self):
         return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'
+    def is_unsigned_type(self):
+        return self.ALL_PRIMITIVE_TYPES[self.name] == 'u'
     def is_float_type(self):
         return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
 
     def inspect_nonconst_type(self, block, inspect):
-        star_p1 = inspect.fetch_star_p1()
-        comment1 = inspect.get_comment(0, False, "a valid type", minlevel=1)
-        comment2 = inspect.get_comment(0, False, "an integer type")
-        comment3 = inspect.get_comment(0, False, "not declared 'const'")
-        block.writedecl("char b[sizeof(%s)];%s" % (star_p1, comment1))
-        if self.const:
-            block.writeline("memset(b, -1, sizeof(b));")
-        inspect.assign_to_p1("b")
-        block.writeline("(void)(%s << 1);%s" % (star_p1, comment2))
-        if not self.const:
-            block.writeline("%s = -1;%s" % (star_p1, comment3))
-        t1 = block.add_crx_type_var()
-        if self.is_integer_type():
-            block.writeline('%s = CRX_INT_TYPE(cb, %s, "%s");' % (
-                t1, star_p1, self.name))
-        elif self.is_char_type():
-            block.writeline('%s = cb->get_char_type(cb);' % (t1,))
-        elif self.is_float_type():
-            xxx
+        if inspect is None:
+            if self.is_signed_type():
+                expr = 'cb->get_signed_type(cb, sizeof(%s), "%s")' % (
+                    self.name, self.name)
+            elif self.is_unsigned_type():
+                expr = 'cb->get_unsigned_type(cb, sizeof(%s), "%s")' % (
+                    self.name, self.name)
+            elif self.is_char_type():
+                expr = 'cb->get_char_type(cb)'
+            elif self.is_float_type():
+                xxx
+            else:
+                raise AssertionError
         else:
-            raise AssertionError
-        return t1
+            star_p1 = inspect.fetch_star_p1()
+            comment1 = inspect.get_comment(0, False, "a valid type", minlevel=1)
+            comment2 = inspect.get_comment(0, False, "an integer type")
+            comment3 = inspect.get_comment(0, False, "not declared 'const'")
+            block.writedecl("char b[sizeof(%s)];%s" % (star_p1, comment1))
+            if self.const:
+                block.writeline("memset(b, -1, sizeof(b));")
+            inspect.assign_to_p1("b")
+            block.writeline("(void)(%s << 1);%s" % (star_p1, comment2))
+            if not self.const:
+                block.writeline("%s = -1;%s" % (star_p1, comment3))
+            if self.is_integer_type():
+                expr = 'CRX_INT_TYPE(cb, %s, "%s")' % (star_p1, self.name)
+            elif self.is_char_type():
+                expr = 'cb->get_char_type(cb)'
+            elif self.is_float_type():
+                xxx
+            else:
+                raise AssertionError
+        return block.write_crx_type_var(expr)
 
 
-class BaseFunctionType(BaseType):
-    pass
+class FunctionType(BaseType):
+    _attrs_ = ('args', 'result', 'ellipsis')
+    const = True
 
-class RawFunctionType(BaseFunctionType):
-    pass
+    def __init__(self, args, result, ellipsis):
+        self.args = args
+        self.result = result
+        self.ellipsis = ellipsis
+        #
+        reprargs = [arg.get_c_name() for arg in self.args]
+        if self.ellipsis:
+            reprargs.append('...')
+        reprargs = reprargs or ['void']
+        replace_with = '&(%s)' % (', '.join(reprargs),)
+        self.c_name_with_marker = (
+            self.result.c_name_with_marker.replace('&', replace_with))
+
+    def inspect_type(self, block, inspect):
+        # this class overrides inspect_type() instead of
+        # inspect_nonconst_type(), to avoid the extra call to get_const_type()
+        self._flush_inspect(inspect)
+        assert not self.ellipsis, "XXX"
+        t1 = self.result.inspect_type(block, None)
+        call = ['    ']
+        if not isinstance(self.result, VoidType):
+            call.append('*(%s)result = ' % (self.result.get_c_name('*'),))
+        call.append('f(')
+        t_args = [arg.inspect_type(block, None) for arg in self.args]
+        a2 = block.add_array_crx_types(len(t_args))
+        for i, t in enumerate(t_args):
+            block.writeline('%s[%d] = %s;' % (a2, i, t))
+            if i > 0:
+                call.append(', ')
+            call.append('*(%s)args[%d]' % (self.args[i].get_c_name('*'), i))
+        call.append(');')
+        #
+        toplevel = block.crx_top_level
+        crx_func_name = '%s__%s' % (toplevel.crx_func_name,
+                                    block._get_next_name('f'))
+        wrline = toplevel.writeline
+        wrline('static void %s(void *func, void *args[], void *result) {' % (
+            crx_func_name,))
+        wrline('    func_t f = func;')  # XXX!
+        wrline(''.join(call))
+        wrline('}')
+        wrline('')
+        expr = 'cb->get_function_type(cb, %s, %s, %d, &%s)' % (
+            t1, a2, len(t_args), crx_func_name)
+        return block.write_crx_type_var(expr)
+
 
 class PointerType(BaseType):
     _attrs_ = ('totype',)
+    _base_pattern       = {
+        (False, False): "*&",
+        (True, False): "(*&)",
+        (False, True): "*const &",
+        (True, True): "(*const &)",
+        }
 
     def __init__(self, totype, const):
         self.totype = totype
         self.const = const
         base = self.totype.c_name_with_marker
-        self.c_name_with_marker = base.replace('&', '(*&)')
+        parens = isinstance(self.totype, (ArrayType, FunctionType))
+        extra = self._base_pattern[parens, self.const]
+        self.c_name_with_marker = base.replace('&', extra)
 
     def inspect_nonconst_type(self, block, inspect):
-        star_p1 = inspect.fetch_star_p1()
-        new_var = 'p%d' % (len(inspect.levels) + 2,)
-        block.writedecl("char *%s;" % (new_var,))
-        inspect.assign_to_p1("&%s" % (new_var,))
-        #
-        if self.const:
-            errmsg = "type '%s' is not a pointer, but an array type" % (
-                inspect.get_comment_type(0, False),)
-            inspect.assign_target = new_var
-            def after():
-                ampersand_star_p1 = '&' + star_p1
-                if ampersand_star_p1.startswith('&*'):
-                    ampersand_star_p1 = ampersand_star_p1[2:]
-                block.writeline("if ((void *)%s == (void *)%s) {" % (
-                    ampersand_star_p1, star_p1))
-                block.writeline('    cb->error(cb, "%s");' % (errmsg,))
-                block.writeline("    return;")
-                block.writeline("}")
-            inspect.after_star_p1_assignment.append(after)
-        inspect.levels.append('*')
+        if inspect is not None:
+            star_p1 = inspect.fetch_star_p1()
+            new_var = 'p%d' % (len(inspect.levels) + 2,)
+            block.writedecl("char *%s;" % (new_var,))
+            inspect.assign_to_p1("&%s" % (new_var,))
+            #
+            if self.const:
+                errmsg = "type '%s' is not a pointer, but an array type" % (
+                    inspect.get_comment_type(0, False),)
+                inspect.assign_target = new_var
+                def after():
+                    ampersand_star_p1 = '&' + star_p1
+                    if ampersand_star_p1.startswith('&*'):
+                        ampersand_star_p1 = ampersand_star_p1[2:]
+                    block.writeline("if ((void *)%s == (void *)%s) {" % (
+                        ampersand_star_p1, star_p1))
+                    block.writeline('    cb->error(cb, "%s");' % (errmsg,))
+                    block.writeline("    return;")
+                    block.writeline("}")
+                inspect.after_star_p1_assignment.append(after)
+            inspect.levels.append('*')
         t1 = self.totype.inspect_type(block, inspect)
-        t2 = block.add_crx_type_var()
-        block.writeline('%s = cb->get_pointer_type(cb, %s);' % (t2, t1))
-        return t2
+        return block.write_crx_type_var('cb->get_pointer_type(cb, %s)' % t1)
 
 
 class ArrayType(BaseType):
     _attrs_ = ('item', 'length')
-    is_array_type = True
     const = True     # not really applicable, but must have one
 
     def __init__(self, item, length):
@@ -179,26 +246,25 @@
     def inspect_type(self, block, inspect):
         # this class overrides inspect_type() instead of
         # inspect_nonconst_type(), to avoid the extra call to get_const_type()
-        star_p1 = inspect.fetch_star_p1()
-        errmsg = "type '%s' is not an array, but a pointer type" % (
-            inspect.get_comment_type(0, False),)
-        def after():
-            ampersand_star_p1 = '&' + star_p1
-            if ampersand_star_p1.startswith('&*'):
-                ampersand_star_p1 = ampersand_star_p1[2:]
-            block.writeline("if ((void *)%s != (void *)%s) {" % (
-                ampersand_star_p1, star_p1))
-            block.writeline('    cb->error(cb, "%s");' % (errmsg,))
-            block.writeline("    return;")
-            block.writeline("}")
-        inspect.levels.append('[]')
-        inspect.after_star_p1_assignment.append(after)
+        if inspect is not None:
+            star_p1 = inspect.fetch_star_p1()
+            errmsg = "type '%s' is not an array, but a pointer type" % (
+                inspect.get_comment_type(0, False),)
+            def after():
+                ampersand_star_p1 = '&' + star_p1
+                if ampersand_star_p1.startswith('&*'):
+                    ampersand_star_p1 = ampersand_star_p1[2:]
+                block.writeline("if ((void *)%s != (void *)%s) {" % (
+                    ampersand_star_p1, star_p1))
+                block.writeline('    cb->error(cb, "%s");' % (errmsg,))
+                block.writeline("    return;")
+                block.writeline("}")
+            inspect.levels.append('[]')
+            inspect.after_star_p1_assignment.append(after)
         t1 = self.item.inspect_type(block, inspect)
-        t2 = block.add_crx_type_var()
-        block.writeline('%s = cb->get_array_type(cb, %s, '
-                        'sizeof(%s) / sizeof(*%s));' % (
-            t2, t1, star_p1, star_p1))
-        return t2
+        expr = 'cb->get_array_type(cb, %s, sizeof(%s) / sizeof(*%s));' % (
+            t1, star_p1, star_p1)
+        return block.write_crx_type_var(expr)
 
 
 class StructOrUnionOrEnum(BaseType):
diff --git a/test/cgcompile.c b/test/cgcompile.c
--- a/test/cgcompile.c
+++ b/test/cgcompile.c
@@ -80,9 +80,16 @@
 
 static crx_type_t *tst_get_function_type(crx_builder_t *cb, crx_type_t *ret,
                                          crx_type_t *args[], int nargs,
-                                         crx_trampoline_t *trampl)
+                                         crx_trampoline_fn trampl)
 {
-    abort();
+    int i;
+    const char *prev = "FUNC( ";
+    for (i = 0; i < nargs; i++) {
+        prev = newtype2(prev, args[i]->text)->text;
+        prev = newtype2(prev, " -> ")->text;
+    }
+    prev = newtype2(prev, ret->text)->text;
+    return newtype2(prev, " )");
 }
 
 static crx_type_t *tst_get_ellipsis_function_type(crx_builder_t *cb,
diff --git a/test/codegen/func-003.c b/test/codegen/func-003.c
--- a/test/codegen/func-003.c
+++ b/test/codegen/func-003.c
@@ -1,15 +1,15 @@
-typedef int(*func_t)(long, long);
+typedef int(*func_t)(long, long, int);
 
 # ____________________________________________________________
 
-static void __creflect_t3(void *func, void *args[], void *result) {
+static void testfunc_003__f4(void *func, void *args[], void *result) {
     func_t f = func;
-    *(int *)result = f(*(long *)args[0], *(long *)args[1]);
+    *(int *)result = f(*(long *)args[0], *(long *)args[1], *(int *)args[2]);
 }
 
 void testfunc_003(crx_builder_t *cb)
 {
-    crx_type *t1, *t2, *a3[2], *t3;
+    crx_type_t *t1, *t2, *a3[3], *t4, *t5;
     {
         func_t *p1;
         char *p2;
@@ -19,7 +19,10 @@
         t2 = cb->get_signed_type(cb, sizeof(long), "long");
         a3[0] = t2;
         a3[1] = t2;
-        t3 = cb->get_function_type(cb, t1, a3, 2, &__creflect_t3);
-        cb->define_type(cb, "func_t", t3);
+        a3[2] = t1;
+        t4 = cb->get_function_type(cb, t1, a3, 3, &testfunc_003__f4);
+        t5 = cb->get_pointer_type(cb, t4);
+        cb->define_type(cb, "func_t", t5);
+#expect TYPEDEF func_t = PTR FUNC( long -> long -> int -> int )
     }
 }


More information about the pypy-commit mailing list