[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v5 20/28] qapi: More rigourous checking of types
From: |
Markus Armbruster |
Subject: |
Re: [Qemu-devel] [PATCH v5 20/28] qapi: More rigourous checking of types |
Date: |
Fri, 27 Mar 2015 09:23:26 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) |
Eric Blake <address@hidden> writes:
> Now that we know every expression is valid with regards to
> its keys, we can add further tests that those keys refer to
> valid types. With this patch, all uses of a type (the 'data':
> of command, type, union, alternate, and event; the 'returns':
> of command; the 'base': of type and union) must resolve to an
> appropriate subset of metatypes declared by the current qapi
> parse; this includes recursing into each member of a data
> dictionary. Dealing with '**' and nested anonymous structs
> will be done in later patches.
>
> Update the testsuite to match improved output.
>
> Signed-off-by: Eric Blake <address@hidden>
> ---
[...]
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 6ed6a34..c42683b 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -276,6 +276,63 @@ def discriminator_find_enum_define(expr):
>
> return find_enum(discriminator_type)
>
> +def check_type(expr_info, source, data, allow_array = False,
> + allowed_metas = [], allow_dict = True):
I'd allow_dict = False here, because I like defaulting to false. Matter
of taste.
I'd name the third parameter def rather than data. Matter of taste
again.
> + global all_names
> +
> + if data is None:
> + return
> +
> + if data == '**':
> + return
> +
> + # Check if array type for data is okay
> + if isinstance(data, list):
> + if not allow_array:
> + raise QAPIExprError(expr_info,
> + "%s cannot be an array" % source)
> + if len(data) != 1 or not isinstance(data[0], str):
> + raise QAPIExprError(expr_info,
> + "%s: array type must contain single type
> name"
> + % source)
> + data = data[0]
> +
> + # Check if type name for data is okay
> + if isinstance(data, str):
> + if not data in all_names:
> + raise QAPIExprError(expr_info,
> + "%s uses unknown type '%s'"
> + % (source, data))
> + if not all_names[data] in allowed_metas:
> + raise QAPIExprError(expr_info,
> + "%s cannot use %s type '%s'"
> + % (source, all_names[data], data))
> + return
> +
> + # data is a dictionary, check that each member is okay
> + if not isinstance(data, OrderedDict):
> + raise QAPIExprError(expr_info,
> + "%s should be a dictionary" % source)
> + if not allow_dict:
> + raise QAPIExprError(expr_info,
> + "%s should be a type name" % source)
> + for (key, value) in data.items():
> + check_type(expr_info, "Member '%s' of %s" % (key, source), value,
This can produce messages like
Member 'inner' of Member 'outer' of ...
I figure the problem will go away when you ditch nested structs. Not
worth worrying about it then.
> + allow_array=True,
> + allowed_metas=['built-in', 'union', 'alternate', 'struct',
> + 'enum'],
> + allow_dict=True)
> +
> +def check_command(expr, expr_info):
> + name = expr['command']
> + check_type(expr_info, "'data' for command '%s'" % name,
> + expr.get('data'),
> + allowed_metas=['union', 'struct'])
> + check_type(expr_info, "'returns' for command '%s'" % name,
> + expr.get('returns'), allow_array=True,
> + allowed_metas=['built-in', 'union', 'alternate', 'struct',
> + 'enum'])
> +
> def check_event(expr, expr_info):
> global events
> name = expr['event']
> @@ -287,7 +344,8 @@ def check_event(expr, expr_info):
> raise QAPIExprError(expr_info, "Event name '%s' should be upper case"
> % name)
> events.append(name)
> -
> + check_type(expr_info, "'data' for event '%s'" % name,
> + expr.get('data'), allowed_metas=['union', 'struct'])
> if params:
> for argname, argentry, optional, structured in parse_args(params):
> if structured:
> @@ -348,6 +406,13 @@ def check_union(expr, expr_info):
>
> # Check every branch
> for (key, value) in members.items():
> + # Each value must name a known type
> + check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
> + value, allow_array=True,
> + allowed_metas=['built-in', 'union', 'alternate', 'struct',
> + 'enum'],
> + allow_dict=False)
> +
> # If the discriminator names an enum type, then all members
> # of 'data' must also be members of the enum type.
> if enum_define:
> @@ -383,15 +448,12 @@ def check_alternate(expr, expr_info):
> values[c_key] = key
>
> # Ensure alternates have no type conflicts.
> - if isinstance(value, list):
> - raise QAPIExprError(expr_info,
> - "Alternate '%s' member '%s' must "
> - "not be array type" % (name, key))
> + check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
> + value,
> + allowed_metas=['built-in', 'union', 'struct', 'enum'],
> + allow_dict=False)
> qtype = find_alternate_member_qtype(value)
> - if not qtype:
> - raise QAPIExprError(expr_info,
> - "Alternate '%s' member '%s' has "
> - "invalid type '%s'" % (name, key, value))
> + assert qtype
> if qtype in types_seen:
> raise QAPIExprError(expr_info,
> "Alternate '%s' member '%s' has "
> @@ -419,6 +481,14 @@ def check_enum(expr, expr_info):
> % (name, member, values[key]))
> values[key] = member
>
> +def check_struct(expr, expr_info):
> + name = expr['type']
> + members = expr['data']
> +
> + check_type(expr_info, "'data' for type '%s'" % name, members)
This one gave me pause, until I realized that allowed_metas=[],
allow_dict=True accepts exactly dictionary members, which is what we
want.
> + check_type(expr_info, "'base' for type '%s'" % name, expr.get('base'),
> + allowed_metas=['struct'], allow_dict=False)
> +
> def check_exprs(schema):
> for expr_elem in schema.exprs:
> expr = expr_elem['expr']
> @@ -430,8 +500,14 @@ def check_exprs(schema):
> check_union(expr, info)
> elif expr.has_key('alternate'):
> check_alternate(expr, info)
> + elif expr.has_key('type'):
> + check_struct(expr, info)
> + elif expr.has_key('command'):
> + check_command(expr, info)
> elif expr.has_key('event'):
> check_event(expr, info)
> + else:
> + assert False, 'unexpected meta type'
>
> def check_keys(expr_elem, meta, required, optional=[]):
> expr = expr_elem['expr']
[...]
Reviewed-by: Markus Armbruster <address@hidden>
Re: [Qemu-devel] [PATCH v5 21/28] qapi: Require valid names, Markus Armbruster, 2015/03/27
Re: [Qemu-devel] [PATCH v5 21/28] qapi: Require valid names, Markus Armbruster, 2015/03/29
[Qemu-devel] [PATCH v5 27/28] qapi: Drop inline nested types in query-pci, Eric Blake, 2015/03/24