Beispiel #1
0
 def _emit_one_ctor_union(self, m, ns, fields, tag_field):
     ## def new_foo(cls, x=0, y=0):
     ##     buf = MyStruct.__new(x=x, y=y, foo=None)
     ##     return cls.from_buffer(buf, 0, ..., ...)
     tag_name = m.py_field_name(tag_field)
     name = 'new_' + tag_name
     fieldtree = FieldTree(m, fields, field_force_default=tag_field)
     argnames, params = fieldtree.get_args_and_params()
     argnames = [(arg, arg) for arg in argnames]
     #
     # workaround for this Cython issue:
     #     * https://github.com/cython/cython/issues/1630
     # apparently, Cython complains if I don't pass a value for all the
     # parameters, even if they have a default value; thus, we explicitly
     # pass a value for all fields which are not explicitly listed
     for f in self.get_struct_fields():
         if f not in fields:
             argnames.append((m.py_field_name(f), '_undefined'))
     #
     ns.w('@classmethod')
     with ns.def_(name, ['cls'] + params):
         newfunc = '{clsname}.__new'.format(clsname=self.compile_name(m))
         call = m.code.call(newfunc, argnames)
         ns.w('buf = {call}', call=call)
         ns.w('return cls.from_buffer(buf, 0, {data_size}, {ptrs_size})')
     ns.w()
Beispiel #2
0
 def __init__(self, m, struct, fields):
     self.m = m
     self.struct = struct
     self.data_size = struct.dataWordCount
     self.ptrs_size = struct.pointerCount
     self.fieldtree = FieldTree(m, self.struct)
     self.argnames, self.params = self.fieldtree.get_args_and_params()
Beispiel #3
0
 def test_args_and_params(self):
     m = self.getm(self.schema)
     person = self.find_struct(m, 'Person')
     tree = FieldTree(m, person)
     args, params = tree.get_args_and_params()
     assert args == ['name', 'address']
     params = [(varname, eval(default)) for varname, default in params]
     assert params == [
         ('name', (None, None)),
         ('address', (None, (42, 0)))
         ]
Beispiel #4
0
 def test_allnodes(self):
     m = self.getm(self.schema)
     person = self.find_struct(m, 'Person')
     tree = FieldTree(m, person)
     nodes = tree.allnodes()
     varnames = [node.varname for node in nodes]
     assert varnames == ['name',
                         'name_first',
                         'name_last',
                         'address',
                         'address_street',
                         'address_position',
                         'address_position_x',
                         'address_position_y']
Beispiel #5
0
 def test_default(self):
     m = self.getm(self.schema)
     person = self.find_struct(m, 'Person')
     tree = FieldTree(m, person)
     items = [(node.varname, node.default) for node in tree.allnodes()]
     assert items == [
         ('name', '(None, None,)'),
         ('name_first', 'None'),
         ('name_last', 'None'),
         ('address', '(None, (42, 0,),)'),
         ('address_street', 'None'),
         ('address_position', '(42, 0,)'),
         ('address_position_x', '42'),
         ('address_position_y', '0'),
     ]
Beispiel #6
0
 def test_pprint(self, capsys):
     m = self.getm(self.schema)
     person = self.find_struct(m, 'Person')
     tree = FieldTree(m, person)
     tree.pprint()
     out, err = capsys.readouterr()
     out = out.strip()
     assert out == textwrap.dedent("""
     <FieldTree>
         <Node name: group>
             <Node name_first: slot>
             <Node name_last: slot>
         <Node address: group>
             <Node address_street: slot>
             <Node address_position: group>
                 <Node address_position_x: slot>
                 <Node address_position_y: slot>
     """).strip()
Beispiel #7
0
 def test_void_args(self):
     schema = """
     @0xbf5147cbbecf40c1;
     struct Foo {
         a @0 :Int64;
         b @1 :Void;
         bar :group {
             c @2 :Int64;
             d @3 :Void;
         }
         baz :union {
             e @4 :Int64;
             f @5 :Void;
         }
     }
     """
     m = self.getm(schema)
     foo = self.find_struct(m, 'Foo')
     tree = FieldTree(m, foo)
     varnames = [node.varname for node in tree.allnodes()]
     assert varnames == ['a', 'bar', 'bar_c', 'baz', 'baz_e', 'baz_f']
