c_client.py 132 KB
Newer Older
1
#!/usr/bin/env python
2
from __future__ import print_function
3
import getopt
4
import os
5
import sys
6
import errno
7
8
9
10
import re

# Jump to the bottom of this file for the main routine

11
12
13
#config settings (can be changed with commandline options)
config_server_side = False

14
15
16
17
18
19
20
21
22
# Some hacks to make the API more readable, and to keep backwards compability
_cname_re = re.compile('([A-Z0-9][a-z]+|[A-Z0-9]+(?![a-z])|[a-z]+)')
_cname_special_cases = {'DECnet':'decnet'}

_extension_special_cases = ['XPrint', 'XCMisc', 'BigRequests']

_cplusplus_annoyances = {'class' : '_class',
                         'new'   : '_new',
                         'delete': '_delete'}
23
_c_keywords = {'default' : '_default'}
24
25
26
27
28
29
30

_hlines = []
_hlevel = 0
_clines = []
_clevel = 0
_ns = None

31
# global variable to keep track of serializers and
32
# switch data types due to weird dependencies
33
finished_serializers = []
34
finished_sizeof = []
35
finished_switch = []
36

37
38
39
40
41
# keeps enum objects so that we can refer to them when generating manpages.
enums = {}

manpaths = False

42
43
44
45
46
def _h(fmt, *args):
    '''
    Writes the given line to the header file.
    '''
    _hlines[_hlevel].append(fmt % args)
47

48
49
50
51
52
def _c(fmt, *args):
    '''
    Writes the given line to the source file.
    '''
    _clines[_clevel].append(fmt % args)
53

54
55
56
57
58
59
60
def _hc(fmt, *args):
    '''
    Writes the given line to both the header and source files.
    '''
    _h(fmt, *args)
    _c(fmt, *args)

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def _c_wr_stringlist(indent, strlist):
    '''
    Writes the given list of strings to the source file.
    Each line is prepended by the indent string
    '''
    for str in strlist:
        _c("%s%s", indent, str)


class PreCode(object):
    '''
    For pre-code generated by expression generation
    (for example, the for-loop of a sumof)
    This has to account for recursiveness of the expression
    generation, i.e., there may be pre-code for pre-code.
    Therefore this is implemented as a stack of lists of lines.

    If redirection is switched on, then all output is collected in
    self.redirect_code and self.redirect_tempvars instead of
    being sent to the output via _h und _c.
    '''
    def __init__(self):
        self.nesting_level = 0
        self.tempvars = []
        self.codelines = []
        self.redirect_code = None
        self.redirect_tempvars = None
        self.indent_str = '    '
        self.indent_stack = []
        self.tempvar_num = 0


    # start and end of pre-code blocks
    def start(self):
        self.nesting_level += 1

    def end(self):
        self.nesting_level -= 1
        if self.nesting_level == 0:
            # lowest pre-code level is finished -> output to source
            if self.redirect_tempvars is None:
                _c_wr_stringlist('', self.tempvars)
                self.tempvars = []
            else:
                self.redirect_tempvars.extend(self.tempvars)
                self.tempvars = []
            if self.redirect_code == None:
                _c_wr_stringlist('', self.codelines)
                self.codelines = []
            else:
                self.redirect_code.extend(self.codelines)
                self.codelines = []


    def output_tempvars(self):
        if self.redirect_code == None:
            _c_wr_stringlist('', self.tempvars)
            self.tempvars = []

    # output to precode
    def code(self, fmt, *args):
        self.codelines.append(self.indent_str + fmt % args)

    def tempvar(self, fmt, *args):
        self.tempvars.append('    ' + (fmt % args))

    # get a unique name for a temporary variable
    def get_tempvarname(self):
        self.tempvar_num += 1
        return "xcb_pre_tmp_%d" % self.tempvar_num

    # indentation

    def push_indent(self, indentstr):
        self.indent_stack.append(self.indent_str)
        self.indent_str = indentstr

    def push_addindent(self, indent_add_str):
        self.push_indent(self.indent_str + indent_add_str)

    def indent(self):
        self.push_addindent('    ')

    def pop_indent(self):
        self.indent_str = self.indent_stack.pop()

    # redirection to lists
    def redirect_start(self, redirect_code, redirect_tempvars=None):
        self.redirect_code = redirect_code
        self.redirect_tempvars = redirect_tempvars
        if redirect_tempvars is not None:
            self.tempvar_num = 0

    def redirect_end(self):
        self.redirect_code = None
        self.redirect_tempvars = None

# global PreCode handler
_c_pre = PreCode()


162
163
164
165
166
167
168
169
170
171
# XXX See if this level thing is really necessary.
def _h_setlevel(idx):
    '''
    Changes the array that header lines are written to.
    Supports writing different sections of the header file.
    '''
    global _hlevel
    while len(_hlines) <= idx:
        _hlines.append([])
    _hlevel = idx
