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()
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 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))) ]
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']
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'), ]
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()
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']
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'), ]
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()
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})')