Beispiel #8
0
 def test_args_and_params_union(self):
     schema = """
     @0xbf5147cbbecf40c1;
     struct Baz {
         union {
             foo @0 :Int64;
             bar @1 :Int64;
         }
     }
     """
     m = self.getm(schema)
     baz = self.find_struct(m, 'Baz')
     tree = FieldTree(m, baz)
     args, params = tree.get_args_and_params()
     assert params == [
         ('foo', '_undefined'),
         ('bar', '_undefined'),
         ]
     #
     f_bar = baz.struct.fields[1]
     tree = FieldTree(m, baz, field_force_default=f_bar)
     args, params = tree.get_args_and_params()
     assert params == [
         ('foo', '_undefined'),
         ('bar', '0'),
         ]
Beispiel #9
0
 def _emit_ctor_like(self, m, ns, name):
     ## emit something like this:
     ## @staticmethod
     ## def Position(x=0, y=42):
     ##     return x, y
     ##
     groupnode = m.allnodes[self.group.typeId]
     union_default = None
     if groupnode.struct.is_union():
         union_default = '_undefined'
     tree = FieldTree(m, groupnode.struct)
     argnames, params = tree.get_args_and_params()
     #
     ns.argnames = m.code.args(argnames)
     ns.params = m.code.params(params)
     ns.capitalname = ns.name.capitalize()
     ns.ww("""
         @staticmethod
         def {capitalname}({params}):
             return {argnames},
     """)
     ns.w()
