1'''
2This module contains helper classes for structure fields and length expressions.
3'''
4class Field(object):
5    '''
6    Represents a field of a structure.
7
8    type is the datatype object for the field.
9    field_type is the name of the type (string tuple)
10    field_name is the name of the structure field.
11    visible is true iff the field should be in the request API.
12    wire is true iff the field should be in the request structure.
13    auto is true iff the field is on the wire but not in the request API (e.g. opcode)
14    enum is the enum name this field refers to, if any.
15    '''
16    def __init__(self, type, field_type, field_name, visible, wire, auto, enum=None, isfd=False):
17        self.type = type
18        self.field_type = field_type
19        self.field_name = field_name
20        self.enum = enum
21        self.visible = visible
22        self.wire = wire
23        self.auto = auto
24        self.isfd = isfd
25        self.parent = None
26
27    def __str__(self):
28        field_string = "Field"
29        if self.field_name is None:
30            if self.field_type is not None:
31                field_string += " with type " + str(self.type)
32        else:
33            field_string += " \"" + self.field_name + "\""
34        if self.parent is not None:
35            field_string += " in " + str(self.parent)
36
37        return field_string
38
39class Expression(object):
40    '''
41    Represents a mathematical expression for a list length or exprfield.
42
43    Public fields:
44    op is the operation (text +,*,/,<<,~) or None.
45    lhs and rhs are the sub-Expressions if op is set.
46    lenfield_name is the name of the length field, or None for request lists.
47    lenfield is the Field object for the length field, or None.
48    bitfield is True if the length field is a bitmask instead of a number.
49    nmemb is the fixed size (value)of the expression, or None
50    '''
51    def __init__(self, elt, parent):
52        self.parent = parent
53
54        self.nmemb = None
55
56        self.lenfield_name = None
57        self.lenfield_type = None
58        self.lenfield_parent = None
59        self.lenfield = None
60        self.lenwire = False
61        self.bitfield = False
62
63        self.op = None
64        self.lhs = None
65        self.rhs = None
66
67        self.contains_listelement_ref = False
68
69        if elt.tag == 'list':
70            # List going into a request, which has no length field (inferred by server)
71            self.lenfield_name = elt.get('name') + '_len'
72            self.lenfield_type = 'CARD32'
73
74        elif elt.tag == 'fieldref':
75            # Standard list with a fieldref
76            self.lenfield_name = elt.text
77
78        elif elt.tag == 'paramref':
79            self.lenfield_name = elt.text
80            self.lenfield_type = elt.get('type')
81
82        elif elt.tag == 'op':
83            # Op field.  Need to recurse.
84            self.op = elt.get('op')
85            self.lhs = Expression(list(elt)[0], parent)
86            self.rhs = Expression(list(elt)[1], parent)
87
88            # Hopefully we don't have two separate length fields...
89            self.lenfield_name = self.lhs.lenfield_name
90            if self.lenfield_name == None:
91                self.lenfield_name = self.rhs.lenfield_name
92
93        elif elt.tag == 'unop':
94            # Op field.  Need to recurse.
95            self.op = elt.get('op')
96            self.rhs = Expression(list(elt)[0], parent)
97
98            self.lenfield_name = self.rhs.lenfield_name
99
100        elif elt.tag == 'value':
101            # Constant expression
102            self.nmemb = int(elt.text, 0)
103
104        elif elt.tag == 'popcount':
105            self.op = 'popcount'
106            self.rhs = Expression(list(elt)[0], parent)
107            self.lenfield_name = self.rhs.lenfield_name
108            # xcb_popcount returns 'int' - handle the type in the language-specific part
109
110        elif elt.tag == 'enumref':
111            self.op = 'enumref'
112            self.lenfield_name = (elt.get('ref'), elt.text)
113
114        elif elt.tag == 'sumof':
115            self.op = 'sumof'
116            self.lenfield_name = elt.get('ref')
117            subexpressions = list(elt)
118            if len(subexpressions) > 0:
119                # sumof with a nested expression which is to be evaluated
120                # for each list-element in the context of that list-element.
121                # sumof then returns the sum of the results of these evaluations
122                self.rhs = Expression(subexpressions[0], parent)
123
124        elif elt.tag == 'listelement-ref':
125            # current list element inside iterating expressions such as sumof
126            self.op = 'listelement-ref'
127            self.contains_listelement_ref = True
128
129        else:
130            # Notreached
131            raise Exception("undefined tag '%s'" % elt.tag)
132
133    def fixed_size(self):
134        return self.nmemb != None
135
136    def get_value(self):
137        return self.nmemb
138
139    # if the value of the expression is a guaranteed multiple of a number
140    # return this number, else return 1 (which is trivially guaranteed for integers)
141    def get_multiple(self):
142        multiple = 1
143        if self.op == '*':
144            if self.lhs.fixed_size():
145                multiple *= self.lhs.get_value()
146            if self.rhs.fixed_size():
147                multiple *= self.rhs.get_value()
148
149        return multiple
150
151    def recursive_resolve_tasks(self, module, parents):
152        for subexpr in (self.lhs, self.rhs):
153            if subexpr != None:
154                subexpr.recursive_resolve_tasks(module, parents)
155                self.contains_listelement_ref |= subexpr.contains_listelement_ref
156
157    def resolve(self, module, parents):
158        if self.op == 'enumref':
159            self.lenfield_type = module.get_type(self.lenfield_name[0])
160            self.lenfield_name = self.lenfield_name[1]
161        elif self.op == 'sumof':
162            # need to find the field with lenfield_name
163            for p in reversed(parents):
164                fields = dict([(f.field_name, f) for f in p.fields])
165                if self.lenfield_name in fields.keys():
166                    if p.is_case_or_bitcase:
167                        # switch is the anchestor
168                        self.lenfield_parent = p.parents[-1]
169                    else:
170                        self.lenfield_parent = p
171                    self.lenfield_type = fields[self.lenfield_name].field_type
172                    self.lenfield = fields[self.lenfield_name]
173                    break
174
175        self.recursive_resolve_tasks(module, parents)
176
177