172

173
174
175
176
177
178
179
180
181
def _c_setlevel(idx):
    '''
    Changes the array that source lines are written to.
    Supports writing to different sections of the source file.
    '''
    global _clevel
    while len(_clines) <= idx:
        _clines.append([])
    _clevel = idx
182

183
184
185
186
187
188
189
190
191
192
193
def _n_item(str):
    '''
    Does C-name conversion on a single string fragment.
    Uses a regexp with some hard-coded special cases.
    '''
    if str in _cname_special_cases:
        return _cname_special_cases[str]
    else:
        split = _cname_re.finditer(str)
        name_parts = [match.group(0) for match in split]
        return '_'.join(name_parts)
194

195
196
197
198
199
200
def _cpp(str):
    '''
    Checks for certain C++ reserved words and fixes them.
    '''
    if str in _cplusplus_annoyances:
        return _cplusplus_annoyances[str]
201
202
    elif str in _c_keywords:
        return  _c_keywords[str]
203
204
205
206
207
208
209
210
211
212
213
214
    else:
        return str

def _ext(str):
    '''
    Does C-name conversion on an extension name.
    Has some additional special cases on top of _n_item.
    '''
    if str in _extension_special_cases:
        return _n_item(str).lower()
    else:
        return str.lower()
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
def _n(list):
    '''
    Does C-name conversion on a tuple of strings.
    Different behavior depending on length of tuple, extension/not extension, etc.
    Basically C-name converts the individual pieces, then joins with underscores.
    '''
    if len(list) == 1:
        parts = list
    elif len(list) == 2:
        parts = [list[0], _n_item(list[1])]
    elif _ns.is_ext:
        parts = [list[0], _ext(list[1])] + [_n_item(i) for i in list[2:]]
    else:
        parts = [list[0]] + [_n_item(i) for i in list[1:]]
    return '_'.join(parts).lower()

def _t(list):
    '''
    Does C-name conversion on a tuple of strings representing a type.
    Same as _n but adds a "_t" on the end.
    '''
    if len(list) == 1:
        parts = list
    elif len(list) == 2:
        parts = [list[0], _n_item(list[1]), 't']
    elif _ns.is_ext:
        parts = [list[0], _ext(list[1])] + [_n_item(i) for i in list[2:]] + ['t']
    else:
        parts = [list[0]] + [_n_item(i) for i in list[1:]] + ['t']
    return '_'.join(parts).lower()
246

247
248
249
250
251
252
253
254
255
256

def c_open(self):
    '''
    Exported function that handles module open.
    Opens the files and writes out the auto-generated comment, header file includes, etc.
    '''
    global _ns
    _ns = self.namespace
    _ns.c_ext_global_name = _n(_ns.prefix + ('id',))

257
258
259
    # Build the type-name collision avoidance table used by c_enum
    build_collision_table()

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
    _h_setlevel(0)
    _c_setlevel(0)

    _hc('/*')
    _hc(' * This file generated automatically from %s by c_client.py.', _ns.file)
    _hc(' * Edit at your peril.')
    _hc(' */')
    _hc('')

    _h('/**')
    _h(' * @defgroup XCB_%s_API XCB %s API', _ns.ext_name, _ns.ext_name)
    _h(' * @brief %s XCB Protocol Implementation.', _ns.ext_name)
    _h(' * @{')
    _h(' **/')
    _h('')
    _h('#ifndef __%s_H', _ns.header.upper())
    _h('#define __%s_H', _ns.header.upper())
    _h('')
    _h('#include "xcb.h"')

280
281
282
    _c('#ifdef HAVE_CONFIG_H')
    _c('#include "config.h"')
    _c('#endif')
283
    _c('#include <stdlib.h>')
284
285
    _c('#include <string.h>')
    _c('#include <assert.h>')
Uli Schlachter's avatar
Uli Schlachter committed
286
    _c('#include <stddef.h>  /* for offsetof() */')
287
288
    _c('#include "xcbext.h"')
    _c('#include "%s.h"', _ns.header)
289

290
291
292
    _c('')
    _c('#define ALIGNOF(type) offsetof(struct { char dummy; type member; }, member)')

293
    if _ns.is_ext:
294
        for (n, h) in self.direct_imports:
295
296
            _hc('#include "%s.h"', h)

297
298
299
300
301
302
    _h('')
    _h('#ifdef __cplusplus')
    _h('extern "C" {')
    _h('#endif')

    if _ns.is_ext:
303
304
305
        _h('')
        _h('#define XCB_%s_MAJOR_VERSION %s', _ns.ext_name.upper(), _ns.major_version)
        _h('#define XCB_%s_MINOR_VERSION %s', _ns.ext_name.upper(), _ns.minor_version)