Beispiel #10
0
class Structor(object):
    """
    Create a struct constructor.

    Some terminology:
      - argnames: the name of arguments taken by the ctor
      - params: [(argname, default)], for each argname in argnames
    """
    def __init__(self, m, struct, fields):
        self.m = m
        self.struct = struct
        self.data_size = struct.dataWordCount
        self.ptrs_size = struct.pointerCount
        self.fieldtree = FieldTree(m, self.struct)
        self.argnames, self.params = self.fieldtree.get_args_and_params()

    def slot_offset(self, f):
        offset = f.slot.offset * f.slot.get_size()
        if f.slot.type.is_pointer():
            offset += self.data_size * 8
        return offset

    def emit(self):
        ## generate a constructor which looks like this
        ## @staticmethod
        ## def __new(x=0, y=0, z=None):
        ##     builder = _SegmentBuilder()
        ##     pos = builder.allocate(24)
        ##     builder.write_int64(pos + 0, x)
        ##     builder.write_int64(pos + 8, y)
        ##     builder.alloc_text(pos + 16, z)
        ##     return builder.as_string()
        #
        # the parameters have the same order as fields
        code = self.m.code
        argnames = self.argnames
        if len(argnames) != len(set(argnames)):
            raise ValueError("Duplicate field name(s): %s" % argnames)
        #
        code.w('@staticmethod')
        with code.cdef_('__new', self.params) as ns:
            ns.length = (self.data_size + self.ptrs_size) * 8
            ns.cdef_var('_SegmentBuilder', 'builder')
            ns.cdef_var('long', 'pos')
            ns.w('builder = _SegmentBuilder()')
            ns.w('pos = builder.allocate({length})')
            for union in self.fieldtree.all_unions():
                ns.w('{union}__curtag = None', union=union.varname)
            for node in self.fieldtree.children:
                self.handle_node(node)
            ns.w('return builder.as_string()')

    def handle_node(self, node):
        if node.f.is_part_of_union():
            ns = self.m.code.new_scope()
            ns.varname = node.varname
            ns.union = node.parent.union.varname
            ns.offset = node.parent.union.offset
            ns.tagval = node.f.discriminantValue
            ns.tagname = self.m._field_name(node.f)
            ns.ifmt = 'ord(%r)' % Types.int16.fmt
            with ns.block('if {varname} is not _undefined:'):
                ns.w(
                    '{union}__curtag = _check_tag({union}__curtag, {tagname!r})'
                )
                ns.w('builder.write_int16({offset}, {tagval})')
                self._handle_node(node)
        else:
            self._handle_node(node)

    def _handle_node(self, node):
        f = node.f
        if f.is_nullable(self.m):
            self.handle_nullable(node)
        elif f.is_group():
            self.handle_group(node)
        elif f.is_text():
            self.handle_text(node)
        elif f.is_data():
            self.handle_data(node)
        elif f.is_struct():
            self.handle_struct(node)
        elif f.is_list():
            self.handle_list(node)
        elif f.is_primitive() or f.is_enum():
            self.handle_primitive(node)
        elif f.is_bool():
            self.handle_bool(node)
        elif f.is_void():
            pass  # nothing to do
        else:
            self.m.code.w(
                "raise NotImplementedError('Unsupported field type: {f}')",
                f=node.f.shortrepr())

    def handle_group(self, node):
        # def __init__(self, position, ...):
        #     ...
        #     position_x, position_y = position
        #     builder.write_...(..., position_x)
        #     builder.write_...(..., position_y)
        #     ...
        #
        # 1. unpack the tuple into various indepented variables
        ns = self.m.code.new_scope()
        ns.group = node.varname
        argnames = [child.varname for child in node.children]
        ns.args = self.m.code.args(argnames)
        ns.w('{args}, = {group}')
        #
        # 2. recursively handle all the children
        for child in node.children:
            self.handle_node(child)

    def handle_nullable(self, node):
        # def __init__(self, ..., x, ...):
        #     ...
        #     if x is None:
        #         x_is_null = 1
        #         x_value = 0
        #     else:
        #         x_is_null = 0
        #         x_value = x
        #
        ns = self.m.code.new_scope()
        ns.fname = node.varname
        ns.ww("""
            if {fname} is None:
                {fname}_is_null = 1
                {fname}_value = 0
            else:
                {fname}_is_null = 0
                {fname}_value = {fname}
        """)
        for child in node.children:
            self.handle_node(child)

    def handle_text(self, node):
        self.m.code.w('builder.alloc_text(pos + {offset}, {arg})',
                      arg=node.varname,
                      offset=self.slot_offset(node.f))

    def handle_data(self, node):
        self.m.code.w('builder.alloc_data(pos + {offset}, {arg})',
                      arg=node.varname,
                      offset=self.slot_offset(node.f))

    def handle_struct(self, node):
        ## @staticmethod
        ## def __new(x=0, y=<some struct>):
        ##     builder = _SegmentBuilder()
        ##     pos = builder.allocate(16)
        ##     ...
        ##     builder.copy_from_struct(pos+8, SomeStruct, y)
        ns = self.m.code.new_scope()
        ns.fname = node.varname
        ns.offset = self.slot_offset(node.f)
        ns.structname = node.f.slot.type.runtime_name(self.m)
        ns.w('builder.copy_from_struct(pos + {offset}, {structname}, {fname})')

    def handle_list(self, node):
        ns = self.m.code.new_scope()
        ns.fname = node.varname
        ns.offset = self.slot_offset(node.f)
        t = node.f.slot.type.list.elementType
        ns.list_item_type = t.list_item_type(self.m)
        ns.w(
            'builder.copy_from_list(pos + {offset}, {list_item_type}, {fname})'
        )

    def handle_primitive(self, node):
        ns = self.m.code.new_scope()
        ns.arg = node.varname
        if node.f.slot.hadExplicitDefault:
            ns.default_ = node.f.slot.defaultValue.as_pyobj()
            ns.w('{arg} ^= {default_}')
        #
        ns.type = node.f.slot.get_typename()
        ns.offset = self.slot_offset(node.f)
        ns.w('builder.write_{type}(pos + {offset}, {arg})')

    def handle_bool(self, node):
        ns = self.m.code.new_scope()
        ns.arg = node.varname
        ns.byteoffset, ns.bitoffset = divmod(node.f.slot.offset, 8)
        if node.f.slot.hadExplicitDefault:
            ns.default_ = node.f.slot.defaultValue.as_pyobj()
            ns.w('{arg} ^= {default_}')
        ns.w('builder.write_bool({byteoffset}, {bitoffset}, {arg})')