From 9ac113815511f3ce37b56a1331d6491fc36f7db5 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Tue, 23 Apr 2019 17:11:01 +0200 Subject: [PATCH 1/1] API: Add support for limits to language. string name [limit = 64]; Meta-data to do argument validation. Change-Id: I1f3e0f09b2d5285224399413d25206f77bd3f4b1 Signed-off-by: Ole Troan --- src/tools/vppapigen/vppapigen.py | 16 ++++++++---- src/tools/vppapigen/vppapigen_json.py | 5 +++- .../python/vpp_papi/tests/test_vpp_serializer.py | 17 +++++++++++- src/vpp-api/python/vpp_papi/vpp_serializer.py | 30 +++++++++++++++++----- src/vpp-api/vapi/vapi_json_parser.py | 9 ++++--- src/vpp/api/vpe.api | 8 +++--- 6 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py index c2f221b9e79..ae2b0b1ba40 100755 --- a/src/tools/vppapigen/vppapigen.py +++ b/src/tools/vppapigen/vppapigen.py @@ -298,10 +298,11 @@ class Array(): class Field(): - def __init__(self, fieldtype, name): + def __init__(self, fieldtype, name, limit=None): self.type = 'Field' self.fieldtype = fieldtype self.fieldname = name + self.limit = limit def __repr__(self): return str([self.fieldtype, self.fieldname]) @@ -504,7 +505,7 @@ class VPPAPIParser(object): def p_enum_statements(self, p): '''enum_statements : enum_statement - | enum_statements enum_statement''' + | enum_statements enum_statement''' if len(p) == 2: p[0] = [p[1]] else: @@ -519,11 +520,16 @@ class VPPAPIParser(object): p[0] = p[1] def p_declaration(self, p): - '''declaration : type_specifier ID ';' ''' - if len(p) != 4: + '''declaration : type_specifier ID ';' + | type_specifier ID '[' ID '=' assignee ']' ';' ''' + if len(p) == 9: + p[0] = Field(p[1], p[2], {p[4]: p[6]}) + elif len(p) == 4: + p[0] = Field(p[1], p[2]) + else: self._parse_error('ERROR') self.fields.append(p[2]) - p[0] = Field(p[1], p[2]) + def p_declaration_array(self, p): '''declaration : type_specifier ID '[' NUM ']' ';' diff --git a/src/tools/vppapigen/vppapigen_json.py b/src/tools/vppapigen/vppapigen_json.py index c563a089949..94a9e19577e 100644 --- a/src/tools/vppapigen/vppapigen_json.py +++ b/src/tools/vppapigen/vppapigen_json.py @@ -33,7 +33,10 @@ def walk_defs(s): d.append(t.name) for b in t.block: if b.type == 'Field': - d.append([b.fieldtype, b.fieldname]) + if b.limit: + d.append([b.fieldtype, b.fieldname, b.limit]) + else: + d.append([b.fieldtype, b.fieldname]) elif b.type == 'Array': if b.lengthfield: d.append([b.fieldtype, b.fieldname, b.length, b.lengthfield]) diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py index 4fbda2a01af..ec7334712ab 100755 --- a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py @@ -3,12 +3,27 @@ import unittest from vpp_papi.vpp_serializer import VPPType, VPPEnumType from vpp_papi.vpp_serializer import VPPUnionType, VPPMessage -from vpp_papi.vpp_serializer import VPPTypeAlias +from vpp_papi.vpp_serializer import VPPTypeAlias, VPPSerializerValueError from socket import inet_pton, AF_INET, AF_INET6 import logging import sys from ipaddress import * +class TestLimits(unittest.TestCase): + def test_limit(self): + limited_type = VPPType('limited_type_t', + [['string', 'name', {'limit': 16}]]) + unlimited_type = VPPType('limited_type_t', + [['string', 'name']]) + + + b = limited_type.pack({'name':'foobar'}) + self.assertEqual(len(b), 10) + b = unlimited_type.pack({'name':'foobar'}) + self.assertEqual(len(b), 10) + + with self.assertRaises(VPPSerializerValueError): + b = limited_type.pack({'name':'foobar'*3}) class TestAddType(unittest.TestCase): diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py index bd5f050bba9..d1093318ed0 100644 --- a/src/vpp-api/python/vpp_papi/vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py @@ -96,14 +96,20 @@ class BaseTypes(object): class String(object): - def __init__(self): + def __init__(self, options): self.name = 'string' self.size = 1 self.length_field_packer = BaseTypes('u32') + self.limit = options['limit'] if 'limit' in options else None def pack(self, list, kwargs=None): if not list: return self.length_field_packer.pack(0) + b"" + if self.limit and len(list) > self.limit: + raise VPPSerializerValueError( + "Invalid argument length for: {}, {} maximum {}". + format(list, len(list), self.limit)) + return self.length_field_packer.pack(len(list)) + list.encode('utf8') def unpack(self, data, offset=0, result=None, ntc=False): @@ -120,7 +126,7 @@ class String(object): types = {'u8': BaseTypes('u8'), 'u16': BaseTypes('u16'), 'u32': BaseTypes('u32'), 'i32': BaseTypes('i32'), 'u64': BaseTypes('u64'), 'f64': BaseTypes('f64'), - 'bool': BaseTypes('bool'), 'string': String()} + 'bool': BaseTypes('bool'), 'string': String} def vpp_get_type(name): @@ -416,7 +422,15 @@ class VPPType(object): logger.debug('Unknown type {}'.format(f_type)) raise VPPSerializerValueError( 'Unknown message type {}'.format(f_type)) - if len(f) == 3: # list + + l = len(f) + options = [x for x in f if type(x) is dict] + if len(options): + self.options = options[0] + l -= 1 + else: + self.options = {} + if l == 3: # list list_elements = f[2] if list_elements == 0: p = VLAList_legacy(f_name, f_type) @@ -429,13 +443,17 @@ class VPPType(object): p = FixedList(f_name, f_type, list_elements) self.packers.append(p) size += p.size - elif len(f) == 4: # Variable length list + elif l == 4: # Variable length list length_index = self.fields.index(f[3]) p = VLAList(f_name, f_type, f[3], length_index) self.packers.append(p) else: - self.packers.append(types[f_type]) - size += types[f_type].size + if f_type == 'string': + p = types[f_type](self.options) + else: + p = types[f_type] + self.packers.append(p) + size += p.size self.size = size self.tuple = collections.namedtuple(name, self.fields, rename=True) diff --git a/src/vpp-api/vapi/vapi_json_parser.py b/src/vpp-api/vapi/vapi_json_parser.py index fda3f75d9c1..fbeb1887ac2 100644 --- a/src/vpp-api/vapi/vapi_json_parser.py +++ b/src/vpp-api/vapi/vapi_json_parser.py @@ -167,13 +167,16 @@ class Message(object): else: field_type = json_parser.lookup_type_like_id(field[0]) logger.debug("Parsing message field `%s'" % field) - if len(field) == 2: + l = len(field) + if any(type(n) is dict for n in field): + l -= 1 + if l == 2: if self.header is not None and\ self.header.has_field(field[1]): continue p = field_class(field_name=field[1], field_type=field_type) - elif len(field) == 3: + elif l == 3: if field[2] == 0: raise ParseError( "While parsing message `%s': variable length " @@ -184,7 +187,7 @@ class Message(object): field_name=field[1], field_type=field_type, array_len=field[2]) - elif len(field) == 4: + elif l == 4: nelem_field = None for f in fields: if f.name == field[3]: diff --git a/src/vpp/api/vpe.api b/src/vpp/api/vpe.api index a2a677926e1..94732bbc236 100644 --- a/src/vpp/api/vpe.api +++ b/src/vpp/api/vpe.api @@ -183,10 +183,10 @@ define show_version_reply { u32 context; i32 retval; - string program; - string version; - string build_date; - string build_directory; + string program [limit = 32]; + string version [limit = 32]; + string build_date [limit = 32]; + string build_directory [limit = 256]; }; -- 2.16.6