306
        _h('') #XXX
307
308
309
        _h('extern xcb_extension_t %s;', _ns.c_ext_global_name)

        _c('')
Julien Danjou's avatar
Julien Danjou committed
310
        _c('xcb_extension_t %s = { "%s", 0 };', _ns.c_ext_global_name, _ns.ext_xname)
311
312
313
314
315
316
317
318
319

def c_close(self):
    '''
    Exported function that handles module close.
    Writes out all the stored content lines, then closes the files.
    '''
    _h_setlevel(2)
    _c_setlevel(2)
    _hc('')
320
321
322
323
324
325

    _h('')
    _h('#ifdef __cplusplus')
    _h('}')
    _h('#endif')

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
    _h('')
    _h('#endif')
    _h('')
    _h('/**')
    _h(' * @}')
    _h(' */')

    # Write header file
    hfile = open('%s.h' % _ns.header, 'w')
    for list in _hlines:
        for line in list:
            hfile.write(line)
            hfile.write('\n')
    hfile.close()

    # Write source file
    cfile = open('%s.c' % _ns.header, 'w')
    for list in _clines:
        for line in list:
            cfile.write(line)
            cfile.write('\n')
    cfile.close()

349
350
351
352
353
354
355
356
def build_collision_table():
    global namecount
    namecount = {}

    for v in module.types.values():
        name = _t(v[0])
        namecount[name] = (namecount.get(name) or 0) + 1

357
358
359
360
def c_enum(self, name):
    '''
    Exported function that handles enum declarations.
    '''
361

362
363
    enums[name] = self

364
365
366
367
    tname = _t(name)
    if namecount[tname] > 1:
        tname = _t(name + ('enum',))

368
369
    _h_setlevel(0)
    _h('')
370
    _h('typedef enum %s {', tname)
371

372
    count = len(self.values)
373
374

    for (enam, eval) in self.values:
375
376
377
        count = count - 1
        equals = ' = ' if eval != '' else ''
        comma = ',' if count > 0 else ''
378
        doc = ''
379
        if hasattr(self, "doc") and self.doc and enam in self.doc.fields:
380
381
            doc = '\n/**< %s */\n' % self.doc.fields[enam]
        _h('    %s%s%s%s%s', _n(name + (enam,)).upper(), equals, eval, comma, doc)
382

383
    _h('} %s;', tname)
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

def _c_type_setup(self, name, postfix):
    '''
    Sets up all the C-related state by adding additional data fields to
    all Field and Type objects.  Here is where we figure out most of our
    variable and function names.

    Recurses into child fields and list member types.
    '''
    # Do all the various names in advance
    self.c_type = _t(name + postfix)
    self.c_wiretype = 'char' if self.c_type == 'void' else self.c_type

    self.c_iterator_type = _t(name + ('iterator',))
    self.c_next_name = _n(name + ('next',))
    self.c_end_name = _n(name + ('end',))

    self.c_request_name = _n(name)
    self.c_checked_name = _n(name + ('checked',))
    self.c_unchecked_name = _n(name + ('unchecked',))
    self.c_reply_name = _n(name + ('reply',))
    self.c_reply_type = _t(name + ('reply',))
    self.c_cookie_type = _t(name + ('cookie',))
407
    self.c_reply_fds_name = _n(name + ('reply_fds',))
408

409
410
411
    self.c_need_aux = False
    self.c_need_serialize = False
    self.c_need_sizeof = False
412

413
414
415
416
417
    self.c_aux_name = _n(name + ('aux',))
    self.c_aux_checked_name = _n(name + ('aux', 'checked'))
    self.c_aux_unchecked_name = _n(name + ('aux', 'unchecked'))
    self.c_serialize_name = _n(name + ('serialize',))
    self.c_unserialize_name = _n(name + ('unserialize',))
418
    self.c_unpack_name = _n(name + ('unpack',))
419
    self.c_sizeof_name = _n(name + ('sizeof',))
420
421

    # special case: structs where variable size fields are followed by fixed size fields
422
    self.c_var_followed_by_fixed_fields = False
423
424

    if self.is_switch:
425
        self.c_need_serialize = True
426
        self.c_container = 'struct'
427
        for bitcase in self.bitcases:
428
            bitcase.c_field_name = _cpp(bitcase.field_name)
429
430
            bitcase_name = bitcase.field_type if bitcase.type.has_name else name
            _c_type_setup(bitcase.type, bitcase_name, ())
431

432
    elif self.is_container:
433
434
435
436
437
438
439

        self.c_container = 'union' if self.is_union else 'struct'
        prev_varsized_field = None
        prev_varsized_offset = 0
        first_field_after_varsized = None

        for field in self.fields:
