''' This module contains the classes which represent XCB data types. ''' from xcbgen.expr import Field, Expression from xcbgen.align import Alignment, AlignmentLog import __main__ verbose_align_log = False true_values = ['true', '1', 'yes'] class Type(object): ''' Abstract base class for all XCB data types. Contains default fields, and some abstract methods. ''' def __init__(self, name): ''' Default structure initializer. Sets up default fields. Public fields: name is a tuple of strings specifying the full type name. size is the size of the datatype in bytes, or None if variable-sized. nmemb is 1 for non-list types, None for variable-sized lists, otherwise number of elts. booleans for identifying subclasses, because I can't figure out isinstance(). ''' self.name = name self.size = None self.nmemb = None self.resolved = False # Screw isinstance(). self.is_simple = False self.is_list = False self.is_expr = False self.is_container = False self.is_reply = False self.is_union = False self.is_pad = False self.is_eventstruct = False self.is_event = False self.is_switch = False self.is_case_or_bitcase = False self.is_bitcase = False self.is_case = False self.is_fd = False self.required_start_align = Alignment() # the biggest align value of an align-pad contained in this type self.max_align_pad = 1 def resolve(self, module): ''' Abstract method for resolving a type. This should make sure any referenced types are already declared. ''' raise Exception('abstract resolve method not overridden!') def out(self, name): ''' Abstract method for outputting code. These are declared in the language-specific modules, and there must be a dictionary containing them declared when this module is imported! ''' raise Exception('abstract out method not overridden!') def fixed_size(self): ''' Abstract method for determining if the data type is fixed-size. ''' raise Exception('abstract fixed_size method not overridden!') def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum=None, is_fd=False): ''' Default method for making a data type a member of a structure. Extend this if the data type needs to add an additional length field or something. module is the global module object. complex_type is the structure object. see Field for the meaning of the other parameters. ''' new_field = Field(self, field_type, field_name, visible, wire, auto, enum, is_fd) # We dump the _placeholder_byte if any fields are added. for (idx, field) in enumerate(complex_type.fields): if field == _placeholder_byte: complex_type.fields[idx] = new_field return complex_type.fields.append(new_field) new_field.parent = complex_type def make_fd_of(self, module, complex_type, fd_name): ''' Method for making a fd member of a structure. ''' new_fd = Field(self, module.get_type_name('INT32'), fd_name, True, False, False, None, True) # We dump the _placeholder_byte if any fields are added. for (idx, field) in enumerate(complex_type.fields): if field == _placeholder_byte: complex_type.fields[idx] = new_fd return complex_type.fields.append(new_fd) def get_total_size(self): ''' get the total size of this type if it is fixed-size, otherwise None ''' if self.fixed_size(): if self.nmemb is None: return self.size else: return self.size * self.nmemb else: return None def get_align_offset(self): if self.required_start_align is None: return 0 else: return self.required_start_align.offset def is_acceptable_start_align(self, start_align, callstack, log): return self.get_alignment_after(start_align, callstack, log) is not None def get_alignment_after(self, start_align, callstack, log): ''' get the alignment after this type based on the given start_align. the start_align is checked for compatibility with the internal start align. If it is not compatible, then None is returned ''' if self.required_start_align is None or self.required_start_align.is_guaranteed_at(start_align): return self.unchecked_get_alignment_after(start_align, callstack, log) else: if log is not None: log.fail(start_align, "", self, callstack + [self], "start_align is incompatible with required_start_align %s" % (str(self.required_start_align))) return None def unchecked_get_alignment_after(self, start_align, callstack, log): ''' Abstract method for geting the alignment after this type when the alignment at the start is given, and when this type has variable size. ''' raise Exception('abstract unchecked_get_alignment_after method not overridden!') @staticmethod def type_name_to_str(type_name): if isinstance(type_name, str): #already a string return type_name else: return ".".join(type_name) def __str__(self): return type(self).__name__ + " \"" + Type.type_name_to_str(self.name) + "\"" class PrimitiveType(Type): def __init__(self, name, size): Type.__init__(self, name) self.size = size self.nmemb = 1 # compute the required start_alignment based on the size of the type self.required_start_align = Alignment.for_primitive_type(self.size) def unchecked_get_alignment_after(self, start_align, callstack, log): my_callstack = callstack + [self]; after_align = start_align.align_after_fixed_size(self.size) if log is not None: if after_align is None: log.fail(start_align, "", self, my_callstack, "align after fixed size %d failed" % self.size) else: log.ok(start_align, "", self, my_callstack, after_align) return after_align def fixed_size(self): return True class SimpleType(PrimitiveType): ''' Derived class which represents a cardinal type like CARD32 or char. Any type which is typedef'ed to cardinal will be one of these. Public fields added: none ''' def __init__(self, name, size): PrimitiveType.__init__(self, name, size) self.is_simple = True def resolve(self, module): self.resolved = True out = __main__.output['simple'] # Cardinal datatype globals. See module __init__ method. tcard8 = SimpleType(('uint8_t',), 1) tcard16 = SimpleType(('uint16_t',), 2) tcard32 = SimpleType(('uint32_t',), 4) tcard64 = SimpleType(('uint64_t',), 8) tint8 = SimpleType(('int8_t',), 1) tint16 = SimpleType(('int16_t',), 2) tint32 = SimpleType(('int32_t',), 4) tint64 = SimpleType(('int64_t',), 8) tchar = SimpleType(('char',), 1) tfloat = SimpleType(('float',), 4) tdouble = SimpleType(('double',), 8) class FileDescriptor(SimpleType): ''' Derived class which represents a file descriptor. ''' def __init__(self): SimpleType.__init__(self, ('int'), 4) self.is_fd = True def fixed_size(self): return True out = __main__.output['simple'] class Enum(SimpleType): ''' Derived class which represents an enum. Fixed-size. Public fields added: values contains a list of (name, value) tuples. value is empty, or a number. bits contains a list of (name, bitnum) tuples. items only appear if specified as a bit. bitnum is a number. ''' def __init__(self, name, elt): SimpleType.__init__(self, name, 4) self.values = [] self.bits = [] self.doc = None for item in list(elt): if item.tag == 'doc': self.doc = Doc(name, item) # First check if we're using a default value if len(list(item)) == 0: self.values.append((item.get('name'), '')) continue # An explicit value or bit was specified. value = list(item)[0] if value.tag == 'value': self.values.append((item.get('name'), value.text)) elif value.tag == 'bit': self.values.append((item.get('name'), '%u' % (1 << int(value.text, 0)))) self.bits.append((item.get('name'), value.text)) def resolve(self, module): self.resolved = True def fixed_size(self): return True out = __main__.output['enum'] class ListType(Type): ''' Derived class which represents a list of some other datatype. Fixed- or variable-sized. Public fields added: member is the datatype of the list elements. parent is the structure type containing the list. expr is an Expression object containing the length information, for variable-sized lists. ''' def __init__(self, elt, member, *parent): Type.__init__(self, member.name) self.is_list = True self.member = member self.parents = list(parent) lenfield_name = False if elt.tag == 'list': elts = list(elt) self.expr = Expression(elts[0] if len(elts) else elt, self) is_list_in_parent = self.parents[0].elt.tag in ('request', 'event', 'reply', 'error') if not len(elts) and is_list_in_parent: self.expr = Expression(elt,self) self.expr.op = 'calculate_len' else: self.expr = Expression(elts[0] if len(elts) else elt, self) self.size = member.size if member.fixed_size() else None self.nmemb = self.expr.nmemb if self.expr.fixed_size() else None self.required_start_align = self.member.required_start_align def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum=None): if not self.fixed_size(): # We need a length field. # Ask our Expression object for it's name, type, and whether it's on the wire. lenfid = self.expr.lenfield_type lenfield_name = self.expr.lenfield_name lenwire = self.expr.lenwire needlen = True # See if the length field is already in the structure. for parent in self.parents: for field in parent.fields: if field.field_name == lenfield_name: needlen = False # It isn't, so we need to add it to the structure ourself. if needlen: type = module.get_type(lenfid) lenfield_type = module.get_type_name(lenfid) type.make_member_of(module, complex_type, lenfield_type, lenfield_name, True, lenwire, False, enum) # Add ourself to the structure by calling our original method. if self.member.is_fd: wire = False Type.make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum, self.member.is_fd) def resolve(self, module): if self.resolved: return self.member.resolve(module) self.expr.resolve(module, self.parents) self.required_start_align = self.member.required_start_align # Find my length field again. We need the actual Field object in the expr. # This is needed because we might have added it ourself above. if not self.fixed_size(): for parent in self.parents: for field in parent.fields: if field.field_name == self.expr.lenfield_name and field.wire: self.expr.lenfield = field break self.resolved = True def fixed_size(self): return self.member.fixed_size() and self.expr.fixed_size() def unchecked_get_alignment_after(self, start_align, callstack, log): my_callstack = callstack[:] my_callstack.append(self) if start_align is None: log.fail(start_align, "", self, my_callstack, "start_align is None") return None if self.expr.fixed_size(): # fixed number of elements num_elements = self.nmemb prev_alignment = None alignment = start_align while num_elements > 0: if alignment is None: if log is not None: log.fail(start_align, "", self, my_callstack, ("fixed size list with size %d after %d iterations" + ", at transition from alignment \"%s\"") % (self.nmemb, (self.nmemb - num_elements), str(prev_alignment))) return None prev_alignment = alignment alignment = self.member.get_alignment_after(prev_alignment, my_callstack, log) num_elements -= 1 if log is not None: log.ok(start_align, "", self, my_callstack, alignment) return alignment else: # variable number of elements # check whether the number of elements is a multiple multiple = self.expr.get_multiple() assert multiple > 0 # iterate until the combined alignment does not change anymore alignment = start_align while True: prev_multiple_alignment = alignment # apply "multiple" amount of changes sequentially prev_alignment = alignment for multiple_count in range(0, multiple): after_alignment = self.member.get_alignment_after(prev_alignment, my_callstack, log) if after_alignment is None: if log is not None: log.fail(start_align, "", self, my_callstack, ("variable size list " + "at transition from alignment \"%s\"") % (str(prev_alignment))) return None prev_alignment = after_alignment # combine with the cumulatively combined alignment # (to model the variable number of entries) alignment = prev_multiple_alignment.combine_with(after_alignment) if alignment == prev_multiple_alignment: # does not change anymore by adding more potential elements # -> finished if log is not None: log.ok(start_align, "", self, my_callstack, alignment) return alignment class ExprType(PrimitiveType): ''' Derived class which represents an exprfield. Fixed size. Public fields added: expr is an Expression object containing the value of the field. ''' def __init__(self, elt, member, *parents): PrimitiveType.__init__(self, member.name, member.size) self.is_expr = True self.member = member self.parents = parents self.expr = Expression(list(elt)[0], self) def resolve(self, module): if self.resolved: return self.member.resolve(module) self.resolved = True class PadType(Type): ''' Derived class which represents a padding field. ''' def __init__(self, elt): Type.__init__(self, tcard8.name) self.is_pad = True self.size = 1 self.nmemb = 1 self.align = 1 if elt != None: self.nmemb = int(elt.get('bytes', "1"), 0) self.align = int(elt.get('align', "1"), 0) self.serialize = elt.get('serialize', "false").lower() in true_values # pads don't require any alignment at their start self.required_start_align = Alignment(1,0) def resolve(self, module): self.resolved = True def fixed_size(self): return self.align <= 1 def unchecked_get_alignment_after(self, start_align, callstack, log): if self.align <= 1: # fixed size pad after_align = start_align.align_after_fixed_size(self.get_total_size()) if log is not None: if after_align is None: log.fail(start_align, "", self, callstack, "align after fixed size pad of size %d failed" % self.size) else: log.ok(start_align, "", self, callstack, after_align) return after_align # align-pad assert self.align > 1 assert self.size == 1 assert self.nmemb == 1 if (start_align.offset == 0 and self.align <= start_align.align and start_align.align % self.align == 0): # the alignment pad is size 0 because the start_align # is already sufficiently aligned -> return the start_align after_align = start_align else: # the alignment pad has nonzero size -> return the alignment # that is guaranteed by it, independently of the start_align after_align = Alignment(self.align, 0) if log is not None: log.ok(start_align, "", self, callstack, after_align) return after_align class ComplexType(Type): ''' Derived class which represents a structure. Base type for all structure types. Public fields added: fields is an array of Field objects describing the structure fields. ''' def __init__(self, name, elt): Type.__init__(self, name) self.is_container = True self.elt = elt self.fields = [] self.nmemb = 1 self.size = 0 self.lenfield_parent = [self] self.fds = [] # get required_start_alignment required_start_align_element = elt.find("required_start_align") if required_start_align_element is None: # unknown -> mark for autocompute self.required_start_align = None else: self.required_start_align = Alignment( int(required_start_align_element.get('align', "4"), 0), int(required_start_align_element.get('offset', "0"), 0)) if verbose_align_log: print ("Explicit start-align for %s: %s\n" % (self, self.required_start_align)) def resolve(self, module): if self.resolved: return enum = None # Resolve all of our field datatypes. for child in list(self.elt): if child.tag == 'pad': field_name = 'pad' + str(module.pads) fkey = 'CARD8' type = PadType(child) module.pads = module.pads + 1 visible = False elif child.tag == 'field': field_name = child.get('name') enum = child.get('enum') fkey = child.get('type') type = module.get_type(fkey) visible = True elif child.tag == 'exprfield': field_name = child.get('name') fkey = child.get('type') type = ExprType(child, module.get_type(fkey), *self.lenfield_parent) visible = False elif child.tag == 'list': field_name = child.get('name') fkey = child.get('type') if fkey == 'fd': ftype = FileDescriptor() fkey = 'INT32' else: ftype = module.get_type(fkey) type = ListType(child, ftype, *self.lenfield_parent) visible = True elif child.tag == 'switch': field_name = child.get('name') # construct the switch type name from the parent type and the field name field_type = self.name + (field_name,) type = SwitchType(field_type, child, *self.lenfield_parent) visible = True type.make_member_of(module, self, field_type, field_name, visible, True, False) type.resolve(module) continue elif child.tag == 'fd': fd_name = child.get('name') type = module.get_type('INT32') type.make_fd_of(module, self, fd_name) continue else: # Hit this on Reply continue # Get the full type name for the field field_type = module.get_type_name(fkey) # Add the field to ourself type.make_member_of(module, self, field_type, field_name, visible, True, False, enum) # Recursively resolve the type (could be another structure, list) type.resolve(module) # Compute the size of the maximally contain align-pad if type.max_align_pad > self.max_align_pad: self.max_align_pad = type.max_align_pad self.check_implicit_fixed_size_part_aligns(); self.calc_size() # Figure out how big we are self.calc_or_check_required_start_align() self.resolved = True def calc_size(self): self.size = 0 for m in self.fields: if not m.wire: continue if m.type.fixed_size(): self.size = self.size + m.type.get_total_size() else: self.size = None break def calc_or_check_required_start_align(self): if self.required_start_align is None: # no required-start-align configured -> calculate it log = AlignmentLog() callstack = [] self.required_start_align = self.calc_minimally_required_start_align(callstack, log) if self.required_start_align is None: print ("ERROR: could not calc required_start_align of %s\nDetails:\n%s" % (str(self), str(log))) else: if verbose_align_log: print ("calc_required_start_align: %s has start-align %s" % (str(self), str(self.required_start_align))) print ("Details:\n" + str(log)) if self.required_start_align.offset != 0: print (("WARNING: %s\n\thas start-align with non-zero offset: %s" + "\n\tsuggest to add explicit definition with:" + "\n\t\t" + "\n\tor to fix the xml so that zero offset is ok\n") % (str(self), self.required_start_align, self.required_start_align.align, self.required_start_align.offset)) else: # required-start-align configured -> check it log = AlignmentLog() callstack = [] if not self.is_possible_start_align(self.required_start_align, callstack, log): print ("ERROR: required_start_align %s of %s causes problems\nDetails:\n%s" % (str(self.required_start_align), str(self), str(log))) def calc_minimally_required_start_align(self, callstack, log): # calculate the minimally required start_align that causes no # align errors best_log = None best_failed_align = None for align in [1,2,4,8]: for offset in range(0,align): align_candidate = Alignment(align, offset) if verbose_align_log: print ("trying %s for %s" % (str(align_candidate), str(self))) my_log = AlignmentLog() if self.is_possible_start_align(align_candidate, callstack, my_log): log.append(my_log) if verbose_align_log: print ("found start-align %s for %s" % (str(align_candidate), str(self))) return align_candidate else: my_ok_count = my_log.ok_count() if (best_log is None or my_ok_count > best_log.ok_count() or (my_ok_count == best_log.ok_count() and align_candidate.align > best_failed_align.align) and align_candidate.align != 8): best_log = my_log best_failed_align = align_candidate # none of the candidates applies # this type has illegal internal aligns for all possible start_aligns if verbose_align_log: print ("didn't find start-align for %s" % str(self)) log.append(best_log) return None def is_possible_start_align(self, align, callstack, log): if align is None: return False if (self.max_align_pad > align.align or align.align % self.max_align_pad != 0): # our align pad implementation depends on known alignment # at the start of our type return False return self.get_alignment_after(align, callstack, log) is not None def fixed_size(self): for m in self.fields: if not m.type.fixed_size(): return False return True # default impls of polymorphic methods which assume sequential layout of fields # (like Struct or CaseOrBitcaseType) def check_implicit_fixed_size_part_aligns(self): # find places where the implementation of the C-binding would # create code that makes the compiler add implicit alignment. # make these places explicit, so we have # consistent behaviour for all bindings size = 0 for field in self.fields: if not field.wire: continue if not field.type.fixed_size(): # end of fixed-size part break required_field_align = field.type.required_start_align if required_field_align is None: raise Exception( "field \"%s\" in \"%s\" has not required_start_align" % (field.field_name, self.name) ) mis_align = (size + required_field_align.offset) % required_field_align.align if mis_align != 0: # implicit align pad is required padsize = required_field_align.align - mis_align raise Exception( "C-compiler would insert implicit alignpad of size %d before field \"%s\" in \"%s\"" % (padsize, field.field_name, self.name) ) def unchecked_get_alignment_after(self, start_align, callstack, log): # default impl assumes sequential layout of fields # (like Struct or CaseOrBitcaseType) my_align = start_align if my_align is None: return None for field in self.fields: if not field.wire: continue my_callstack = callstack[:] my_callstack.extend([self, field]) prev_align = my_align my_align = field.type.get_alignment_after(my_align, my_callstack, log) if my_align is None: if log is not None: log.fail(prev_align, field.field_name, self, my_callstack, "alignment is incompatible with this field") return None else: if log is not None: log.ok(prev_align, field.field_name, self, my_callstack, my_align) if log is not None: my_callstack = callstack[:] my_callstack.append(self) log.ok(start_align, "", self, my_callstack, my_align) return my_align class SwitchType(ComplexType): ''' Derived class which represents a List of Items. Public fields added: bitcases is an array of Bitcase objects describing the list items ''' def __init__(self, name, elt, *parents): ComplexType.__init__(self, name, elt) self.parents = parents # FIXME: switch cannot store lenfields, so it should just delegate the parents self.lenfield_parent = list(parents) + [self] # self.fields contains all possible fields collected from the Bitcase objects, # whereas self.items contains the Bitcase objects themselves self.bitcases = [] self.is_switch = True elts = list(elt) self.expr = Expression(elts[0] if len(elts) else elt, self) def resolve(self, module): if self.resolved: return parents = list(self.parents) + [self] # Resolve all of our field datatypes. for index, child in enumerate(list(self.elt)): if child.tag == 'bitcase' or child.tag == 'case': field_name = child.get('name') if field_name is None: field_type = self.name + ('%s%d' % ( child.tag, index ),) else: field_type = self.name + (field_name,) # use self.parent to indicate anchestor, # as switch does not contain named fields itself if child.tag == 'bitcase': type = BitcaseType(index, field_type, child, *parents) else: type = CaseType(index, field_type, child, *parents) # construct the switch type name from the parent type and the field name if field_name is None: type.has_name = False # Get the full type name for the field field_type = type.name visible = True # add the field to ourself type.make_member_of(module, self, field_type, field_name, visible, True, False) # recursively resolve the type (could be another structure, list) type.resolve(module) inserted = False for new_field in type.fields: # We dump the _placeholder_byte if any fields are added. for (idx, field) in enumerate(self.fields): if field == _placeholder_byte: self.fields[idx] = new_field inserted = True break if False == inserted: self.fields.append(new_field) self.calc_size() # Figure out how big we are self.calc_or_check_required_start_align() self.resolved = True def make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum=None): if not self.fixed_size(): # We need a length field. # Ask our Expression object for it's name, type, and whether it's on the wire. lenfid = self.expr.lenfield_type lenfield_name = self.expr.lenfield_name lenwire = self.expr.lenwire needlen = True # See if the length field is already in the structure. for parent in self.parents: for field in parent.fields: if field.field_name == lenfield_name: needlen = False # It isn't, so we need to add it to the structure ourself. if needlen: type = module.get_type(lenfid) lenfield_type = module.get_type_name(lenfid) type.make_member_of(module, complex_type, lenfield_type, lenfield_name, True, lenwire, False, enum) # Add ourself to the structure by calling our original method. Type.make_member_of(self, module, complex_type, field_type, field_name, visible, wire, auto, enum) # size for switch can only be calculated at runtime def calc_size(self): pass # note: switch is _always_ of variable size, but we indicate here wether # it contains elements that are variable-sized themselves def fixed_size(self): return False # for m in self.fields: # if not m.type.fixed_size(): # return False # return True def check_implicit_fixed_size_part_aligns(self): # this is done for the CaseType or BitCaseType return def unchecked_get_alignment_after(self, start_align, callstack, log): # we assume that BitCases can appear in any combination, # and that at most one Case can appear # (assuming that Cases are mutually exclusive) # get all Cases (we assume that at least one case is selected if there are cases) case_fields = [] for field in self.bitcases: if field.type.is_case: case_fields.append(field) if not case_fields: # there are no case-fields -> check without case-fields case_fields = [None] my_callstack = callstack[:] my_callstack.append(self) # total_align = None first = True for case_field in case_fields: my2_callstack = my_callstack[:] if case_field is not None: my2_callstack.append(case_field) case_align = self.get_align_for_selected_case_field( case_field, start_align, my2_callstack, log) if case_align is None: if log is not None: if case_field is None: log.fail(start_align, "", self, my2_callstack, "alignment without cases (only bitcases) failed") else: log.fail(start_align, "", self, my2_callstack + [case_field], "alignment for selected case %s failed" % case_field.field_name) return None if first: total_align = case_align else: total_align = total_align.combine_with(case_align) if log is not None: if case_field is None: log.ok( start_align, "without cases (only arbitrary bitcases)", self, my2_callstack, case_align) else: log.ok( start_align, "case %s and arbitrary bitcases" % case_field.field_name, self, my2_callstack, case_align) if log is not None: log.ok(start_align, "", self, my_callstack, total_align) return total_align # aux function for unchecked_get_alignment_after def get_align_for_selected_case_field(self, case_field, start_align, callstack, log): if verbose_align_log: print ("get_align_for_selected_case_field: %s, case_field = %s" % (str(self), str(case_field))) total_align = start_align for field in self.bitcases: my_callstack = callstack[:] my_callstack.append(field) if not field.wire: continue if field is case_field: # assume that this field is active -> no combine_with to emulate optional after_field_align = field.type.get_alignment_after(total_align, my_callstack, log) if log is not None: if after_field_align is None: log.fail(total_align, field.field_name, field.type, my_callstack, "invalid aligment for this case branch") else: log.ok(total_align, field.field_name, field.type, my_callstack, after_field_align) total_align = after_field_align elif field.type.is_bitcase: after_field_align = field.type.get_alignment_after(total_align, my_callstack, log) # we assume that this field is optional, therefore combine # alignment after the field with the alignment before the field. if after_field_align is None: if log is not None: log.fail(total_align, field.field_name, field.type, my_callstack, "invalid aligment for this bitcase branch") total_align = None else: if log is not None: log.ok(total_align, field.field_name, field.type, my_callstack, after_field_align) # combine with the align before the field because # the field is optional total_align = total_align.combine_with(after_field_align) else: # ignore other fields as they are irrelevant for alignment continue if total_align is None: break return total_align class Struct(ComplexType): ''' Derived class representing a struct data type. ''' out = __main__.output['struct'] class Union(ComplexType): ''' Derived class representing a union data type. ''' def __init__(self, name, elt): ComplexType.__init__(self, name, elt) self.is_union = True out = __main__.output['union'] def calc_size(self): self.size = 0 for m in self.fields: if not m.wire: continue if m.type.fixed_size(): self.size = max(self.size, m.type.get_total_size()) else: self.size = None break def check_implicit_fixed_size_part_aligns(self): # a union does not have implicit aligns because all fields start # at the start of the union return def unchecked_get_alignment_after(self, start_align, callstack, log): my_callstack = callstack[:] my_callstack.append(self) after_align = None if self.fixed_size(): #check proper alignment for all members start_align_ok = all( [field.type.is_acceptable_start_align(start_align, my_callstack + [field], log) for field in self.fields]) if start_align_ok: #compute the after align from the start_align after_align = start_align.align_after_fixed_size(self.get_total_size()) else: after_align = None if log is not None and after_align is not None: log.ok(start_align, "fixed sized union", self, my_callstack, after_align) else: if start_align is None: if log is not None: log.fail(start_align, "", self, my_callstack, "missing start_align for union") return None after_align = reduce( lambda x, y: None if x is None or y is None else x.combine_with(y), [field.type.get_alignment_after(start_align, my_callstack + [field], log) for field in self.fields]) if log is not None and after_align is not None: log.ok(start_align, "var sized union", self, my_callstack, after_align) if after_align is None and log is not None: log.fail(start_align, "", self, my_callstack, "start_align is not ok for all members") return after_align class CaseOrBitcaseType(ComplexType): ''' Derived class representing a case or bitcase. ''' def __init__(self, index, name, elt, *parent): elts = list(elt) self.expr = [] for sub_elt in elts: if sub_elt.tag == 'enumref': self.expr.append(Expression(sub_elt, self)) elt.remove(sub_elt) ComplexType.__init__(self, name, elt) self.has_name = True self.index = 1 self.lenfield_parent = list(parent) + [self] self.parents = list(parent) self.is_case_or_bitcase = True def make_member_of(self, module, switch_type, field_type, field_name, visible, wire, auto, enum=None): ''' register BitcaseType with the corresponding SwitchType module is the global module object. complex_type is the structure object. see Field for the meaning of the other parameters. ''' new_field = Field(self, field_type, field_name, visible, wire, auto, enum) # We dump the _placeholder_byte if any bitcases are added. for (idx, field) in enumerate(switch_type.bitcases): if field == _placeholder_byte: switch_type.bitcases[idx] = new_field return switch_type.bitcases.append(new_field) def resolve(self, module): if self.resolved: return for e in self.expr: e.resolve(module, self.parents+[self]) # Resolve the bitcase expression ComplexType.resolve(self, module) #calculate alignment self.calc_or_check_required_start_align() class BitcaseType(CaseOrBitcaseType): ''' Derived class representing a bitcase. ''' def __init__(self, index, name, elt, *parent): CaseOrBitcaseType.__init__(self, index, name, elt, *parent) self.is_bitcase = True class CaseType(CaseOrBitcaseType): ''' Derived class representing a case. ''' def __init__(self, index, name, elt, *parent): CaseOrBitcaseType.__init__(self, index, name, elt, *parent) self.is_case = True class Reply(ComplexType): ''' Derived class representing a reply. Only found as a field of Request. ''' def __init__(self, name, elt): ComplexType.__init__(self, name, elt) self.is_reply = True self.doc = None if self.required_start_align is None: self.required_start_align = Alignment(4,0) for child in list(elt): if child.tag == 'doc': self.doc = Doc(name, child) def resolve(self, module): if self.resolved: return # Reset pads count module.pads = 0 # Add the automatic protocol fields self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True)) self.fields.append(_placeholder_byte) self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True)) self.fields.append(Field(tcard32, tcard32.name, 'length', False, True, True)) ComplexType.resolve(self, module) class Request(ComplexType): ''' Derived class representing a request. Public fields added: reply contains the reply datatype or None for void requests. opcode contains the request number. ''' def __init__(self, name, elt): ComplexType.__init__(self, name, elt) self.reply = None self.doc = None self.opcode = elt.get('opcode') if self.required_start_align is None: self.required_start_align = Alignment(4,0) for child in list(elt): if child.tag == 'reply': self.reply = Reply(name, child) if child.tag == 'doc': self.doc = Doc(name, child) def resolve(self, module): if self.resolved: return # Add the automatic protocol fields if module.namespace.is_ext: self.fields.append(Field(tcard8, tcard8.name, 'major_opcode', False, True, True)) self.fields.append(Field(tcard8, tcard8.name, 'minor_opcode', False, True, True)) self.fields.append(Field(tcard16, tcard16.name, 'length', False, True, True)) ComplexType.resolve(self, module) else: self.fields.append(Field(tcard8, tcard8.name, 'major_opcode', False, True, True)) self.fields.append(_placeholder_byte) self.fields.append(Field(tcard16, tcard16.name, 'length', False, True, True)) ComplexType.resolve(self, module) if self.reply: self.reply.resolve(module) out = __main__.output['request'] class EventStructAllowedRule: def __init__(self, parent, elt): self.elt = elt self.extension = elt.get('extension') self.ge_events = elt.get('xge') == "true" self.min_opcode = int( elt.get('opcode-min') ) self.max_opcode = int( elt.get('opcode-max') ) def resolve(self, parent, module): # get the namespace of the specified extension extension_namespace = module.get_namespace( self.extension ) if extension_namespace is None: raise Exception( "EventStructAllowedRule.resolve: cannot find extension \"" + self.extension + "\"" ) return # find and add the selected events for opcode in range(self.min_opcode, self.max_opcode): name_and_event = extension_namespace.get_event_by_opcode( opcode, self.ge_events ) if name_and_event is None: # could not find event -> error handling if self.ge_events: raise Exception("EventStructAllowedRule.resolve: cannot find xge-event with opcode " + str(opcode) + " in extension " + self.extension ) else: raise Exception("EventStructAllowedRule.resolve: cannot find oldstyle-event with opcode " + str(opcode) + " in extension " + self.extension ) return ( name, event ) = name_and_event # add event to EventStruct parent.add_event( module, self.extension, opcode, name, event ) class EventStruct(Union): ''' Derived class representing an event-use-as-struct data type. ''' def __init__(self, name, elt): Union.__init__(self, name, elt) self.is_eventstruct = True self.events = [] self.allowedRules = [] self.contains_ge_events = False for item in list(elt): if item.tag == 'allowed': allowedRule = EventStructAllowedRule(self, item) self.allowedRules.append( allowedRule ) if allowedRule.ge_events: self.contains_ge_events = True out = __main__.output['eventstruct'] def resolve(self, module): if self.resolved: return for allowedRule in self.allowedRules: allowedRule.resolve(self, module) Union.resolve(self,module) self.resolved = True # add event. called by resolve def add_event(self, module, extension, opcode, name, event_type ): self.events.append( (extension, opcode, name, event_type) ) # Add the field to ourself event_type.make_member_of(module, self, name, name[-1], True, True, False) # Recursively resolve the event (could be another structure, list) event_type.resolve(module) def fixed_size(self): is_fixed_size = True for extension, opcode, name, event in self.events: if not event.fixed_size(): is_fixed_size = False return is_fixed_size class Event(ComplexType): ''' Derived class representing an event data type. Public fields added: opcodes is a dictionary of name -> opcode number, for eventcopies. ''' def __init__(self, name, elt): ComplexType.__init__(self, name, elt) if self.required_start_align is None: self.required_start_align = Alignment(4,0) self.opcodes = {} self.has_seq = not bool(elt.get('no-sequence-number')) self.is_ge_event = bool(elt.get('xge')) self.is_event = True self.doc = None for item in list(elt): if item.tag == 'doc': self.doc = Doc(name, item) def add_opcode(self, opcode, name, main): self.opcodes[name] = opcode if main: self.name = name def get_name_for_opcode(self, opcode): for name, my_opcode in self.opcodes.items(): if int(my_opcode) == opcode: return name else: return None def resolve(self, module): def add_event_header(): self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True)) if self.has_seq: self.fields.append(_placeholder_byte) self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True)) def add_ge_event_header(): self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True)) self.fields.append(Field(tcard8, tcard8.name, 'extension', False, True, True)) self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True)) self.fields.append(Field(tcard32, tcard32.name, 'length', False, True, True)) self.fields.append(Field(tcard16, tcard16.name, 'event_type', False, True, True)) if self.resolved: return # Add the automatic protocol fields if self.is_ge_event: add_ge_event_header() else: add_event_header() ComplexType.resolve(self, module) out = __main__.output['event'] class Error(ComplexType): ''' Derived class representing an error data type. Public fields added: opcodes is a dictionary of name -> opcode number, for errorcopies. ''' def __init__(self, name, elt): ComplexType.__init__(self, name, elt) self.opcodes = {} if self.required_start_align is None: self.required_start_align = Alignment(4,0) def add_opcode(self, opcode, name, main): self.opcodes[name] = opcode if main: self.name = name def resolve(self, module): if self.resolved: return # Add the automatic protocol fields self.fields.append(Field(tcard8, tcard8.name, 'response_type', False, True, True)) self.fields.append(Field(tcard8, tcard8.name, 'error_code', False, True, True)) self.fields.append(Field(tcard16, tcard16.name, 'sequence', False, True, True)) ComplexType.resolve(self, module) out = __main__.output['error'] class Doc(object): ''' Class representing a tag. ''' def __init__(self, name, elt): self.name = name self.description = None self.brief = 'BRIEF DESCRIPTION MISSING' self.fields = {} self.errors = {} self.see = {} self.example = None for child in list(elt): text = child.text if child.text else '' if child.tag == 'description': self.description = text.strip() if child.tag == 'brief': self.brief = text.strip() if child.tag == 'field': self.fields[child.get('name')] = text.strip() if child.tag == 'error': self.errors[child.get('type')] = text.strip() if child.tag == 'see': self.see[child.get('name')] = child.get('type') if child.tag == 'example': self.example = text.strip() _placeholder_byte = Field(PadType(None), tcard8.name, 'pad0', False, True, False)