1#!/usr/bin/python
2
3import os
4import sys
5
6sys.path.append('{}/tools/libxl'.format(os.environ['XEN_ROOT']))
7import idl
8
9# Go versions of some builtin types.
10# Append the libxl-defined builtins after IDL parsing.
11builtin_type_names = {
12    idl.bool.typename: 'bool',
13    idl.string.typename: 'string',
14    idl.integer.typename: 'int',
15    idl.uint8.typename: 'byte',
16    idl.uint16.typename: 'uint16',
17    idl.uint32.typename: 'uint32',
18    idl.uint64.typename: 'uint64',
19}
20
21# Some go keywords that conflict with field names in libxl structs.
22go_keywords = ['type', 'func']
23
24go_builtin_types = ['bool', 'string', 'int', 'byte',
25                    'uint16', 'uint32', 'uint64']
26
27# cgo preamble for xenlight_helpers.go, created during type generation and
28# written later.
29cgo_helpers_preamble = []
30
31def xenlight_golang_generate_types(path = None, types = None, comment = None):
32    """
33    Generate a .go file (types.gen.go by default)
34    that contains a Go type for each type in types.
35    """
36    if path is None:
37        path = 'types.gen.go'
38
39    with open(path, 'w') as f:
40        if comment is not None:
41            f.write(comment)
42        f.write('package xenlight\n\n')
43
44        for ty in types:
45            (tdef, extras) = xenlight_golang_type_define(ty)
46
47            f.write(tdef)
48            f.write('\n')
49
50            # Append extra types
51            for extra in extras:
52                f.write(extra)
53                f.write('\n')
54
55def xenlight_golang_type_define(ty = None):
56    """
57    Generate the Go type definition of ty.
58
59    Return a tuple that contains a string with the
60    type definition, and a (potentially empty) list
61    of extra definitions that are associated with
62    this type.
63    """
64    if isinstance(ty, idl.Enumeration):
65        return (xenlight_golang_define_enum(ty), [])
66
67    elif isinstance(ty, idl.Aggregate):
68        return xenlight_golang_define_struct(ty)
69
70def xenlight_golang_define_enum(ty = None):
71    s = ''
72    typename = ''
73
74    if ty.typename is not None:
75        typename = xenlight_golang_fmt_name(ty.typename)
76        s += 'type {} int\n'.format(typename)
77
78    # Start const block
79    s += 'const(\n'
80
81    for v in ty.values:
82        name = xenlight_golang_fmt_name(v.name)
83        s += '{} {} = {}\n'.format(name, typename, v.value)
84
85    # End const block
86    s += ')\n'
87
88    return s
89
90def xenlight_golang_define_struct(ty = None, typename = None, nested = False):
91    s = ''
92    extras = []
93    name = ''
94
95    if typename is not None:
96        name = xenlight_golang_fmt_name(typename)
97    else:
98        name = xenlight_golang_fmt_name(ty.typename)
99
100    # Begin struct definition
101    if nested:
102        s += '{} struct {{\n'.format(name)
103    else:
104        s += 'type {} struct {{\n'.format(name)
105
106    # Write struct fields
107    for f in ty.fields:
108        if f.type.typename is not None:
109            if isinstance(f.type, idl.Array):
110                typename = f.type.elem_type.typename
111                typename = xenlight_golang_fmt_name(typename)
112                name     = xenlight_golang_fmt_name(f.name)
113
114                s += '{} []{}\n'.format(name, typename)
115            else:
116                typename = f.type.typename
117                typename = xenlight_golang_fmt_name(typename)
118                name     = xenlight_golang_fmt_name(f.name)
119
120                s += '{} {}\n'.format(name, typename)
121
122        elif isinstance(f.type, idl.Struct):
123            r = xenlight_golang_define_struct(f.type, typename=f.name, nested=True)
124
125            s += r[0]
126            extras.extend(r[1])
127
128        elif isinstance(f.type, idl.KeyedUnion):
129            r = xenlight_golang_define_union(f.type, ty.typename, f.name)
130
131            s += r[0]
132            extras.extend(r[1])
133
134        else:
135            raise Exception('type {} not supported'.format(f.type))
136
137    # End struct definition
138    s += '}\n'
139
140    return (s,extras)
141
142def xenlight_golang_define_union(ty = None, struct_name = '', union_name = ''):
143    """
144    Generate the Go translation of a KeyedUnion.
145
146    Define an unexported interface to be used as
147    the type of the union. Then, define a struct
148    for each field of the union which implements
149    that interface.
150    """
151    s = ''
152    extras = []
153
154    interface_name = '{}_{}_union'.format(struct_name, ty.keyvar.name)
155    interface_name = xenlight_golang_fmt_name(interface_name, exported=False)
156
157    s += 'type {} interface {{\n'.format(interface_name)
158    s += 'is{}()\n'.format(interface_name)
159    s += '}\n'
160
161    extras.append(s)
162
163    for f in ty.fields:
164        if f.type is None:
165            continue
166
167        # Define struct
168        name = '{}_{}_union_{}'.format(struct_name, ty.keyvar.name, f.name)
169        r = xenlight_golang_define_struct(f.type, typename=name)
170        extras.append(r[0])
171        extras.extend(r[1])
172
173        # This typeof trick ensures that the fields used in the cgo struct
174        # used for marshaling are the same as the fields of the union in the
175        # actual C type, and avoids re-defining all of those fields.
176        s = 'typedef typeof(((struct {} *)NULL)->{}.{}){};'
177        s = s.format(struct_name, union_name, f.name, name)
178        cgo_helpers_preamble.append(s)
179
180        # Define function to implement 'union' interface
181        name = xenlight_golang_fmt_name(name)
182        s = 'func (x {}) is{}(){{}}\n'.format(name, interface_name)
183        extras.append(s)
184
185    fname = xenlight_golang_fmt_name(ty.keyvar.name)
186    ftype = xenlight_golang_fmt_name(ty.keyvar.type.typename)
187    s = '{} {}\n'.format(fname, ftype)
188
189    fname = xenlight_golang_fmt_name('{}_union'.format(ty.keyvar.name))
190    s += '{} {}\n'.format(fname, interface_name)
191
192    return (s,extras)
193
194def xenlight_golang_generate_helpers(path = None, types = None, comment = None):
195    """
196    Generate a .go file (helpers.gen.go by default)
197    that contains helper functions for marshaling between
198    C and Go types.
199    """
200    if path is None:
201        path = 'helpers.gen.go'
202
203    with open(path, 'w') as f:
204        if comment is not None:
205            f.write(comment)
206        f.write('package xenlight\n\n')
207        f.write('import (\n"unsafe"\n"errors"\n"fmt"\n)\n')
208
209        # Cgo preamble
210        f.write('/*\n')
211        f.write('#cgo LDFLAGS: -lxenlight\n')
212        f.write('#include <stdlib.h>\n')
213        f.write('#include <libxl.h>\n')
214        f.write('\n')
215
216        for s in cgo_helpers_preamble:
217            f.write(s)
218            f.write('\n')
219
220        f.write('*/\nimport "C"\n')
221
222        for ty in types:
223            if not isinstance(ty, idl.Struct):
224                continue
225
226            f.write(xenlight_golang_define_constructor(ty))
227            f.write('\n')
228
229            (fdef, extras) = xenlight_golang_define_from_C(ty)
230
231            f.write(fdef)
232            f.write('\n')
233
234            for extra in extras:
235                f.write(extra)
236                f.write('\n')
237
238            f.write(xenlight_golang_define_to_C(ty))
239            f.write('\n')
240
241def xenlight_golang_define_from_C(ty = None):
242    """
243    Define the fromC marshaling function for the type
244    represented by ty.
245    """
246    func = 'func (x *{}) fromC(xc *C.{}) error {{\n {}\n return nil}}\n'
247
248    goname = xenlight_golang_fmt_name(ty.typename)
249    cname  = ty.typename
250
251    body = ''
252    extras = []
253
254    for f in ty.fields:
255        if f.type.typename is not None:
256            if isinstance(f.type, idl.Array):
257                body += xenlight_golang_array_from_C(f)
258                continue
259
260            body += xenlight_golang_convert_from_C(f)
261
262        elif isinstance(f.type, idl.Struct):
263            # Go through the fields of the anonymous nested struct.
264            for nf in f.type.fields:
265                body += xenlight_golang_convert_from_C(nf,outer_name=f.name)
266
267        elif isinstance(f.type, idl.KeyedUnion):
268            r = xenlight_golang_union_from_C(f.type, f.name, ty.typename)
269
270            body += r[0]
271            extras.extend(r[1])
272
273        else:
274            raise Exception('type {} not supported'.format(f.type))
275
276    return (func.format(goname, cname, body), extras)
277
278def xenlight_golang_convert_from_C(ty = None, outer_name = None, cvarname = None):
279    """
280    Returns a line of Go code that converts the C type represented
281    by ty to its corresponding Go type.
282
283    If outer_name is set, the type is treated as nested within another field
284    named outer_name.
285    """
286    s = ''
287
288    # Use 'xc' as the name for the C variable unless otherwise specified
289    if cvarname is None:
290        cvarname = 'xc'
291
292    gotypename = xenlight_golang_fmt_name(ty.type.typename)
293    goname     = xenlight_golang_fmt_name(ty.name)
294    cname      = ty.name
295
296    # In cgo, C names that conflict with Go keywords can be
297    # accessed by prepending an underscore to the name.
298    if cname in go_keywords:
299        cname = '_' + cname
300
301    # If outer_name is set, treat this as nested.
302    if outer_name is not None:
303        goname = '{}.{}'.format(xenlight_golang_fmt_name(outer_name), goname)
304        cname  = '{}.{}'.format(outer_name, cname)
305
306    # Types that satisfy this condition can be easily casted or
307    # converted to a Go builtin type.
308    is_castable = (ty.type.json_parse_type == 'JSON_INTEGER' or
309                   isinstance(ty.type, idl.Enumeration) or
310                   gotypename in go_builtin_types)
311
312    if not is_castable:
313        # If the type is not castable, we need to call its fromC
314        # function.
315        s += 'if err := x.{}.fromC(&{}.{});'.format(goname,cvarname,cname)
316        s += 'err != nil {{\nreturn fmt.Errorf("converting field {}: %v", err)\n}}\n'.format(goname)
317
318    elif gotypename == 'string':
319        # Use the cgo helper for converting C strings.
320        s += 'x.{} = C.GoString({}.{})\n'.format(goname,cvarname,cname)
321
322    else:
323        s += 'x.{} = {}({}.{})\n'.format(goname,gotypename,cvarname,cname)
324
325    return s
326
327def xenlight_golang_union_from_C(ty = None, union_name = '', struct_name = ''):
328    extras = []
329
330    keyname   = ty.keyvar.name
331    gokeyname = xenlight_golang_fmt_name(keyname)
332    keytype   = ty.keyvar.type.typename
333    gokeytype = xenlight_golang_fmt_name(keytype)
334    field_name = xenlight_golang_fmt_name('{}_union'.format(keyname))
335
336    interface_name = '{}_{}_union'.format(struct_name, keyname)
337    interface_name = xenlight_golang_fmt_name(interface_name, exported=False)
338
339    cgo_keyname = keyname
340    if cgo_keyname in go_keywords:
341        cgo_keyname = '_' + cgo_keyname
342
343    cases = {}
344
345    for f in ty.fields:
346        val = '{}_{}'.format(keytype, f.name)
347        val = xenlight_golang_fmt_name(val)
348
349        # Add to list of cases to make for the switch
350        # statement below.
351        cases[f.name] = (val, f.type)
352
353        if f.type is None:
354            continue
355
356        # Define fromC func for 'union' struct.
357        typename   = '{}_{}_union_{}'.format(struct_name,keyname,f.name)
358        gotypename = xenlight_golang_fmt_name(typename)
359
360        # Define the function here. The cases for keyed unions are a little
361        # different.
362        s = 'func (x *{}) fromC(xc *C.{}) error {{\n'.format(gotypename,struct_name)
363        s += 'if {}(xc.{}) != {} {{\n'.format(gokeytype,cgo_keyname,val)
364        err_string = '"expected union key {}"'.format(val)
365        s += 'return errors.New({})\n'.format(err_string)
366        s += '}\n\n'
367        s += 'tmp := (*C.{})(unsafe.Pointer(&xc.{}[0]))\n'.format(typename,union_name)
368
369        for nf in f.type.fields:
370            s += xenlight_golang_convert_from_C(nf,cvarname='tmp')
371
372        s += 'return nil\n'
373        s += '}\n'
374
375        extras.append(s)
376
377    s = 'x.{} = {}(xc.{})\n'.format(gokeyname,gokeytype,cgo_keyname)
378    s += 'switch x.{}{{\n'.format(gokeyname)
379
380    # Create switch statement to determine which 'union element'
381    # to populate in the Go struct.
382    for case_name, case_tuple in sorted(cases.items()):
383        (case_val, case_type) = case_tuple
384
385        s += 'case {}:\n'.format(case_val)
386
387        if case_type is None:
388            s += "x.{} = nil\n".format(field_name)
389            continue
390
391        gotype = '{}_{}_union_{}'.format(struct_name,keyname,case_name)
392        gotype = xenlight_golang_fmt_name(gotype)
393        goname = '{}_{}'.format(keyname,case_name)
394        goname = xenlight_golang_fmt_name(goname,exported=False)
395
396        s += 'var {} {}\n'.format(goname, gotype)
397        s += 'if err := {}.fromC(xc);'.format(goname)
398        s += 'err != nil {{\n return fmt.Errorf("converting field {}: %v", err)\n}}\n'.format(goname)
399
400        s += 'x.{} = {}\n'.format(field_name, goname)
401
402    # End switch statement
403    s += 'default:\n'
404    err_string = '"invalid union key \'%v\'", x.{}'.format(gokeyname)
405    s += 'return fmt.Errorf({})'.format(err_string)
406    s += '}\n'
407
408    return (s,extras)
409
410def xenlight_golang_array_from_C(ty = None):
411    """
412    Convert C array to Go slice using the method
413    described here:
414
415    https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices
416    """
417    s = ''
418
419    gotypename = xenlight_golang_fmt_name(ty.type.elem_type.typename)
420    goname     = xenlight_golang_fmt_name(ty.name)
421    ctypename  = ty.type.elem_type.typename
422    cname      = ty.name
423    cslice     = 'c{}'.format(goname)
424    clenvar    = ty.type.lenvar.name
425
426    s += 'x.{} = nil\n'.format(goname)
427    s += 'if n := int(xc.{}); n > 0 {{\n'.format(clenvar)
428    s += '{} := '.format(cslice)
429    s +='(*[1<<28]C.{})(unsafe.Pointer(xc.{}))[:n:n]\n'.format(ctypename, cname)
430    s += 'x.{} = make([]{}, n)\n'.format(goname, gotypename)
431    s += 'for i, v := range {} {{\n'.format(cslice)
432
433    is_enum = isinstance(ty.type.elem_type,idl.Enumeration)
434    if gotypename in go_builtin_types or is_enum:
435        s += 'x.{}[i] = {}(v)\n'.format(goname, gotypename)
436    else:
437        s += 'if err := x.{}[i].fromC(&v); err != nil {{\n'.format(goname)
438        s += 'return fmt.Errorf("converting field {}: %v", err) }}\n'.format(goname)
439
440    s += '}\n}\n'
441
442    return s
443
444def xenlight_golang_define_to_C(ty = None, typename = None, nested = False):
445    """
446    Define the toC marshaling function for the type
447    represented by ty.
448    """
449    func = 'func (x *{}) toC(xc *C.{}) (err error){{{}\n return nil\n }}\n'
450    body = ''
451
452    if ty.dispose_fn is not None:
453        body += 'defer func(){{\nif err != nil{{\nC.{}(xc)}}\n}}()\n\n'.format(ty.dispose_fn)
454
455    goname = xenlight_golang_fmt_name(ty.typename)
456    cname  = ty.typename
457
458    for f in ty.fields:
459        if f.type.typename is not None:
460            if isinstance(f.type, idl.Array):
461                body += xenlight_golang_array_to_C(f)
462                continue
463
464            body += xenlight_golang_convert_to_C(f)
465
466        elif isinstance(f.type, idl.Struct):
467            for nf in f.type.fields:
468                body += xenlight_golang_convert_to_C(nf, outer_name=f.name)
469
470        elif isinstance(f.type, idl.KeyedUnion):
471            body += xenlight_golang_union_to_C(f.type, f.name, ty.typename)
472
473        else:
474            raise Exception('type {} not supported'.format(f.type))
475
476    return func.format(goname, cname, body)
477
478def xenlight_golang_convert_to_C(ty = None, outer_name = None,
479                                 govarname = None, cvarname = None):
480    """
481    Returns a line of Go code that converts the Go type represented
482    by ty to its corresponding Go type.
483
484    If outer_name is set, the type is treated as nested within another field
485    named outer_name.
486    """
487    s = ''
488
489    # Use 'xc' as the name for the C variable unless otherwise specified.
490    if cvarname is None:
491        cvarname = 'xc'
492
493    # Use 'x' as the name for the Go variable unless otherwise specified.
494    if govarname is None:
495        govarname = 'x'
496
497    gotypename = xenlight_golang_fmt_name(ty.type.typename)
498    ctypename  = ty.type.typename
499    goname     = xenlight_golang_fmt_name(ty.name)
500    cname      = ty.name
501
502    # In cgo, C names that conflict with Go keywords can be
503    # accessed by prepending an underscore to the name.
504    if cname in go_keywords:
505        cname = '_' + cname
506
507    # If outer_name is set, treat this as nested.
508    if outer_name is not None:
509        goname = '{}.{}'.format(xenlight_golang_fmt_name(outer_name), goname)
510        cname  = '{}.{}'.format(outer_name, cname)
511
512    is_castable = (ty.type.json_parse_type == 'JSON_INTEGER' or
513                   isinstance(ty.type, idl.Enumeration) or
514                   gotypename in go_builtin_types)
515
516    if not is_castable:
517        s += 'if err := {}.{}.toC(&{}.{}); err != nil {{\n'.format(govarname,goname,
518                                                                   cvarname,cname)
519        s += 'return fmt.Errorf("converting field {}: %v", err)\n}}\n'.format(goname)
520
521    elif gotypename == 'string':
522        # Use the cgo helper for converting C strings.
523        s += 'if {}.{} != "" {{\n'.format(govarname,goname)
524        s += '{}.{} = C.CString({}.{})}}\n'.format(cvarname,cname,
525                                                   govarname,goname)
526
527    else:
528        s += '{}.{} = C.{}({}.{})\n'.format(cvarname,cname,ctypename,
529                                            govarname,goname)
530
531    return s
532
533def xenlight_golang_union_to_C(ty = None, union_name = '',
534                               struct_name = ''):
535    keyname   = ty.keyvar.name
536    gokeyname = xenlight_golang_fmt_name(keyname)
537    keytype   = ty.keyvar.type.typename
538    gokeytype = xenlight_golang_fmt_name(keytype)
539
540    interface_name = '{}_{}_union'.format(struct_name, keyname)
541    interface_name = xenlight_golang_fmt_name(interface_name, exported=False)
542
543    cgo_keyname = keyname
544    if cgo_keyname in go_keywords:
545        cgo_keyname = '_' + cgo_keyname
546
547
548    s = 'xc.{} = C.{}(x.{})\n'.format(cgo_keyname,keytype,gokeyname)
549    s += 'switch x.{}{{\n'.format(gokeyname)
550
551    # Create switch statement to determine how to populate the C union.
552    for f in ty.fields:
553        key_val = '{}_{}'.format(keytype, f.name)
554        key_val = xenlight_golang_fmt_name(key_val)
555
556        s += 'case {}:\n'.format(key_val)
557
558        if f.type is None:
559            s += "break\n"
560            continue
561
562        cgotype = '{}_{}_union_{}'.format(struct_name,keyname,f.name)
563        gotype  = xenlight_golang_fmt_name(cgotype)
564
565        field_name = xenlight_golang_fmt_name('{}_union'.format(keyname))
566        s += 'tmp, ok := x.{}.({})\n'.format(field_name,gotype)
567        s += 'if !ok {\n'
568        s += 'return errors.New("wrong type for union key {}")\n'.format(keyname)
569        s += '}\n'
570
571        s += 'var {} C.{}\n'.format(f.name,cgotype)
572        for uf in f.type.fields:
573            s += xenlight_golang_convert_to_C(uf,cvarname=f.name,
574                                              govarname='tmp')
575
576        # The union is still represented as Go []byte.
577        s += '{}Bytes := C.GoBytes(unsafe.Pointer(&{}),C.sizeof_{})\n'.format(f.name,
578                                                                              f.name,
579                                                                              cgotype)
580        s += 'copy(xc.{}[:],{}Bytes)\n'.format(union_name,f.name)
581
582    # End switch statement
583    s += 'default:\n'
584    err_string = '"invalid union key \'%v\'", x.{}'.format(gokeyname)
585    s += 'return fmt.Errorf({})'.format(err_string)
586    s += '}\n'
587
588    return s
589
590def xenlight_golang_array_to_C(ty = None):
591    s = ''
592
593    gotypename = xenlight_golang_fmt_name(ty.type.elem_type.typename)
594    goname     = xenlight_golang_fmt_name(ty.name)
595    ctypename  = ty.type.elem_type.typename
596    cname      = ty.name
597    clenvar    = ty.type.lenvar.name
598    golenvar   = xenlight_golang_fmt_name(clenvar,exported=False)
599
600    is_enum = isinstance(ty.type.elem_type,idl.Enumeration)
601    if gotypename in go_builtin_types or is_enum:
602        s += 'if {} := len(x.{}); {} > 0 {{\n'.format(golenvar,goname,golenvar)
603        s += 'xc.{} = (*C.{})(C.malloc(C.size_t({}*{})))\n'.format(cname,ctypename,
604                                                                   golenvar,golenvar)
605        s += 'xc.{} = C.int({})\n'.format(clenvar,golenvar)
606        s += 'c{} := (*[1<<28]C.{})(unsafe.Pointer(xc.{}))[:{}:{}]\n'.format(goname,
607                                                                      ctypename,cname,
608                                                                      golenvar,golenvar)
609        s += 'for i,v := range x.{} {{\n'.format(goname)
610        s += 'c{}[i] = C.{}(v)\n'.format(goname,ctypename)
611        s += '}\n}\n'
612
613        return s
614
615    s += 'if {} := len(x.{}); {} > 0 {{\n'.format(golenvar,goname,golenvar)
616    s += 'xc.{} = (*C.{})(C.malloc(C.ulong({})*C.sizeof_{}))\n'.format(cname,ctypename,
617                                                                   golenvar,ctypename)
618    s += 'xc.{} = C.int({})\n'.format(clenvar,golenvar)
619    s += 'c{} := (*[1<<28]C.{})(unsafe.Pointer(xc.{}))[:{}:{}]\n'.format(goname,
620                                                                         ctypename,cname,
621                                                                         golenvar,golenvar)
622    s += 'for i,v := range x.{} {{\n'.format(goname)
623    s += 'if err := v.toC(&c{}[i]); err != nil {{\n'.format(goname)
624    s += 'return fmt.Errorf("converting field {}: %v", err)\n'.format(goname)
625    s += '}\n}\n}\n'
626
627    return s
628
629def xenlight_golang_define_constructor(ty = None):
630    s = ''
631
632    ctypename  = ty.typename
633    gotypename = xenlight_golang_fmt_name(ctypename)
634
635    # Since this func is exported, add a comment as per Go conventions.
636    s += '// New{} returns an instance of {}'.format(gotypename,gotypename)
637    s += ' initialized with defaults.\n'
638
639    # If a struct has a keyed union, an extra argument is
640    # required in the function signature, and an extra _init
641    # call is needed.
642    params   = []
643    init_fns = []
644
645    # Add call to parent init_fn first.
646    init_fns.append('C.{}(&xc)'.format(ty.init_fn))
647
648    for f in ty.fields:
649        if not isinstance(f.type, idl.KeyedUnion):
650            continue
651
652        param = f.type.keyvar
653
654        param_ctype  = param.type.typename
655        param_gotype = xenlight_golang_fmt_name(param_ctype)
656        param_goname = xenlight_golang_fmt_name(param.name,exported=False)
657
658        # Serveral keyed unions use 'type' as the key variable name. In
659        # that case, prepend the first letter of the Go type name.
660        if param_goname == 'type':
661            param_goname = '{}type'.format(param_gotype.lower()[0])
662
663        # Add call to keyed union's init_fn.
664        init_fns.append('C.{}_{}(&xc, C.{}({}))'.format(ty.init_fn,
665                                                        param.name,
666                                                        param_ctype,
667                                                        param_goname))
668
669        # Add to params list.
670        params.append('{} {}'.format(param_goname, param_gotype))
671
672    # Define function
673    s += 'func New{}({}) (*{}, error) {{\n'.format(gotypename,
674                                                   ','.join(params),
675                                                   gotypename)
676
677    # Declare variables.
678    s += 'var (\nx {}\nxc C.{})\n\n'.format(gotypename, ctypename)
679
680    # Write init_fn calls.
681    s += '\n'.join(init_fns)
682    s += '\n'
683
684    # Make sure dispose_fn get's called when constructor
685    # returns.
686    if ty.dispose_fn is not None:
687        s += 'defer C.{}(&xc)\n'.format(ty.dispose_fn)
688
689    s += '\n'
690
691    # Call fromC to initialize Go type.
692    s += 'if err := x.fromC(&xc); err != nil {\n'
693    s += 'return nil, err }\n\n'
694    s += 'return &x, nil}\n'
695
696    return s
697
698def xenlight_golang_fmt_name(name, exported = True):
699    """
700    Take a given type name and return an
701    appropriate Go type name.
702    """
703    if name in builtin_type_names.keys():
704        return builtin_type_names[name]
705
706    # Name is not a builtin, format it for Go.
707    words = name.split('_')
708
709    # Remove 'libxl' prefix
710    if words[0].lower() == 'libxl':
711        words.remove(words[0])
712
713    if exported:
714        return ''.join(x.title() for x in words)
715
716    return words[0] + ''.join(x.title() for x in words[1:])
717
718if __name__ == '__main__':
719    idlname = sys.argv[1]
720
721    (builtins, types) = idl.parse(idlname)
722
723    for b in builtins:
724        name = b.typename
725        builtin_type_names[name] = xenlight_golang_fmt_name(name)
726
727    header_comment="""// DO NOT EDIT.
728//
729// This file is generated by:
730// {}
731//
732
733""".format(' '.join(sys.argv))
734
735    xenlight_golang_generate_types(types=types,
736                                   comment=header_comment)
737    xenlight_golang_generate_helpers(types=types,
738                                     comment=header_comment)
739