440
441
442
443
444
            if field.type.is_event:
                field.c_field_type = _t(field.field_type + ('event',))
            else:
                field.c_field_type = _t(field.field_type)

445
446
            field.c_field_const_type = ('' if field.type.nmemb == 1 else 'const ') + field.c_field_type
            field.c_field_name = _cpp(field.field_name)
447
            field.c_subscript = '[%d]' % field.type.nmemb if (field.type.nmemb and field.type.nmemb > 1) else ''
448
            field.c_pointer = ' ' if field.type.nmemb == 1 else '*'
449

450
451
452
453
454
            # correct the c_pointer field for variable size non-list types
            if not field.type.fixed_size() and field.c_pointer == ' ':
                field.c_pointer = '*'
            if field.type.is_list and not field.type.member.fixed_size():
                field.c_pointer = '*'
455

456
457
458
            if field.type.is_switch:
                field.c_pointer = '*'
                field.c_field_const_type = 'const ' + field.c_field_type
459
                self.c_need_aux = True
460

461
            if not field.type.fixed_size() and not field.type.is_case_or_bitcase and field.wire:
462
                self.c_need_sizeof = True
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

            field.c_iterator_type = _t(field.field_type + ('iterator',))      # xcb_fieldtype_iterator_t
            field.c_iterator_name = _n(name + (field.field_name, 'iterator')) # xcb_container_field_iterator
            field.c_accessor_name = _n(name + (field.field_name,))            # xcb_container_field
            field.c_length_name = _n(name + (field.field_name, 'length'))     # xcb_container_field_length
            field.c_end_name = _n(name + (field.field_name, 'end'))           # xcb_container_field_end

            field.prev_varsized_field = prev_varsized_field
            field.prev_varsized_offset = prev_varsized_offset

            if prev_varsized_offset == 0:
                first_field_after_varsized = field
            field.first_field_after_varsized = first_field_after_varsized

            if field.type.fixed_size():
Jaya Tiwari's avatar
Jaya Tiwari committed
478
479
                if field.wire:
                    prev_varsized_offset += field.type.size
480
                # special case: intermixed fixed and variable size fields
481
482
                if prev_varsized_field is not None and not field.type.is_pad and field.wire:
                    if not self.is_union:
483
484
                        self.c_need_serialize = True
                        self.c_var_followed_by_fixed_fields = True
485
486
487
            else:
                self.last_varsized_field = field
                prev_varsized_field = field
488
                prev_varsized_offset = 0
489

490
            if self.c_var_followed_by_fixed_fields:
491
492
                if field.type.fixed_size():
                    field.prev_varsized_field = None
493

494
495
            # recurse into this field this has to be done here, i.e.,
            # after the field has been set up. Otherwise the function
496
            # _c_helper_fieldaccess_expr will produce garbage or crash
497
498
499
            _c_type_setup(field.type, field.field_type, ())
            if field.type.is_list:
                _c_type_setup(field.type.member, field.field_type, ())
500
                if (field.type.nmemb is None and field.wire):
501
502
                    self.c_need_sizeof = True

503
    if self.c_need_serialize:
504
        # when _unserialize() is wanted, create _sizeof() as well for consistency reasons
505
        self.c_need_sizeof = True
506

507
    # as switch does never appear at toplevel,
508
509
    # continue here with type construction
    if self.is_switch:
510
511
512
513
        if self.c_type not in finished_switch:
            finished_switch.append(self.c_type)
            # special: switch C structs get pointer fields for variable-sized members
            _c_complex(self)
514
515
516
            for bitcase in self.bitcases:
                bitcase_name = bitcase.type.name if bitcase.type.has_name else name
                _c_accessors(bitcase.type, bitcase_name, bitcase_name)
517
                # no list with switch as element, so no call to
518
                # _c_iterator(field.type, field_name) necessary
519

520
    if not self.is_case_or_bitcase:
521
        if self.c_need_serialize:
522
523
            if self.c_serialize_name not in finished_serializers:
                finished_serializers.append(self.c_serialize_name)
524
525
526
527
528
                _c_serialize('serialize', self)

                # _unpack() and _unserialize() are only needed for special cases:
                #   switch -> unpack
                #   special cases -> unserialize
529
                if self.is_switch or self.c_var_followed_by_fixed_fields:
530
                    _c_serialize('unserialize', self)
531

532
        if self.c_need_sizeof:
533
            if self.c_sizeof_name not in finished_sizeof:
534
535
536
                if not module.namespace.is_ext or self.name[:2] == module.namespace.prefix:
                    finished_sizeof.append(self.c_sizeof_name)
                    _c_serialize('sizeof', self)
537

538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
# Functions for querying field properties
def _c_field_needs_list_accessor(field):
    return field.type.is_list and not field.type.fixed_size()

