vppapigen: coverity issues in autogenerated code pass 3.
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
1 #
2 # Copyright (c) 2020 Cisco and/or its affiliates.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15
16 #
17 # Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
18 # binary API format
19 #
20
21 '''
22 This module creates C code for core VPP, VPP plugins and client side VAT and
23 VAT2 tests.
24 '''
25
26 import datetime
27 import os
28 import time
29 import sys
30 from io import StringIO
31 import shutil
32
33 process_imports = False
34
35
36 ###############################################################################
37 class ToJSON():
38     '''Class to generate functions converting from VPP binary API to JSON.'''
39     _dispatch = {}
40     noprint_fields = {'_vl_msg_id': None,
41                       'client_index': None,
42                       'context': None}
43     is_number = {'u8': None,
44                  'i8': None,
45                  'u16': None,
46                  'i16': None,
47                  'u32': None,
48                  'i32': None,
49                  'u64': None,
50                  'i64': None,
51                  'f64': None,
52                  }
53
54     def __init__(self, module, types, defines, imported_types, stream):
55         self.stream = stream
56         self.module = module
57         self.defines = defines
58         self.types = types
59         self.types_hash = {'vl_api_'+d.name+'_t':
60                            d for d in types + imported_types}
61         self.defines_hash = {d.name: d for d in defines}
62
63     def header(self):
64         '''Output the top boilerplate.'''
65         write = self.stream.write
66         write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
67         write('#define included_{}_api_tojson_h\n'.format(self.module))
68         write('#include <vppinfra/cJSON.h>\n\n')
69         write('#include <vat2/jsonconvert.h>\n\n')
70
71     def footer(self):
72         '''Output the bottom boilerplate.'''
73         write = self.stream.write
74         write('#endif\n')
75
76     def get_base_type(self, t):
77         vt_type = None
78         try:
79             vt = self.types_hash[t]
80             if vt.type == 'Using' and 'length' not in vt.alias:
81                 vt_type = vt.alias['type']
82         except KeyError:
83             vt = t
84         return vt, vt_type
85
86     def get_json_func(self, t):
87         '''Given the type, returns the function to use to create a
88         cJSON object'''
89         vt, vt_type = self.get_base_type(t)
90
91         if t in self.is_number or vt_type in self.is_number:
92             return 'cJSON_AddNumberToObject', '', False
93         if t == 'bool':
94             return 'cJSON_AddBoolToObject', '', False
95
96         # Lookup type name check if it's enum
97         if vt.type == 'Enum' or vt.type == 'EnumFlag':
98             return '{t}_tojson'.format(t=t), '', True
99         return '{t}_tojson'.format(t=t), '&', True
100
101     def get_json_array_func(self, t):
102         '''Given a type returns the function to create a cJSON object
103         for arrays.'''
104         if t in self.is_number:
105             return 'cJSON_CreateNumber', ''
106         if t == 'bool':
107             return 'cJSON_CreateBool', ''
108         vt, vt_type = self.get_base_type(t)
109         if vt.type == 'Enum' or vt.type == 'EnumFlag':
110             return '{t}_tojson'.format(t=t), ''
111         return '{t}_tojson'.format(t=t), '&'
112
113     def print_string(self, o):
114         '''Create cJSON object from vl_api_string_t'''
115         write = self.stream.write
116         if o.modern_vla:
117             write('    vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'
118                   .format(n=o.fieldname))
119         else:
120
121             write('    cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'
122                   .format(n=o.fieldname))
123
124     def print_field(self, o):
125         '''Called for every field in a typedef or define.'''
126         write = self.stream.write
127         if o.fieldname in self.noprint_fields:
128             return
129
130         f, p, newobj = self.get_json_func(o.fieldtype)
131
132         if newobj:
133             write('    cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'
134                   .format(f=f, p=p, n=o.fieldname))
135         else:
136             write('    {f}(o, "{n}", {p}a->{n});\n'
137                   .format(f=f, p=p, n=o.fieldname))
138
139     _dispatch['Field'] = print_field
140
141     def print_array(self, o):
142         '''Converts a VPP API array to cJSON array.'''
143         write = self.stream.write
144
145         forloop = '''\
146     {{
147         int i;
148         cJSON *array = cJSON_AddArrayToObject(o, "{n}");
149         for (i = 0; i < {lfield}; i++) {{
150             cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
151         }}
152     }}
153 '''
154
155         if o.fieldtype == 'string':
156             self.print_string(o)
157             return
158
159         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
160         if o.fieldtype == 'u8':
161             write('    {\n')
162             # What is length field doing here?
163             write('    u8 *s = format(0, "0x%U", format_hex_bytes, '
164                   '&a->{n}, {lfield});\n'
165                   .format(n=o.fieldname, lfield=lfield))
166             write('    cJSON_AddStringToObject(o, "{n}", (char *)s);\n'
167                   .format(n=o.fieldname))
168             write('    vec_free(s);\n')
169             write('    }\n')
170             return
171
172         f, p = self.get_json_array_func(o.fieldtype)
173         write(forloop.format(lfield=lfield,
174                              t=o.fieldtype,
175                              n=o.fieldname,
176                              f=f,
177                              p=p))
178
179     _dispatch['Array'] = print_array
180
181     def print_enum(self, o):
182         '''Create cJSON object (string) for VPP API enum'''
183         write = self.stream.write
184         write('static inline cJSON *vl_api_{name}_t_tojson '
185               '(vl_api_{name}_t a) {{\n'.format(name=o.name))
186
187         write("    switch(a) {\n")
188         for b in o.block:
189             write("    case %s:\n" % b[1])
190             write('        return cJSON_CreateString("{}");\n'.format(b[0]))
191         write('    default: return cJSON_CreateString("Invalid ENUM");\n')
192         write('    }\n')
193         write('    return 0;\n')
194         write('}\n')
195
196     _dispatch['Enum'] = print_enum
197
198     def print_enum_flag(self, o):
199         '''Create cJSON object (string) for VPP API enum'''
200         write = self.stream.write
201         write('static inline cJSON *vl_api_{name}_t_tojson '
202               '(vl_api_{name}_t a) {{\n'.format(name=o.name))
203         write('    cJSON *array = cJSON_CreateArray();\n')
204
205         for b in o.block:
206             write('    if (a & {})\n'.format(b[0]))
207             write('       cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(b[0]))
208         write('    return array;\n')
209         write('}\n')
210
211     _dispatch['EnumFlag'] = print_enum_flag
212
213     def print_typedef(self, o):
214         '''Create cJSON (dictionary) object from VPP API typedef'''
215         write = self.stream.write
216         write('static inline cJSON *vl_api_{name}_t_tojson '
217               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
218         write('    cJSON *o = cJSON_CreateObject();\n')
219
220         for t in o.block:
221             self._dispatch[t.type](self, t)
222
223         write('    return o;\n')
224         write('}\n')
225
226     def print_define(self, o):
227         '''Create cJSON (dictionary) object from VPP API define'''
228         write = self.stream.write
229         write('static inline cJSON *vl_api_{name}_t_tojson '
230               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
231         write('    cJSON *o = cJSON_CreateObject();\n')
232         write('    cJSON_AddStringToObject(o, "_msgname", "{}");\n'
233               .format(o.name))
234
235         for t in o.block:
236             self._dispatch[t.type](self, t)
237
238         write('    return o;\n')
239         write('}\n')
240
241     def print_using(self, o):
242         '''Create cJSON (dictionary) object from VPP API aliased type'''
243         if o.manual_print:
244             return
245
246         write = self.stream.write
247         write('static inline cJSON *vl_api_{name}_t_tojson '
248               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
249
250         write('    u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'
251               .format(o.name))
252         write('    cJSON *o = cJSON_CreateString((char *)s);\n')
253         write('    vec_free(s);\n')
254         write('    return o;\n')
255         write('}\n')
256
257     _dispatch['Typedef'] = print_typedef
258     _dispatch['Define'] = print_define
259     _dispatch['Using'] = print_using
260     _dispatch['Union'] = print_typedef
261
262     def generate_function(self, t):
263         '''Main entry point'''
264         write = self.stream.write
265         if t.manual_print:
266             write('/* Manual print {} */\n'.format(t.name))
267             return
268         self._dispatch[t.type](self, t)
269
270     def generate_types(self):
271         '''Main entry point'''
272         for t in self.types:
273             self.generate_function(t)
274
275     def generate_defines(self):
276         '''Main entry point'''
277         for t in self.defines:
278             self.generate_function(t)
279
280
281 class FromJSON():
282     '''
283     Parse JSON objects into VPP API binary message structures.
284     '''
285     _dispatch = {}
286     noprint_fields = {'_vl_msg_id': None,
287                       'client_index': None,
288                       'context': None}
289     is_number = {'u8': None,
290                  'i8': None,
291                  'u16': None,
292                  'i16': None,
293                  'u32': None,
294                  'i32': None,
295                  'u64': None,
296                  'i64': None,
297                  'f64': None,
298                  }
299
300     def __init__(self, module, types, defines, imported_types, stream):
301         self.stream = stream
302         self.module = module
303         self.defines = defines
304         self.types = types
305         self.types_hash = {'vl_api_'+d.name+'_t':
306                            d for d in types + imported_types}
307         self.defines_hash = {d.name: d for d in defines}
308
309     def header(self):
310         '''Output the top boilerplate.'''
311         write = self.stream.write
312         write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
313         write('#define included_{}_api_fromjson_h\n'.format(self.module))
314         write('#include <vppinfra/cJSON.h>\n\n')
315         write('#include <vat2/jsonconvert.h>\n\n')
316         write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
317
318     def is_base_type(self, t):
319         '''Check if a type is one of the VPP API base types'''
320         if t in self.is_number:
321             return True
322         if t == 'bool':
323             return True
324         return False
325
326     def footer(self):
327         '''Output the bottom boilerplate.'''
328         write = self.stream.write
329         write('#endif\n')
330
331     def print_string(self, o, toplevel=False):
332         '''Convert JSON string to vl_api_string_t'''
333         write = self.stream.write
334
335         msgvar = "a" if toplevel else "*mp"
336         msgsize = "l" if toplevel else "*len"
337
338         if o.modern_vla:
339             write('    char *p = cJSON_GetStringValue(item);\n')
340             write('    size_t plen = strlen(p);\n')
341             write('    {msgvar} = realloc({msgvar}, {msgsize} + plen);\n'
342                   .format(msgvar=msgvar, msgsize=msgsize))
343             write('    if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar))
344             write('    vl_api_c_string_to_api_string(p, (void *){msgvar} + '
345                   '{msgsize} - sizeof(vl_api_string_t));\n'
346                   .format(msgvar=msgvar, msgsize=msgsize))
347             write('    {msgsize} += plen;\n'.format(msgsize=msgsize))
348         else:
349             write('    strncpy_s((char *)a->{n}, sizeof(a->{n}), '
350                   'cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n'
351                   .format(n=o.fieldname))
352
353     def print_field(self, o, toplevel=False):
354         '''Called for every field in a typedef or define.'''
355         write = self.stream.write
356         if o.fieldname in self.noprint_fields:
357             return
358         is_bt = self.is_base_type(o.fieldtype)
359         t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
360
361         msgvar = "(void **)&a" if toplevel else "mp"
362         msgsize = "&l" if toplevel else "len"
363
364         if is_bt:
365             write('    vl_api_{t}_fromjson(item, &a->{n});\n'
366                   .format(t=o.fieldtype, n=o.fieldname))
367         else:
368             write('    if ({t}_fromjson({msgvar}, '
369                   '{msgsize}, item, &a->{n}) < 0) goto error;\n'
370                   .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
371
372     _dispatch['Field'] = print_field
373
374     def print_array(self, o, toplevel=False):
375         '''Convert JSON array to VPP API array'''
376         write = self.stream.write
377
378         forloop = '''\
379     {{
380         int i;
381         cJSON *array = cJSON_GetObjectItem(o, "{n}");
382         int size = cJSON_GetArraySize(array);
383         if (size != {lfield}) return 0;
384         for (i = 0; i < size; i++) {{
385             cJSON *e = cJSON_GetArrayItem(array, i);
386             {call}
387         }}
388     }}
389 '''
390         forloop_vla = '''\
391     {{
392         int i;
393         cJSON *array = cJSON_GetObjectItem(o, "{n}");
394         int size = cJSON_GetArraySize(array);
395         {lfield} = size;
396         {realloc} = realloc({realloc}, {msgsize} + sizeof({t}) * size);
397         {t} *d = (void *){realloc} + {msgsize};
398         {msgsize} += sizeof({t}) * size;
399         for (i = 0; i < size; i++) {{
400             cJSON *e = cJSON_GetArrayItem(array, i);
401             {call}
402         }}
403     }}
404 '''
405         t = o.fieldtype
406         if o.fieldtype == 'string':
407             self.print_string(o, toplevel)
408             return
409
410         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
411         msgvar = "(void **)&a" if toplevel else "mp"
412         realloc = "a" if toplevel else "*mp"
413         msgsize = "l" if toplevel else "*len"
414
415         if o.fieldtype == 'u8':
416             if o.lengthfield:
417                 write('    s = u8string_fromjson(o, "{}");\n'
418                       .format(o.fieldname))
419                 write('    if (!s) return 0;\n')
420                 write('    {} = vec_len(s);\n'.format(lfield))
421
422                 write('    {realloc} = realloc({realloc}, {msgsize} + '
423                       'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc))
424                 write('    memcpy((void *){realloc} + {msgsize}, s, '
425                       'vec_len(s));\n'.format(realloc=realloc, msgsize=msgsize))
426                 write('    {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
427
428                 write('    vec_free(s);\n')
429             else:
430                 write('    u8string_fromjson2(o, "{n}", a->{n});\n'
431                       .format(n=o.fieldname))
432             return
433
434         is_bt = self.is_base_type(o.fieldtype)
435
436         if o.lengthfield:
437             if is_bt:
438                 call = ('vl_api_{t}_fromjson(e, &d[i]);'
439                         .format(t=o.fieldtype))
440             else:
441                 call = ('{t}_fromjson({msgvar}, len, e, &d[i]); '
442                         .format(t=o.fieldtype, msgvar=msgvar))
443             write(forloop_vla.format(lfield=lfield,
444                                      t=o.fieldtype,
445                                      n=o.fieldname,
446                                      call=call,
447                                      realloc=realloc,
448                                      msgsize=msgsize))
449         else:
450             if is_bt:
451                 call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
452                         .format(t=t, n=o.fieldname))
453             else:
454                 call = ('if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;'
455                         .format(t, msgvar, o.fieldname))
456             write(forloop.format(lfield=lfield,
457                                  t=t,
458                                  n=o.fieldname,
459                                  call=call,
460                                  msgvar=msgvar,
461                                  realloc=realloc,
462                                  msgsize=msgsize))
463
464     _dispatch['Array'] = print_array
465
466     def print_enum(self, o):
467         '''Convert to JSON enum(string) to VPP API enum (int)'''
468         write = self.stream.write
469         write('static inline int vl_api_{n}_t_fromjson'
470               '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
471               .format(n=o.name))
472         write('    char *p = cJSON_GetStringValue(o);\n')
473         for b in o.block:
474             write('    if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'
475                   .format(b[0], b[1]))
476         write('    *a = 0;\n')
477         write('    return -1;\n')
478         write('}\n')
479
480     _dispatch['Enum'] = print_enum
481
482     def print_enum_flag(self, o):
483         '''Convert to JSON enum(string) to VPP API enum (int)'''
484         write = self.stream.write
485         write('static inline int vl_api_{n}_t_fromjson '
486               '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
487               .format(n=o.name))
488         write('   int i;\n')
489         write('   *a = 0;\n')
490         write('   for (i = 0; i < cJSON_GetArraySize(o); i++) {\n')
491         write('       cJSON *e = cJSON_GetArrayItem(o, i);\n')
492         write('       char *p = cJSON_GetStringValue(e);\n')
493         write('       if (!p) return -1;\n')
494         for b in o.block:
495             write('       if (strcmp(p, "{}") == 0) *a |= {};\n'
496                   .format(b[0], b[1]))
497         write('    }\n')
498         write('   return 0;\n')
499         write('}\n')
500
501     _dispatch['EnumFlag'] = print_enum_flag
502
503     def print_typedef(self, o):
504         '''Convert from JSON object to VPP API binary representation'''
505         write = self.stream.write
506
507         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
508               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
509               .format(name=o.name))
510         write('    cJSON *item __attribute__ ((unused));\n')
511         write('    u8 *s __attribute__ ((unused));\n')
512         for t in o.block:
513             if t.type == 'Field' and t.is_lengthfield:
514                 continue
515             write('\n    item = cJSON_GetObjectItem(o, "{}");\n'
516                   .format(t.fieldname))
517             write('    if (!item) goto error;\n')
518             self._dispatch[t.type](self, t)
519
520         write('\n    return 0;\n')
521         write('\n  error:\n')
522         write('    return -1;\n')
523         write('}\n')
524
525     def print_union(self, o):
526         '''Convert JSON object to VPP API binary union'''
527         write = self.stream.write
528
529         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
530               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
531               .format(name=o.name))
532         write('    cJSON *item __attribute__ ((unused));\n')
533         write('    u8 *s __attribute__ ((unused));\n')
534         for t in o.block:
535             if t.type == 'Field' and t.is_lengthfield:
536                 continue
537             write('    item = cJSON_GetObjectItem(o, "{}");\n'
538                   .format(t.fieldname))
539             write('    if (item) {\n')
540             self._dispatch[t.type](self, t)
541             write('    };\n')
542         write('\n    return 0;\n')
543         write('\n  error:\n')
544         write('    return -1;\n')
545         write('}\n')
546
547     def print_define(self, o):
548         '''Convert JSON object to VPP API message'''
549         write = self.stream.write
550         write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
551               '(cJSON *o, int *len) {{\n'.format(name=o.name))
552         write('    cJSON *item __attribute__ ((unused));\n')
553         write('    u8 *s __attribute__ ((unused));\n')
554         write('    int l = sizeof(vl_api_{}_t);\n'.format(o.name))
555         write('    vl_api_{}_t *a = malloc(l);\n'.format(o.name))
556         write('\n')
557
558         for t in o.block:
559             if t.fieldname in self.noprint_fields:
560                 continue
561             if t.type == 'Field' and t.is_lengthfield:
562                 continue
563             write('    item = cJSON_GetObjectItem(o, "{}");\n'
564                   .format(t.fieldname))
565             write('    if (!item) goto error;\n')
566             self._dispatch[t.type](self, t, toplevel=True)
567             write('\n')
568
569         write('    *len = l;\n')
570         write('    return a;\n')
571         write('\n  error:\n')
572         write('    free(a);\n')
573         write('    return 0;\n')
574         write('}\n')
575
576     def print_using(self, o):
577         '''Convert JSON field to VPP type alias'''
578         write = self.stream.write
579
580         if o.manual_print:
581             return
582
583         t = o.using
584         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
585               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
586               .format(name=o.name))
587         if 'length' in o.alias:
588             if t.fieldtype != 'u8':
589                 raise ValueError("Error in processing type {} for {}"
590                                  .format(t.fieldtype, o.name))
591             write('    vl_api_u8_string_fromjson(o, (u8 *)a, {});\n'
592                   .format(o.alias['length']))
593         else:
594             write('    vl_api_{t}_fromjson(o, ({t} *)a);\n'
595                   .format(t=t.fieldtype))
596
597         write('    return 0;\n')
598         write('}\n')
599
600     _dispatch['Typedef'] = print_typedef
601     _dispatch['Define'] = print_define
602     _dispatch['Using'] = print_using
603     _dispatch['Union'] = print_union
604
605     def generate_function(self, t):
606         '''Main entry point'''
607         write = self.stream.write
608         if t.manual_print:
609             write('/* Manual print {} */\n'.format(t.name))
610             return
611         self._dispatch[t.type](self, t)
612
613     def generate_types(self):
614         '''Main entry point'''
615         for t in self.types:
616             self.generate_function(t)
617
618     def generate_defines(self):
619         '''Main entry point'''
620         for t in self.defines:
621             self.generate_function(t)
622
623
624 def generate_tojson(s, modulename, stream):
625     '''Generate all functions to convert from API to JSON'''
626     write = stream.write
627
628     write('/* Imported API files */\n')
629     for i in s['Import']:
630         f = i.filename.replace('plugins/', '')
631         write('#include <{}_tojson.h>\n'.format(f))
632
633     pp = ToJSON(modulename, s['types'], s['Define'], s['imported']['types'],
634                 stream)
635     pp.header()
636     pp.generate_types()
637     pp.generate_defines()
638     pp.footer()
639     return ''
640
641
642 def generate_fromjson(s, modulename, stream):
643     '''Generate all functions to convert from JSON to API'''
644     write = stream.write
645     write('/* Imported API files */\n')
646     for i in s['Import']:
647         f = i.filename.replace('plugins/', '')
648         write('#include <{}_fromjson.h>\n'.format(f))
649
650     pp = FromJSON(modulename, s['types'], s['Define'], s['imported']['types'],
651                   stream)
652     pp.header()
653     pp.generate_types()
654     pp.generate_defines()
655     pp.footer()
656
657     return ''
658
659 ###############################################################################
660
661
662 DATESTRING = datetime.datetime.utcfromtimestamp(
663     int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
664 TOP_BOILERPLATE = '''\
665 /*
666  * VLIB API definitions {datestring}
667  * Input file: {input_filename}
668  * Automatically generated: please edit the input file NOT this file!
669  */
670
671 #include <stdbool.h>
672 #if defined(vl_msg_id)||defined(vl_union_id) \\
673     || defined(vl_printfun) ||defined(vl_endianfun) \\
674     || defined(vl_api_version)||defined(vl_typedefs) \\
675     || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
676     || defined(vl_api_version_tuple)
677 /* ok, something was selected */
678 #else
679 #warning no content included from {input_filename}
680 #endif
681
682 #define VL_API_PACKED(x) x __attribute__ ((packed))
683 '''
684
685 BOTTOM_BOILERPLATE = '''\
686 /****** API CRC (whole file) *****/
687
688 #ifdef vl_api_version
689 vl_api_version({input_filename}, {file_crc:#08x})
690
691 #endif
692 '''
693
694
695 def msg_ids(s):
696     '''Generate macro to map API message id to handler'''
697     output = '''\
698
699 /****** Message ID / handler enum ******/
700
701 #ifdef vl_msg_id
702 '''
703
704     for t in s['Define']:
705         output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
706                   (t.name.upper(), t.name)
707     output += "#endif"
708
709     return output
710
711
712 def msg_names(s):
713     '''Generate calls to name mapping macro'''
714     output = '''\
715
716 /****** Message names ******/
717
718 #ifdef vl_msg_name
719 '''
720
721     for t in s['Define']:
722         dont_trace = 0 if t.dont_trace else 1
723         output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
724     output += "#endif"
725
726     return output
727
728
729 def msg_name_crc_list(s, suffix):
730     '''Generate list of names to CRC mappings'''
731     output = '''\
732
733 /****** Message name, crc list ******/
734
735 #ifdef vl_msg_name_crc_list
736 '''
737     output += "#define foreach_vl_msg_name_crc_%s " % suffix
738
739     for t in s['Define']:
740         output += "\\\n_(VL_API_%s, %s, %08x) " % \
741                    (t.name.upper(), t.name, t.crc)
742     output += "\n#endif"
743
744     return output
745
746
747 def api2c(fieldtype):
748     '''Map between API type names and internal VPP type names'''
749     mappingtable = {'string': 'vl_api_string_t', }
750     if fieldtype in mappingtable:
751         return mappingtable[fieldtype]
752     return fieldtype
753
754
755 def typedefs(filename):
756     '''Include in the main files to the types file'''
757     output = '''\
758
759 /****** Typedefs ******/
760
761 #ifdef vl_typedefs
762 #include "{include}.api_types.h"
763 #endif
764 '''.format(include=filename)
765     return output
766
767
768 FORMAT_STRINGS = {'u8': '%u',
769                   'bool': '%u',
770                   'i8': '%d',
771                   'u16': '%u',
772                   'i16': '%d',
773                   'u32': '%u',
774                   'i32': '%ld',
775                   'u64': '%llu',
776                   'i64': '%lld',
777                   'f64': '%.2f'}
778
779
780 class Printfun():
781     '''Functions for pretty printing VPP API messages'''
782     _dispatch = {}
783     noprint_fields = {'_vl_msg_id': None,
784                       'client_index': None,
785                       'context': None}
786
787     def __init__(self, stream):
788         self.stream = stream
789
790     @staticmethod
791     def print_string(o, stream):
792         '''Pretty print a vl_api_string_t'''
793         write = stream.write
794         if o.modern_vla:
795             write('    if (vl_api_string_len(&a->{f}) > 0) {{\n'
796                   .format(f=o.fieldname))
797             write('        s = format(s, "\\n%U{f}: %U", '
798                   'format_white_space, indent, '
799                   'vl_api_format_string, (&a->{f}));\n'.format(f=o.fieldname))
800             write('    } else {\n')
801             write('        s = format(s, "\\n%U{f}:", '
802                   'format_white_space, indent);\n'.format(f=o.fieldname))
803             write('    }\n')
804         else:
805             write('    s = format(s, "\\n%U{f}: %s", '
806                   'format_white_space, indent, a->{f});\n'
807                   .format(f=o.fieldname))
808
809     def print_field(self, o, stream):
810         '''Pretty print API field'''
811         write = stream.write
812         if o.fieldname in self.noprint_fields:
813             return
814         if o.fieldtype in FORMAT_STRINGS:
815             f = FORMAT_STRINGS[o.fieldtype]
816             write('    s = format(s, "\\n%U{n}: {f}", '
817                   'format_white_space, indent, a->{n});\n'
818                   .format(n=o.fieldname, f=f))
819         else:
820             write('    s = format(s, "\\n%U{n}: %U", '
821                   'format_white_space, indent, '
822                   'format_{t}, &a->{n}, indent);\n'
823                   .format(n=o.fieldname, t=o.fieldtype))
824
825     _dispatch['Field'] = print_field
826
827     def print_array(self, o, stream):
828         '''Pretty print API array'''
829         write = stream.write
830
831         forloop = '''\
832     for (i = 0; i < {lfield}; i++) {{
833         s = format(s, "\\n%U{n}: %U",
834                    format_white_space, indent, format_{t}, &a->{n}[i], indent);
835     }}
836 '''
837
838         forloop_format = '''\
839     for (i = 0; i < {lfield}; i++) {{
840         s = format(s, "\\n%U{n}: {t}",
841                    format_white_space, indent, a->{n}[i]);
842     }}
843 '''
844
845         if o.fieldtype == 'string':
846             self.print_string(o, stream)
847             return
848
849         if o.fieldtype == 'u8':
850             if o.lengthfield:
851                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
852                       'indent, format_hex_bytes, a->{n}, a->{lfield});\n'
853                       .format(n=o.fieldname, lfield=o.lengthfield))
854             else:
855                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
856                       'indent, format_hex_bytes, a, {lfield});\n'
857                       .format(n=o.fieldname, lfield=o.length))
858             return
859
860         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
861         if o.fieldtype in FORMAT_STRINGS:
862             write(forloop_format.format(lfield=lfield,
863                                         t=FORMAT_STRINGS[o.fieldtype],
864                                         n=o.fieldname))
865         else:
866             write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
867
868     _dispatch['Array'] = print_array
869
870     @staticmethod
871     def print_alias(k, v, stream):
872         '''Pretty print type alias'''
873         write = stream.write
874         if ('length' in v.alias and v.alias['length'] and
875                 v.alias['type'] == 'u8'):
876             write('    return format(s, "%U", format_hex_bytes, a, {});\n'
877                   .format(v.alias['length']))
878         elif v.alias['type'] in FORMAT_STRINGS:
879             write('    return format(s, "{}", *a);\n'
880                   .format(FORMAT_STRINGS[v.alias['type']]))
881         else:
882             write('    return format(s, "{} (print not implemented)");\n'
883                   .format(k))
884
885     @staticmethod
886     def print_enum(o, stream):
887         '''Pretty print API enum'''
888         write = stream.write
889         write("    switch(*a) {\n")
890         for b in o:
891             write("    case %s:\n" % b[1])
892             write('        return format(s, "{}");\n'.format(b[0]))
893         write('    }\n')
894
895     _dispatch['Enum'] = print_enum
896     _dispatch['EnumFlag'] = print_enum
897
898     def print_obj(self, o, stream):
899         '''Entry point'''
900         write = stream.write
901
902         if o.type in self._dispatch:
903             self._dispatch[o.type](self, o, stream)
904         else:
905             write('    s = format(s, "\\n{} {} {} (print not implemented");\n'
906                   .format(o.type, o.fieldtype, o.fieldname))
907
908
909 def printfun(objs, stream, modulename):
910     '''Main entry point for pretty print function generation'''
911     write = stream.write
912
913     h = '''\
914 /****** Print functions *****/
915 #ifdef vl_printfun
916 #ifndef included_{module}_printfun
917 #define included_{module}_printfun
918
919 #ifdef LP64
920 #define _uword_fmt \"%lld\"
921 #define _uword_cast (long long)
922 #else
923 #define _uword_fmt \"%ld\"
924 #define _uword_cast long
925 #endif
926
927 '''
928
929     signature = '''\
930 static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
931 {{
932     u8 *s = 0;
933     u32 indent __attribute__((unused)) = 2;
934     int i __attribute__((unused));
935 '''
936
937     h = h.format(module=modulename)
938     write(h)
939
940     pp = Printfun(stream)
941     for t in objs:
942         if t.manual_print:
943             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
944             continue
945         write(signature.format(name=t.name))
946         write('    /* Message definition: vl_api_{}_t: */\n'.format(t.name))
947         write("    s = format(s, \"vl_api_%s_t:\");\n" % t.name)
948         for o in t.block:
949             pp.print_obj(o, stream)
950         write('    vec_add1(s, 0);\n')
951         write('    vl_print (handle, (char *)s);\n')
952         write('    vec_free (s);\n')
953         write('    return handle;\n')
954         write('}\n\n')
955
956     write("\n#endif")
957     write("\n#endif /* vl_printfun */\n")
958
959     return ''
960
961
962 def printfun_types(objs, stream, modulename):
963     '''Pretty print API types'''
964     write = stream.write
965     pp = Printfun(stream)
966
967     h = '''\
968 /****** Print functions *****/
969 #ifdef vl_printfun
970 #ifndef included_{module}_printfun_types
971 #define included_{module}_printfun_types
972
973 '''
974     h = h.format(module=modulename)
975     write(h)
976
977     signature = '''\
978 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
979 {{
980     vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
981     u32 indent __attribute__((unused)) = va_arg (*args, u32);
982     int i __attribute__((unused));
983     indent += 2;
984 '''
985
986     for t in objs:
987         if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
988             write(signature.format(name=t.name))
989             pp.print_enum(t.block, stream)
990             write('    return s;\n')
991             write('}\n\n')
992             continue
993
994         if t.manual_print:
995             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
996             continue
997
998         if t.__class__.__name__ == 'Using':
999             write(signature.format(name=t.name))
1000             pp.print_alias(t.name, t, stream)
1001             write('}\n\n')
1002             continue
1003
1004         write(signature.format(name=t.name))
1005         for o in t.block:
1006             pp.print_obj(o, stream)
1007
1008         write('    return s;\n')
1009         write('}\n\n')
1010
1011     write("\n#endif")
1012     write("\n#endif /* vl_printfun_types */\n")
1013
1014
1015 def generate_imports(imports):
1016     '''Add #include matching the API import statements'''
1017     output = '/* Imported API files */\n'
1018     output += '#ifndef vl_api_version\n'
1019
1020     for i in imports:
1021         s = i.filename.replace('plugins/', '')
1022         output += '#include <{}.h>\n'.format(s)
1023     output += '#endif\n'
1024     return output
1025
1026
1027 ENDIAN_STRINGS = {
1028     'u16': 'clib_net_to_host_u16',
1029     'u32': 'clib_net_to_host_u32',
1030     'u64': 'clib_net_to_host_u64',
1031     'i16': 'clib_net_to_host_i16',
1032     'i32': 'clib_net_to_host_i32',
1033     'i64': 'clib_net_to_host_i64',
1034     'f64': 'clib_net_to_host_f64',
1035 }
1036
1037
1038 def endianfun_array(o):
1039     '''Generate endian functions for arrays'''
1040     forloop = '''\
1041     for (i = 0; i < {length}; i++) {{
1042         a->{name}[i] = {format}(a->{name}[i]);
1043     }}
1044 '''
1045
1046     forloop_format = '''\
1047     for (i = 0; i < {length}; i++) {{
1048         {type}_endian(&a->{name}[i]);
1049     }}
1050 '''
1051
1052     output = ''
1053     if o.fieldtype == 'u8' or o.fieldtype == 'string' or o.fieldtype == 'bool':
1054         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1055     else:
1056         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
1057         if o.fieldtype in ENDIAN_STRINGS:
1058             output += (forloop
1059                        .format(length=lfield,
1060                                format=ENDIAN_STRINGS[o.fieldtype],
1061                                name=o.fieldname))
1062         else:
1063             output += (forloop_format
1064                        .format(length=lfield, type=o.fieldtype,
1065                                name=o.fieldname))
1066     return output
1067
1068
1069 NO_ENDIAN_CONVERSION = {'client_index': None}
1070
1071
1072 def endianfun_obj(o):
1073     '''Generate endian conversion function for type'''
1074     output = ''
1075     if o.type == 'Array':
1076         return endianfun_array(o)
1077     if o.type != 'Field':
1078         output += ('    s = format(s, "\\n{} {} {} (print not implemented");\n'
1079                    .format(o.type, o.fieldtype, o.fieldname))
1080         return output
1081     if o.fieldname in NO_ENDIAN_CONVERSION:
1082         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1083         return output
1084     if o.fieldtype in ENDIAN_STRINGS:
1085         output += ('    a->{name} = {format}(a->{name});\n'
1086                    .format(name=o.fieldname,
1087                            format=ENDIAN_STRINGS[o.fieldtype]))
1088     elif o.fieldtype.startswith('vl_api_'):
1089         output += ('    {type}_endian(&a->{name});\n'
1090                    .format(type=o.fieldtype, name=o.fieldname))
1091     else:
1092         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1093
1094     return output
1095
1096
1097 def endianfun(objs, modulename):
1098     '''Main entry point for endian function generation'''
1099     output = '''\
1100
1101 /****** Endian swap functions *****/\n\
1102 #ifdef vl_endianfun
1103 #ifndef included_{module}_endianfun
1104 #define included_{module}_endianfun
1105
1106 #undef clib_net_to_host_uword
1107 #ifdef LP64
1108 #define clib_net_to_host_uword clib_net_to_host_u64
1109 #else
1110 #define clib_net_to_host_uword clib_net_to_host_u32
1111 #endif
1112
1113 '''
1114     output = output.format(module=modulename)
1115
1116     signature = '''\
1117 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1118 {{
1119     int i __attribute__((unused));
1120 '''
1121
1122     for t in objs:
1123         if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag' :
1124             output += signature.format(name=t.name)
1125             if t.enumtype in ENDIAN_STRINGS:
1126                 output += ('    *a = {}(*a);\n'
1127                            .format(ENDIAN_STRINGS[t.enumtype]))
1128             else:
1129                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
1130                            .format(name=t.name))
1131
1132             output += '}\n\n'
1133             continue
1134
1135         if t.manual_endian:
1136             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
1137             continue
1138
1139         if t.__class__.__name__ == 'Using':
1140             output += signature.format(name=t.name)
1141             if ('length' in t.alias and t.alias['length'] and
1142                     t.alias['type'] == 'u8'):
1143                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
1144                            .format(name=t.name))
1145             elif t.alias['type'] in FORMAT_STRINGS:
1146                 output += ('    *a = {}(*a);\n'
1147                            .format(ENDIAN_STRINGS[t.alias['type']]))
1148             else:
1149                 output += '    /* Not Implemented yet {} */'.format(t.name)
1150             output += '}\n\n'
1151             continue
1152
1153         output += signature.format(name=t.name)
1154
1155         for o in t.block:
1156             output += endianfun_obj(o)
1157         output += '}\n\n'
1158
1159     output += "\n#endif"
1160     output += "\n#endif /* vl_endianfun */\n\n"
1161
1162     return output
1163
1164
1165 def version_tuple(s, module):
1166     '''Generate semantic version string'''
1167     output = '''\
1168 /****** Version tuple *****/
1169
1170 #ifdef vl_api_version_tuple
1171
1172 '''
1173     if 'version' in s['Option']:
1174         v = s['Option']['version']
1175         (major, minor, patch) = v.split('.')
1176         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
1177                   (module, major, minor, patch)
1178
1179     output += "\n#endif /* vl_api_version_tuple */\n\n"
1180
1181     return output
1182
1183
1184 def generate_include_enum(s, module, stream):
1185     '''Generate <name>.api_enum.h'''
1186     write = stream.write
1187
1188     if 'Define' in s:
1189         write('typedef enum {\n')
1190         for t in s['Define']:
1191             write('   VL_API_{},\n'.format(t.name.upper()))
1192         write('   VL_MSG_{}_LAST\n'.format(module.upper()))
1193         write('}} vl_api_{}_enum_t;\n'.format(module))
1194
1195
1196 def generate_include_counters(s, stream):
1197     '''Include file for the counter data model types.'''
1198     write = stream.write
1199
1200     for counters in s:
1201         csetname = counters.name
1202         write('typedef enum {\n')
1203         for c in counters.block:
1204             write('   {}_ERROR_{},\n'
1205                   .format(csetname.upper(), c['name'].upper()))
1206         write('   {}_N_ERROR\n'.format(csetname.upper()))
1207         write('}} vl_counter_{}_enum_t;\n'.format(csetname))
1208
1209         write('extern vl_counter_t {}_error_counters[];\n'.format(csetname))
1210
1211
1212 def generate_include_types(s, module, stream):
1213     '''Generate separate API _types file.'''
1214     write = stream.write
1215
1216     write('#ifndef included_{module}_api_types_h\n'.format(module=module))
1217     write('#define included_{module}_api_types_h\n'.format(module=module))
1218
1219     if 'version' in s['Option']:
1220         v = s['Option']['version']
1221         (major, minor, patch) = v.split('.')
1222         write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'
1223               .format(m=module.upper(), v=major))
1224         write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'
1225               .format(m=module.upper(), v=minor))
1226         write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'
1227               .format(m=module.upper(), v=patch))
1228
1229     if 'Import' in s:
1230         write('/* Imported API files */\n')
1231         for i in s['Import']:
1232             filename = i.filename.replace('plugins/', '')
1233             write('#include <{}_types.h>\n'.format(filename))
1234
1235     for o in s['types'] + s['Define']:
1236         tname = o.__class__.__name__
1237         if tname == 'Using':
1238             if 'length' in o.alias:
1239                 write('typedef %s vl_api_%s_t[%s];\n' %
1240                       (o.alias['type'], o.name, o.alias['length']))
1241             else:
1242                 write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name))
1243         elif tname == 'Enum' or tname == 'EnumFlag':
1244             if o.enumtype == 'u32':
1245                 write("typedef enum {\n")
1246             else:
1247                 write("typedef enum __attribute__((packed)) {\n")
1248
1249             for b in o.block:
1250                 write("    %s = %s,\n" % (b[0], b[1]))
1251             write('} vl_api_%s_t;\n' % o.name)
1252             if o.enumtype != 'u32':
1253                 size1 = 'sizeof(vl_api_%s_t)' % o.name
1254                 size2 = 'sizeof(%s)' % o.enumtype
1255                 err_str = 'size of API enum %s is wrong' % o.name
1256                 write('STATIC_ASSERT(%s == %s, "%s");\n'
1257                       % (size1, size2, err_str))
1258         else:
1259             if tname == 'Union':
1260                 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n"
1261                       % o.name)
1262             else:
1263                 write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n")
1264                       % o.name)
1265             for b in o.block:
1266                 if b.type == 'Option':
1267                     continue
1268                 if b.type == 'Field':
1269                     write("    %s %s;\n" % (api2c(b.fieldtype),
1270                                             b.fieldname))
1271                 elif b.type == 'Array':
1272                     if b.lengthfield:
1273                         write("    %s %s[0];\n" % (api2c(b.fieldtype),
1274                                                    b.fieldname))
1275                     else:
1276                         # Fixed length strings decay to nul terminated u8
1277                         if b.fieldtype == 'string':
1278                             if b.modern_vla:
1279                                 write('    {} {};\n'
1280                                       .format(api2c(b.fieldtype),
1281                                               b.fieldname))
1282                             else:
1283                                 write('    u8 {}[{}];\n'
1284                                       .format(b.fieldname, b.length))
1285                         else:
1286                             write("    %s %s[%s];\n" %
1287                                   (api2c(b.fieldtype), b.fieldname,
1288                                    b.length))
1289                 else:
1290                     raise ValueError("Error in processing type {} for {}"
1291                                      .format(b, o.name))
1292
1293             write('} vl_api_%s_t;\n' % o.name)
1294
1295     for t in s['Define']:
1296         write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'
1297               .format(n=t.name, ID=t.name.upper(), crc=t.crc))
1298
1299     write("\n#endif\n")
1300
1301
1302 def generate_c_boilerplate(services, defines, counters, file_crc,
1303                            module, stream):
1304     '''VPP side plugin.'''
1305     write = stream.write
1306     define_hash = {d.name: d for d in defines}
1307
1308     hdr = '''\
1309 #define vl_endianfun            /* define message structures */
1310 #include "{module}.api.h"
1311 #undef vl_endianfun
1312
1313 /* instantiate all the print functions we know about */
1314 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1315 #define vl_printfun
1316 #include "{module}.api.h"
1317 #undef vl_printfun
1318
1319 '''
1320
1321     write(hdr.format(module=module))
1322     write('static u16\n')
1323     write('setup_message_id_table (void) {\n')
1324     write('   api_main_t *am = my_api_main;\n')
1325     write('   vl_msg_api_msg_config_t c;\n')
1326     write('   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1327           'VL_MSG_{m}_LAST);\n'
1328           .format(module, crc=file_crc, m=module.upper()))
1329
1330     for d in defines:
1331         write('   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1332               '                                VL_API_{ID} + msg_id_base);\n'
1333               .format(n=d.name, ID=d.name.upper(), crc=d.crc))
1334     for s in services:
1335         d = define_hash[s.caller]
1336         write('   c = (vl_msg_api_msg_config_t) '
1337               ' {{.id = VL_API_{ID} + msg_id_base,\n'
1338               '   .name = "{n}",\n'
1339               '   .handler = vl_api_{n}_t_handler,\n'
1340               '   .cleanup = vl_noop_handler,\n'
1341               '   .endian = vl_api_{n}_t_endian,\n'
1342               '   .print = vl_api_{n}_t_print,\n'
1343               '   .is_autoendian = {auto}}};\n'
1344               .format(n=s.caller, ID=s.caller.upper(),
1345                       auto=d.autoendian))
1346         write('   vl_msg_api_config (&c);\n')
1347         try:
1348             d = define_hash[s.reply]
1349             write('   c = (vl_msg_api_msg_config_t) '
1350                   '{{.id = VL_API_{ID} + msg_id_base,\n'
1351                   '  .name = "{n}",\n'
1352                   '  .handler = 0,\n'
1353                   '  .cleanup = vl_noop_handler,\n'
1354                   '  .endian = vl_api_{n}_t_endian,\n'
1355                   '  .print = vl_api_{n}_t_print,\n'
1356                   '  .is_autoendian = {auto}}};\n'
1357                   .format(n=s.reply, ID=s.reply.upper(),
1358                           auto=d.autoendian))
1359             write('   vl_msg_api_config (&c);\n')
1360         except KeyError:
1361             pass
1362
1363     write('   return msg_id_base;\n')
1364     write('}\n')
1365
1366     severity = {'error': 'VL_COUNTER_SEVERITY_ERROR',
1367                 'info': 'VL_COUNTER_SEVERITY_INFO',
1368                 'warn': 'VL_COUNTER_SEVERITY_WARN'}
1369
1370     for cnt in counters:
1371         csetname = cnt.name
1372         write('vl_counter_t {}_error_counters[] = {{\n'.format(csetname))
1373         for c in cnt.block:
1374             write('  {\n')
1375             write('   .name = "{}",\n'.format(c['name']))
1376             write('   .desc = "{}",\n'.format(c['description']))
1377             write('   .severity = {},\n'.format(severity[c['severity']]))
1378             write('  },\n')
1379         write('};\n')
1380
1381
1382 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
1383                                 stream):
1384     '''Generate code for legacy style VAT. To be deleted.'''
1385     write = stream.write
1386
1387     define_hash = {d.name: d for d in defines}
1388
1389     hdr = '''\
1390 #define vl_endianfun            /* define message structures */
1391 #include "{module}.api.h"
1392 #undef vl_endianfun
1393
1394 /* instantiate all the print functions we know about */
1395 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1396 #define vl_printfun
1397 #include "{module}.api.h"
1398 #undef vl_printfun
1399
1400 '''
1401
1402     write(hdr.format(module=module))
1403     for s in services:
1404         try:
1405             d = define_hash[s.reply]
1406         except KeyError:
1407             continue
1408         if d.manual_print:
1409             write('/*\n'
1410                   ' * Manual definition requested for: \n'
1411                   ' * vl_api_{n}_t_handler()\n'
1412                   ' */\n'
1413                   .format(n=s.reply))
1414             continue
1415         if not define_hash[s.caller].autoreply:
1416             write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
1417                   .format(n=s.reply))
1418             continue
1419         write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
1420         write('static void\n')
1421         write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
1422               .format(n=s.reply))
1423         write('   vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
1424         write('   i32 retval = ntohl(mp->retval);\n')
1425         write('   if (vam->async_mode) {\n')
1426         write('      vam->async_errors += (retval < 0);\n')
1427         write('   } else {\n')
1428         write('      vam->retval = retval;\n')
1429         write('      vam->result_ready = 1;\n')
1430         write('   }\n')
1431         write('}\n')
1432         write('#endif\n')
1433
1434         for e in s.events:
1435             if define_hash[e].manual_print:
1436                 continue
1437             write('static void\n')
1438             write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e))
1439             write('    vl_print(0, "{n} event called:");\n'.format(n=e))
1440             write('    vl_api_{n}_t_print(mp, 0);\n'.format(n=e))
1441             write('}\n')
1442
1443     write('static void\n')
1444     write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
1445     for s in services:
1446         write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1447               '                           "{n}",\n'
1448               '                           vl_api_{n}_t_handler, '
1449               '                           vl_noop_handler,\n'
1450               '                           vl_api_{n}_t_endian, '
1451               '                           vl_api_{n}_t_print,\n'
1452               '                           sizeof(vl_api_{n}_t), 1);\n'
1453               .format(n=s.reply, ID=s.reply.upper()))
1454         write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
1455               .format(n=s.caller))
1456         try:
1457             write('   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
1458                   .format(n=s.caller,
1459                           help=define_hash[s.caller].options['vat_help']))
1460         except KeyError:
1461             pass
1462
1463         # Events
1464         for e in s.events:
1465             write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1466                   '                          "{n}",\n'
1467                   '                           vl_api_{n}_t_handler, '
1468                   '                           vl_noop_handler,\n'
1469                   '                           vl_api_{n}_t_endian, '
1470                   '                           vl_api_{n}_t_print,\n'
1471                   '                           sizeof(vl_api_{n}_t), 1);\n'
1472                   .format(n=e, ID=e.upper()))
1473
1474     write('}\n')
1475     if plugin:
1476         write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1477     else:
1478         write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'
1479               .format(module))
1480     write('{\n')
1481     write('   {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1482     write('   mainp->vat_main = vam;\n')
1483     write('   mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1484           '                       ("{n}_{crc:08x}");\n'
1485           .format(n=module, crc=file_crc))
1486     write('   if (mainp->msg_id_base == (u16) ~0)\n')
1487     write('      return clib_error_return (0, "{} plugin not loaded...");\n'
1488           .format(module))
1489     write('   setup_message_id_table (vam, mainp->msg_id_base);\n')
1490     write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1491     write('    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1492     write('#endif\n')
1493     write('   return 0;\n')
1494     write('}\n')
1495
1496
1497 def apifunc(func):
1498     '''Check if a method is generated already.'''
1499     def _f(module, d, processed, *args):
1500         if d.name in processed:
1501             return None
1502         processed[d.name] = True
1503         return func(module, d, *args)
1504     return _f
1505
1506
1507 def c_test_api_service(s, dump, stream):
1508     '''Generate JSON code for a service.'''
1509     write = stream.write
1510
1511     req_reply_template = '''\
1512 static cJSON *
1513 api_{n} (cJSON *o)
1514 {{
1515   vl_api_{n}_t *mp;
1516   int len;
1517   if (!o) return 0;
1518   mp = vl_api_{n}_t_fromjson(o, &len);
1519   if (!mp) {{
1520     fprintf(stderr, "Failed converting JSON to API\\n");
1521     return 0;
1522   }}
1523
1524   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1525   vl_api_{n}_t_endian(mp);
1526   vac_write((char *)mp, len);
1527   free(mp);
1528
1529   /* Read reply */
1530   char *p;
1531   int l;
1532   vac_read(&p, &l, 5); // XXX: Fix timeout
1533     // XXX Will fail in case of event received. Do loop
1534   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1535     fprintf(stderr, "Mismatched reply\\n");
1536     return 0;
1537   }}
1538   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1539   vl_api_{r}_t_endian(rmp);
1540   return vl_api_{r}_t_tojson(rmp);
1541 }}
1542
1543 '''
1544     dump_details_template = '''\
1545 static cJSON *
1546 api_{n} (cJSON *o)
1547 {{
1548   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1549   int len;
1550   if (!o) return 0;
1551   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1552   if (!mp) {{
1553       fprintf(stderr, "Failed converting JSON to API\\n");
1554       return 0;
1555   }}
1556   mp->_vl_msg_id = msg_id;
1557   vl_api_{n}_t_endian(mp);
1558   vac_write((char *)mp, len);
1559   free(mp);
1560
1561   vat2_control_ping(123); // FIX CONTEXT
1562   cJSON *reply = cJSON_CreateArray();
1563
1564   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1565   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1566
1567   while (1) {{
1568     /* Read reply */
1569     char *p;
1570     int l;
1571     vac_read(&p, &l, 5); // XXX: Fix timeout
1572
1573     /* Message can be one of [_details, control_ping_reply
1574      * or unrelated event]
1575      */
1576     u16 reply_msg_id = ntohs(*((u16 *)p));
1577     if (reply_msg_id == ping_reply_msg_id) {{
1578         break;
1579     }}
1580
1581     if (reply_msg_id == details_msg_id) {{
1582         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1583         vl_api_{r}_t_endian(rmp);
1584         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1585     }}
1586   }}
1587   return reply;
1588 }}
1589
1590 '''
1591     gets_details_reply_template = '''\
1592 static cJSON *
1593 api_{n} (cJSON *o)
1594 {{
1595     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1596   int len = 0;
1597   if (!o) return 0;
1598   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1599   if (!mp) {{
1600     fprintf(stderr, "Failed converting JSON to API\\n");
1601     return 0;
1602   }}
1603   mp->_vl_msg_id = msg_id;
1604
1605   vl_api_{n}_t_endian(mp);
1606   vac_write((char *)mp, len);
1607   free(mp);
1608
1609   cJSON *reply = cJSON_CreateArray();
1610
1611   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1612   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1613
1614   while (1) {{
1615     /* Read reply */
1616     char *p;
1617     int l;
1618     vac_read(&p, &l, 5); // XXX: Fix timeout
1619
1620     /* Message can be one of [_details, control_ping_reply
1621      * or unrelated event]
1622      */
1623     u16 msg_id = ntohs(*((u16 *)p));
1624     if (msg_id == reply_msg_id) {{
1625         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1626         vl_api_{r}_t_endian(rmp);
1627         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1628         break;
1629     }}
1630
1631     if (msg_id == details_msg_id) {{
1632         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1633         vl_api_{d}_t_endian(rmp);
1634         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1635     }}
1636   }}
1637   return reply;
1638 }}
1639
1640 '''
1641
1642     if dump:
1643         if s.stream_message:
1644             write(gets_details_reply_template
1645                   .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1646                           R=s.reply.upper(), d=s.stream_message,
1647                           D=s.stream_message.upper()))
1648         else:
1649             write(dump_details_template.format(n=s.caller, r=s.reply,
1650                                                N=s.caller.upper(),
1651                                                R=s.reply.upper()))
1652     else:
1653         write(req_reply_template.format(n=s.caller, r=s.reply,
1654                                         N=s.caller.upper(),
1655                                         R=s.reply.upper()))
1656
1657
1658 def generate_c_test2_boilerplate(services, defines, module, stream):
1659     '''Generate code for VAT2 plugin.'''
1660     write = stream.write
1661
1662     define_hash = {d.name: d for d in defines}
1663     # replies = {}
1664
1665     hdr = '''\
1666 #include <vlibapi/api.h>
1667 #include <vlibmemory/api.h>
1668 #include <vppinfra/error.h>
1669 #include <vnet/ip/ip_format_fns.h>
1670 #include <vnet/ethernet/ethernet_format_fns.h>
1671
1672 #define vl_typedefs             /* define message structures */
1673 #include <vpp/api/vpe_all_api_h.h>
1674 #undef vl_typedefs
1675
1676 #include "{module}.api_enum.h"
1677 #include "{module}.api_types.h"
1678
1679 #define vl_endianfun            /* define message structures */
1680 #include "{module}.api.h"
1681 #undef vl_endianfun
1682
1683 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1684 #define vl_printfun
1685 #include "{module}.api.h"
1686 #undef vl_printfun
1687
1688 #include "{module}.api_tojson.h"
1689 #include "{module}.api_fromjson.h"
1690 #include <vpp-api/client/vppapiclient.h>
1691
1692 #include <vat2/vat2_helpers.h>
1693
1694 '''
1695
1696     write(hdr.format(module=module))
1697
1698     for s in services:
1699         if s.reply not in define_hash:
1700             continue
1701         c_test_api_service(s, s.stream, stream)
1702
1703     write('void vat2_register_function(char *, cJSON * (*)(cJSON *));\n')
1704     # write('__attribute__((constructor))')
1705     write('clib_error_t *\n')
1706     write('vat2_register_plugin (void) {\n')
1707     for s in services:
1708         write('   vat2_register_function("{n}", api_{n});\n'
1709               .format(n=s.caller))
1710     write('   return 0;\n')
1711     write('}\n')
1712
1713
1714 #
1715 # Plugin entry point
1716 #
1717 def run(args, apifilename, s):
1718     '''Main plugin entry point.'''
1719     stream = StringIO()
1720
1721     if not args.outputdir:
1722         sys.stderr.write('Missing --outputdir argument')
1723         return None
1724
1725     basename = os.path.basename(apifilename)
1726     filename, _ = os.path.splitext(basename)
1727     modulename = filename.replace('.', '_')
1728     filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1729     filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1730     filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1731     filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
1732     filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1733                                      '_test2.c'))
1734     filename_c_tojson = (os.path.join(args.outputdir +
1735                                       '/' + basename + '_tojson.h'))
1736     filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1737                                         basename + '_fromjson.h'))
1738
1739     # Generate separate types file
1740     st = StringIO()
1741     generate_include_types(s, modulename, st)
1742     with open(filename_types, 'w') as fd:
1743         st.seek(0)
1744         shutil.copyfileobj(st, fd)
1745     st.close()
1746
1747     # Generate separate enum file
1748     st = StringIO()
1749     st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1750     st.write('#define included_{}_api_enum_h\n'.format(modulename))
1751     generate_include_enum(s, modulename, st)
1752     generate_include_counters(s['Counters'], st)
1753     st.write('#endif\n')
1754     with open(filename_enum, 'w') as fd:
1755         st.seek(0)
1756         shutil.copyfileobj(st, fd)
1757     st.close()
1758
1759     # Generate separate C file
1760     st = StringIO()
1761     generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1762                            s['file_crc'], modulename, st)
1763     with open(filename_c, 'w') as fd:
1764         st.seek(0)
1765         shutil.copyfileobj(st, fd)
1766     st.close()
1767
1768     # Generate separate C test file
1769     st = StringIO()
1770     plugin = bool('plugin' in apifilename)
1771     generate_c_test_boilerplate(s['Service'], s['Define'],
1772                                 s['file_crc'],
1773                                 modulename, plugin, st)
1774     with open(filename_c_test, 'w') as fd:
1775         st.seek(0)
1776         shutil.copyfileobj(st, fd)
1777     st.close()
1778
1779     # Fully autogenerated VATv2 C test file
1780     st = StringIO()
1781     generate_c_test2_boilerplate(s['Service'], s['Define'],
1782                                  modulename, st)
1783     with open(filename_c_test2, 'w') as fd:
1784         st.seek(0)
1785         shutil.copyfileobj(st, fd)
1786     st.close()                  #
1787
1788     # Generate separate JSON file
1789     st = StringIO()
1790     generate_tojson(s, modulename, st)
1791     with open(filename_c_tojson, 'w') as fd:
1792         st.seek(0)
1793         shutil.copyfileobj(st, fd)
1794     st.close()
1795     st = StringIO()
1796     generate_fromjson(s, modulename, st)
1797     with open(filename_c_fromjson, 'w') as fd:
1798         st.seek(0)
1799         shutil.copyfileobj(st, fd)
1800     st.close()
1801
1802     output = TOP_BOILERPLATE.format(datestring=DATESTRING,
1803                                     input_filename=basename)
1804     output += generate_imports(s['Import'])
1805     output += msg_ids(s)
1806     output += msg_names(s)
1807     output += msg_name_crc_list(s, filename)
1808     output += typedefs(modulename)
1809     printfun_types(s['types'], stream, modulename)
1810     printfun(s['Define'], stream, modulename)
1811     output += stream.getvalue()
1812     stream.close()
1813     output += endianfun(s['types'] + s['Define'], modulename)
1814     output += version_tuple(s, basename)
1815     output += BOTTOM_BOILERPLATE.format(input_filename=basename,
1816                                         file_crc=s['file_crc'])
1817
1818     return output