diff options
author | Ole Troan <ot@cisco.com> | 2019-08-23 22:55:18 +0200 |
---|---|---|
committer | Andrew Yourtchenko <ayourtch@gmail.com> | 2019-09-03 20:04:13 +0000 |
commit | e5ff5a36dd126ee57dca4e0b03da2f7704e0a4f5 (patch) | |
tree | 2108baa60e8a697624288fe2c3050be29697bc88 /src/tools | |
parent | b6fde4a8bae474c6b73d08d223028f42e396d452 (diff) |
api: enforce vla is last and fixed string type
Enforce that variable length fields are the last element of API messages.
Add a 'fixed' version of string type, since dealing with
multiple variable length strings turned out too painful
for the C language bindings.
The string type is now:
{
string name[64]; // NUL terminated C-string. Essentially decays to u8 name[64]
string name[]; // Variable length string with embedded len field (vl_api_string_t)
};
The latter notation could be made available to other types as well.
e.g.
{
vl_api_address_t addresses[];
}
instead of
{
u32 n_addr;
vl_api_address_t addresses[n_addr];
};
Type: fix
Change-Id: I18fa17ef47227633752ab50453e8d20a652a9f9b
Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src/tools')
-rwxr-xr-x | src/tools/vppapigen/vppapigen.py | 74 | ||||
-rw-r--r-- | src/tools/vppapigen/vppapigen_c.py | 11 |
2 files changed, 74 insertions, 11 deletions
diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py index c01fa40c169..d0391cd7a37 100755 --- a/src/tools/vppapigen/vppapigen.py +++ b/src/tools/vppapigen/vppapigen.py @@ -152,6 +152,25 @@ class Typedef(): self.manual_endian = True global_type_add(name, self) + self.vla = False + + for i, b in enumerate(block): + if isinstance(b, Array): + if b.length == 0: + self.vla = True + if i + 1 < len(block): + raise ValueError( + 'VLA field "{}" must be the last ' + 'field in message "{}"' + .format(b.fieldname, name)) + elif b.fieldtype == 'string': + self.vla = True + if i + 1 < len(block): + raise ValueError( + 'VLA field "{}" must be the last ' + 'field in message "{}"' + .format(b.fieldname, name)) + def __repr__(self): return self.name + str(self.flags) + str(self.block) @@ -159,12 +178,13 @@ class Typedef(): class Using(): def __init__(self, name, alias): self.name = name + self.vla = False if isinstance(alias, Array): - a = { 'type': alias.fieldtype, # noqa: E201 - 'length': alias.length } # noqa: E202 + a = {'type': alias.fieldtype, + 'length': alias.length} else: - a = { 'type': alias.fieldtype } # noqa: E201,E202 + a = {'type': alias.fieldtype} self.alias = a self.crc = str(alias).encode() global_type_add(name, self) @@ -208,12 +228,29 @@ class Define(): elif f == 'autoreply': self.autoreply = True - for b in block: + for i, b in enumerate(block): if isinstance(b, Option): if b[1] == 'singular' and b[2] == 'true': self.singular = True block.remove(b) + if isinstance(b, Array) and b.vla and i + 1 < len(block): + raise ValueError( + 'VLA field "{}" must be the last field in message "{}"' + .format(b.fieldname, name)) + elif b.fieldtype.startswith('vl_api_'): + if (global_types[b.fieldtype].vla and i + 1 < len(block)): + raise ValueError( + 'VLA field "{}" must be the last ' + 'field in message "{}"' + .format(b.fieldname, name)) + elif b.fieldtype == 'string' and b.length == 0: + if i + 1 < len(block): + raise ValueError( + 'VLA field "{}" must be the last ' + 'field in message "{}"' + .format(b.fieldname, name)) + def __repr__(self): return self.name + str(self.flags) + str(self.block) @@ -222,6 +259,7 @@ class Enum(): def __init__(self, name, block, enumtype='u32'): self.name = name self.enumtype = enumtype + self.vla = False count = 0 for i, b in enumerate(block): @@ -272,16 +310,19 @@ class Option(): class Array(): - def __init__(self, fieldtype, name, length): + def __init__(self, fieldtype, name, length, modern_vla=False): self.type = 'Array' self.fieldtype = fieldtype self.fieldname = name + self.modern_vla = modern_vla if type(length) is str: self.lengthfield = length self.length = 0 + self.vla = True else: self.length = length self.lengthfield = None + self.vla = False def __repr__(self): return str([self.fieldtype, self.fieldname, self.length, @@ -292,6 +333,11 @@ class Field(): def __init__(self, fieldtype, name, limit=None): self.type = 'Field' self.fieldtype = fieldtype + + if self.fieldtype == 'string': + raise ValueError("The string type {!r} is an " + "array type ".format(name)) + if name in keyword.kwlist: raise ValueError("Fieldname {!r} is a python keyword and is not " "accessible via the python API. ".format(name)) @@ -521,13 +567,18 @@ class VPPAPIParser(object): if len(p) == 2: p[0] = p[1] else: - p[0] = { **p[1], **p[2] } + p[0] = {**p[1], **p[2]} def p_field_option(self, p): - '''field_option : ID '=' assignee ',' + '''field_option : ID + | ID '=' assignee ',' | ID '=' assignee + ''' - p[0] = { p[1]: p[3] } + if len(p) == 2: + p[0] = {p[1]: None} + else: + p[0] = {p[1]: p[3]} def p_declaration(self, p): '''declaration : type_specifier ID ';' @@ -540,9 +591,14 @@ class VPPAPIParser(object): self._parse_error('ERROR') self.fields.append(p[2]) + def p_declaration_array_vla(self, p): + '''declaration : type_specifier ID '[' ']' ';' ''' + p[0] = Array(p[1], p[2], 0, modern_vla=True) + def p_declaration_array(self, p): '''declaration : type_specifier ID '[' NUM ']' ';' | type_specifier ID '[' ID ']' ';' ''' + if len(p) != 7: return self._parse_error( 'array: %s' % p.value, @@ -780,7 +836,7 @@ def foldup_blocks(block, crc): try: crc = crc_block_combine(t.block, crc) return foldup_blocks(t.block, crc) - except: + except AttributeError: pass return crc diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py index 19efc99dae0..c1bc11d4a12 100644 --- a/src/tools/vppapigen/vppapigen_c.py +++ b/src/tools/vppapigen/vppapigen_c.py @@ -153,8 +153,15 @@ def typedefs(objs, aliases, filename): if b.lengthfield: output += " %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname) else: - output += " %s %s[%s];\n" % (api2c(b.fieldtype), b.fieldname, - b.length) + # Fixed length strings decay to nul terminated u8 + if b.fieldtype == 'string': + if b.modern_vla: + output += ' {} {};\n'.format(api2c(b.fieldtype), b.fieldname) + else: + output += ' u8 {}[{}];\n'.format(b.fieldname, b.length) + else: + output += " %s %s[%s];\n" % (api2c(b.fieldtype), b.fieldname, + b.length) else: raise ValueError("Error in processing array type %s" % b) |