def _c_field_needs_field_accessor(field):
    if field.type.is_list:
        return False
    else:
        return (field.prev_varsized_field is not None or
                not field.type.fixed_size())

def _c_field_needs_accessor(field):
    return (_c_field_needs_list_accessor(field) or
            _c_field_needs_field_accessor(field))

def _c_field_is_member_of_case_or_bitcase(field):
    return field.parent and field.parent.is_case_or_bitcase

556
def _c_helper_fieldaccess_expr(prefix, field=None):
557
558
    """
    turn prefix, which is a list of tuples (name, separator, Type obj) into a string
559
560
561
562
563
564
565
566
567
568
569
    representing a valid field-access-expression in C (based on the context)
    if field is not None, append access to the field as well.

    "separator" is one of the C-operators "." or "->".

    A field access expression can consist of the following components:
    * struct/union member access from a value with the "."-operator
    * struct/union member access from a pointer with "->"-operator
    * function-call of an accessor function:
      This is used when a xcb-field is not contained in a struct.
      This can, e.g., happen for fields after var-sized fields, etc.
570
571
    """
    prefix_str = ''
572
    last_sep =''
573
    for name, sep, obj in prefix:
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
        prefix_str += last_sep + name
        last_sep = sep

    if field is None:
        # add separator for access to a yet unknown field
        prefix_str += last_sep
    else:
        if _c_field_needs_accessor(field):
            if _c_field_is_member_of_case_or_bitcase(field):
                # case members are available in the deserialized struct,
                # so there is no need to use the accessor function
                # (also, their accessor function needs a different arglist
                # so this would require special treatment here)
                # Therefore: Access as struct member
                prefix_str += last_sep + _cpp(field.field_name)
            else:
                # Access with the accessor function
                prefix_str = field.c_accessor_name + "(" + prefix_str + ")"
        else:
            # Access as struct member
            prefix_str += last_sep + _cpp(field.field_name)

596
    return prefix_str
597

598
599
600
601
602
603
604
605
606
607
608
609
def _c_helper_field_mapping(complex_type, prefix, flat=False):
    """
    generate absolute names, based on prefix, for all fields starting from complex_type
    if flat == True, nested complex types are not taken into account
    """
    all_fields = {}
    if complex_type.is_switch:
        for b in complex_type.bitcases:
            if b.type.has_name:
                switch_name, switch_sep, switch_type = prefix[-1]
                bitcase_prefix = prefix + [(b.type.name[-1], '.', b.type)]
            else:
610
                bitcase_prefix = prefix
611

612
            if (flat and not b.type.has_name) or not flat:
613
614
615
                all_fields.update(_c_helper_field_mapping(b.type, bitcase_prefix, flat))
    else:
        for f in complex_type.fields:
616
            fname = _c_helper_fieldaccess_expr(prefix, f)
617
            if f.field_name in all_fields:
618
619
620
                raise Exception("field name %s has been registered before" % f.field_name)

            all_fields[f.field_name] = (fname, f)
621
            if f.type.is_container and not flat:
622
                if f.type.is_case_or_bitcase and not f.type.has_name:
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
                    new_prefix = prefix
                elif f.type.is_switch and len(f.type.parents)>1:
                    # nested switch gets another separator
                    new_prefix = prefix+[(f.c_field_name, '.', f.type)]
                else:
                    new_prefix = prefix+[(f.c_field_name, '->', f.type)]
                all_fields.update(_c_helper_field_mapping(f.type, new_prefix, flat))

    return all_fields

def _c_helper_resolve_field_names (prefix):
    """
    get field names for all objects in the prefix array
    """
    all_fields = {}
    tmp_prefix = []
    # look for fields in the remaining containers
640
    for idx, (name, sep, obj) in enumerate(prefix):
641
642
        if ''==sep:
            # sep can be preset in prefix, if not, make a sensible guess
643
            sep = '.' if (obj.is_switch or obj.is_case_or_bitcase) else '->'
644
645
            # exception: 'toplevel' object (switch as well!) always have sep '->'
            sep = '->' if idx<1 else sep
646
        if not obj.is_case_or_bitcase or (obj.is_case_or_bitcase and obj.has_name):
647
648
649
650
            tmp_prefix.append((name, sep, obj))
        all_fields.update(_c_helper_field_mapping(obj, tmp_prefix, flat=True))

    return all_fields
651

652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683

def get_expr_field_names(expr):
    """
    returns a list of field names referenced in an expression
    """
    if expr.op is None or expr.op == 'calculate_len':
        if expr.lenfield_name is not None:
            return [expr.lenfield_name]
        # constant value expr
        return []

    if expr.op == '~':
        return get_expr_field_names(expr.rhs)
    if expr.op == 'popcount':
        return get_expr_field_names(expr.rhs)
    if expr.op == 'sumof':
        # sumof expr references another list,
        # we need that list's length field here
        field = None
        for f in expr.lenfield_parent.fields:
            if f.field_name == expr.lenfield_name:
                field = f
                break
        if field is None:
            raise Exception("list field '%s' referenced by sumof not found" % expr.lenfield_name)
        # referenced list + its length field
        return [expr.lenfield_name] + get_expr_field_names(field.type.expr)
    if expr.op == 'enumref':
        return []
    return get_expr_field_names(expr.lhs) + get_expr_field_names(expr.rhs)


684
def get_expr_fields(self):
685
    """
686
    get the Fields referenced by switch or list expression
687
    """
688

689
    # resolve the field names with the parent structure(s)
690
    unresolved_fields_names = get_expr_field_names(self.expr)
691

692
693
694
695
696
697
    # construct prefix from self
    prefix = [('', '', p) for p in self.parents]
    if self.is_container:
        prefix.append(('', '', self))

    all_fields = _c_helper_resolve_field_names (prefix)
698
    resolved_fields_names = [x for x in unresolved_fields_names if x in all_fields]
699
700
    if len(unresolved_fields_names) != len(resolved_fields_names):
        raise Exception("could not resolve all fields for %s" % self.name)
701

702
703
    resolved_fields = [all_fields[n][1] for n in resolved_fields_names]
    return resolved_fields
704

705
706
707
708
709
710
def resolve_expr_fields(complex_obj):
    """
    find expr fields appearing in complex_obj and descendents that cannot be resolved within complex_obj
    these are normally fields that need to be given as function parameters
    """
    all_fields = []
711
712
    expr_fields = []
    unresolved = []
713

714
715
716
717
718
    for field in complex_obj.fields:
        all_fields.append(field)
        if field.type.is_switch or field.type.is_list:
            expr_fields += get_expr_fields(field.type)
        if field.type.is_container:
719
720
            expr_fields += resolve_expr_fields(field.type)

721
722
    # try to resolve expr fields
    for e in expr_fields:
723
        if e not in all_fields and e not in unresolved:
724
725
            unresolved.append(e)
    return unresolved
726

727
728
729
730
731
732
733
734
735
736
def resolve_expr_fields_list(self, parents):
    """
    Find expr fields appearing in a list and descendents
    that cannot be resolved within the parents of the list.
    These are normally fields that need to be given as function parameters
    for length and iterator functions.
    """
    all_fields = []
    expr_fields = get_expr_fields(self)
    unresolved = []
Jaya Tiwari's avatar
Jaya Tiwari committed
737
    dont_resolve_this = ''
738
739
    for complex_obj in parents:
        for field in complex_obj.fields:
Jaya Tiwari's avatar
Jaya Tiwari committed
740
741
            if field.type.is_list and field.type.expr.op == 'calculate_len':
                dont_resolve_this = field.type.expr.lenfield_name
742
743
744
745
746
            if field.wire:
                all_fields.append(field)

    # try to resolve expr fields
    for e in expr_fields:
Jaya Tiwari's avatar
Jaya Tiwari committed
747
        if e not in all_fields and e not in unresolved and e.field_name != dont_resolve_this:
748
749
750
751
752
753
            unresolved.append(e)

    return unresolved
# resolve_expr_fields_list()


754
def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
755
756
    """
    functions like _serialize(), _unserialize(), and _unpack() sometimes need additional parameters:
757
758
759
    E.g. in order to unpack switch, extra parameters might be needed to evaluate the switch
    expression. This function tries to resolve all fields within a structure, and returns the
    unresolved fields as the list of external parameters.
760
    """
761
762
763
764
    def add_param(params, param):
        if param not in params:
            params.append(param)

765
766
767
768
769
770
771
772
773
774
775
776
777
    # collect all fields into param_fields
    param_fields = []
    wire_fields = []

    for field in self.fields:
        if field.visible:
            # the field should appear as a parameter in the function call
            param_fields.append(field)
        if field.wire and not field.auto:
            if field.type.fixed_size() and not self.is_switch:
                # field in the xcb_out structure
                wire_fields.append(field)
        # fields like 'pad0' are skipped!
778

779
    # in case of switch, parameters always contain any fields referenced in the switch expr
780
    # we do not need any variable size fields here, as the switch data type contains both
781
    # fixed and variable size fields
782
    if self.is_switch:
783
        param_fields = get_expr_fields(self)
784

785
786
    # _serialize()/_unserialize()/_unpack() function parameters
    # note: don't use set() for params, it is unsorted
787
    params = []
Jaya Tiwari's avatar
Jaya Tiwari committed
788
789
790
    parameter = ''
    if self.is_list:
       parameter = self.type.expr.lenfield_name
791
    # 1. the parameter for the void * buffer
792
    if  'serialize' == context:
793
        params.append(('void', '**', buffer_var))
794
    elif context in ('unserialize', 'unpack', 'sizeof'):
795
796
        params.append(('const void', '*', buffer_var))

797
798
    # 2. any expr fields that cannot be resolved within self and descendants
    unresolved_fields = resolve_expr_fields(self)
799
800
    for f in unresolved_fields:
        add_param(params, (f.c_field_type, '', f.c_field_name))
801

802
803
    # 3. param_fields contain the fields necessary to evaluate the switch expr or any other fields
    #    that do not appear in the data type struct
804
    for p in param_fields:
805
        if self.is_switch:
806
            typespec = p.c_field_const_type
807
            pointerspec = p.c_pointer
808
            add_param(params, (typespec, pointerspec, p.c_field_name))
809
        else:
Jaya Tiwari's avatar
Jaya Tiwari committed
810
            if p.visible and not p.wire and not p.auto and p.field_name != parameter:
811
812
813
                typespec = p.c_field_type
                pointerspec = ''
                add_param(params, (typespec, pointerspec, p.c_field_name))
814

815
    # 4. aux argument
816
    if 'serialize' == context:
817
        add_param(params, ('const %s' % self.c_type, '*', aux_var))
818
    elif 'unserialize' == context:
819
820
821
822
        add_param(params, ('%s' % self.c_type, '**', aux_var))
    elif 'unpack' == context:
        add_param(params, ('%s' % self.c_type, '*', aux_var))

823
824
    # 5. switch contains all variable size fields as struct members
    #    for other data types though, these have to be supplied separately
825
    #    this is important for the special case of intermixed fixed and
826
    #    variable size fields
827
    if not self.is_switch and 'serialize' == context:
828
829
        for p in param_fields:
            if not p.type.fixed_size():
830
                add_param(params, (p.c_field_const_type, '*', p.c_field_name))
831

832
833
    return (param_fields, wire_fields, params)

834
def _c_serialize_helper_insert_padding(context, complex_type, code_lines, space, postpone, is_case_or_bitcase):
835
    code_lines.append('%s    /* insert padding */' % space)
836
837
838
839
840
841
842
    if is_case_or_bitcase:
        code_lines.append(
            '%s    xcb_pad = -(xcb_block_len + xcb_padding_offset) & (xcb_align_to - 1);'
            % space)
    else:
        code_lines.append(
            '%s    xcb_pad = -xcb_block_len & (xcb_align_to - 1);' % space)
843
    code_lines.append('%s    xcb_buffer_len += xcb_block_len + xcb_pad;' % space)
844

845
846
    if not postpone:
        code_lines.append('%s    if (0 != xcb_pad) {' % space)
847

848
849
850
851
852
853
854
855
856
        if 'serialize' == context:
            code_lines.append('%s        xcb_parts[xcb_parts_idx].iov_base = xcb_pad0;' % space)
            code_lines.append('%s        xcb_parts[xcb_parts_idx].iov_len = xcb_pad;' % space)
            code_lines.append('%s        xcb_parts_idx++;' % space)
        elif context in ('unserialize', 'unpack', 'sizeof'):
            code_lines.append('%s        xcb_tmp += xcb_pad;' % space)

        code_lines.append('%s        xcb_pad = 0;' % space)
        code_lines.append('%s    }' % space)
857

858
    code_lines.append('%s    xcb_block_len = 0;' % space)
859
860
    if is_case_or_bitcase:
        code_lines.append('%s    xcb_padding_offset = 0;' % space)
861
862

    # keep tracking of xcb_parts entries for serialize
863
    return 1
864

865
866
def _c_serialize_helper_switch(context, self, complex_name,
                               code_lines, temp_vars,
867
868
                               space, prefix):
    count = 0
869
870
    switch_expr = _c_accessor_get_expr(self.expr, None)

871
    for b in self.bitcases:
872
        len_expr = len(b.type.expr)
873
874
875
876
877
878
879

        compare_operator = '&'
        if b.type.is_case:
            compare_operator = '=='
        else:
            compare_operator = '&'

880
881
882
883
        for n, expr in enumerate(b.type.expr):
            bitcase_expr = _c_accessor_get_expr(expr, None)
            # only one <enumref> in the <bitcase>
            if len_expr == 1:
884
885
                code_lines.append(
                    '    if(%s %s %s) {' % (switch_expr, compare_operator, bitcase_expr))
886
887
            # multiple <enumref> in the <bitcase>
            elif n == 0: # first
888
889
                code_lines.append(
                    '    if((%s %s %s) ||' % (switch_expr, compare_operator, bitcase_expr))
890
            elif len_expr == (n + 1): # last
891
892
                code_lines.append(
                    '       (%s %s %s)) {' % (switch_expr, compare_operator, bitcase_expr))
893
            else: # between first and last
894
895
                code_lines.append(
                    '       (%s %s %s) ||' % (switch_expr, compare_operator, bitcase_expr))
896

897
        b_prefix = prefix
898
        if b.type.has_name:
899
            b_prefix = prefix + [(b.c_field_name, '.', b.type)]
900
901
902
903
904

        count += _c_serialize_helper_fields(context, b.type,
                                            code_lines, temp_vars,
                                            "%s    " % space,
                                            b_prefix,
905
                                            is_case_or_bitcase = True)
906
907
        code_lines.append('    }')

908
#    if 'serialize' == context:
909
#        count += _c_serialize_helper_insert_padding(context, self, code_lines, space, False)
910
911
912
913
#    elif context in ('unserialize', 'unpack', 'sizeof'):
#        # padding
#        code_lines.append('%s    xcb_pad = -xcb_block_len & 3;' % space)
#        code_lines.append('%s    xcb_buffer_len += xcb_block_len + xcb_pad;' % space)
914

915
    return count
916

917
def _c_serialize_helper_switch_field(context, self, field, c_switch_variable, prefix):
918
919
920
    """
    handle switch by calling _serialize() or _unpack(), depending on context
    """
921
    # switch is handled by this function as a special case
922
    field_mapping = _c_helper_field_mapping(self, prefix)
923
    prefix_str = _c_helper_fieldaccess_expr(prefix)
924
925
926

    # find the parameters that need to be passed to _serialize()/_unpack():
    # all switch expr fields must be given as parameters
927
    args = get_expr_fields(field.type)
928
    # length fields for variable size types in switch, normally only some of need
929
930
    # need to be passed as parameters
    switch_len_fields = resolve_expr_fields(field.type)
931

932
933
934
    # a switch field at this point _must_ be a bitcase field
    # we require that bitcases are "self-contiguous"
    bitcase_unresolved = resolve_expr_fields(self)
935
936
    if len(bitcase_unresolved) != 0:
        raise Exception('unresolved fields within bitcase is not supported at this point')
937

938
    # get the C names for the parameters
939
    c_field_names = ''
940
941
    for a in switch_len_fields:
        c_field_names += "%s, " % field_mapping[a.c_field_name][0]
942
    for a in args:
943
        c_field_names += "%s, " % field_mapping[a.c_field_name][0]
944
945
946
947

    # call _serialize()/_unpack() to determine the actual size
    if 'serialize' == context:
        length = "%s(&%s, %s&%s%s)" % (field.type.c_serialize_name, c_switch_variable,
948
                                       c_field_names, prefix_str, field.c_field_name)
949
    elif context in ('unserialize', 'unpack'):
950
        length = "%s(xcb_tmp, %s&%s%s)" % (field.type.c_unpack_name,
951
                                           c_field_names, prefix_str, field.c_field_name)
952
953
954
955
    elif 'sizeof' == context:
        # remove trailing ", " from c_field_names because it will be used at end of arglist
        my_c_field_names = c_field_names[:-2]
        length = "%s(xcb_tmp, %s)" % (field.type.c_sizeof_name, my_c_field_names)
956

957
958
    return length

959
960
961
962
963
964
965
966
967
968
def _c_get_additional_type_params(type):
    """
    compute list of additional params for functions created for the given type
    """
    if type.is_simple:
        return []
    else:
        param_fields, wire_fields, params = get_serialize_params('sizeof', type)
        return params[1:]

969
970

def _c_get_field_mapping_for_expr(self, expr, prefix):
971
    """
972
    helper function to get field mapping of a particular expression.
973
    """
974
    param_fields, wire_fields, params = get_serialize_params('sizeof', self)
975
    param_names = [p[2] for p in params]
976

977
    expr_fields_names = get_expr_field_names(expr)
978
979
    resolved = [x for x in expr_fields_names if x in param_names]
    unresolved = [x for x in expr_fields_names if x not in param_names]
980

981
982
983
    field_mapping = {}
    for r in resolved:
        field_mapping[r] = (r, None)
984

985
986
987
    if len(unresolved)>0:
        tmp_prefix = prefix
        if len(tmp_prefix)==0:
988
989
990
            raise Exception("found an empty prefix while resolving expr field names for list %s",
                            field.c_field_name)

991
        field_mapping.update(_c_helper_resolve_field_names(prefix))
992
993
        resolved += [x for x in unresolved if x in field_mapping]
        unresolved = [x for x in unresolved if x not in field_mapping]
994
995
        if len(unresolved)>0:
            raise Exception('could not resolve the length fields required for list %s' % field.c_field_name)
996
997
998
999
1000

    return field_mapping


def _c_serialize_helper_list_field(context, self, field,
For faster browsing, not all history is shown. View entire blame