Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-09-0…
Browse files Browse the repository at this point in the history
…3' into staging

QAPI patches patches for 2021-09-03

# gpg: Signature made Fri 03 Sep 2021 16:20:49 BST
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2021-09-03:
  qapi: Tweak error messages for unknown / conflicting 'if' keys
  qapi: Tweak error messages for missing / conflicting meta-type
  tests/qapi-schema: Hide OrderedDict in test output
  qapi: Use re.fullmatch() where appropriate
  qapi: Use "not COND" instead of "!COND" for generated documentation
  qapi: Avoid redundant parens in code generated for conditionals
  qapi: Factor common recursion out of cgen_ifcond(), docgen_ifcond()
  qapi: Fix C code generation for 'if'
  tests/qapi-schema: Demonstrate broken C code for 'if'
  tests/qapi-schema: Correct two 'if' conditionals
  qapi: Simplify how QAPISchemaIfCond represents "no condition"
  qapi: Simplify QAPISchemaIfCond's interface for generating C
  qapi: Set boolean value correctly in examples

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
  • Loading branch information
pm215 committed Sep 4, 2021
2 parents 9c03aa8 + 34f7b25 commit 31ebff5
Show file tree
Hide file tree
Showing 19 changed files with 120 additions and 113 deletions.
2 changes: 1 addition & 1 deletion qapi/trace.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
# Example:
#
# -> { "execute": "trace-event-set-state",
# "arguments": { "name": "qemu_memalign", "enable": "true" } }
# "arguments": { "name": "qemu_memalign", "enable": true } }
# <- { "return": {} }
#
##
Expand Down
49 changes: 28 additions & 21 deletions scripts/qapi/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Dict,
Match,
Optional,
Sequence,
Union,
)

Expand Down Expand Up @@ -200,33 +201,39 @@ def guardend(name: str) -> str:
name=c_fname(name).upper())


def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:
def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]],
cond_fmt: str, not_fmt: str,
all_operator: str, any_operator: str) -> str:

def do_gen(ifcond: Union[str, Dict[str, Any]], need_parens: bool):
if isinstance(ifcond, str):
return cond_fmt % ifcond
assert isinstance(ifcond, dict) and len(ifcond) == 1
if 'not' in ifcond:
return not_fmt % do_gen(ifcond['not'], True)
if 'all' in ifcond:
gen = gen_infix(all_operator, ifcond['all'])
else:
gen = gen_infix(any_operator, ifcond['any'])
if need_parens:
gen = '(' + gen + ')'
return gen

def gen_infix(operator: str, operands: Sequence[Any]) -> str:
return operator.join([do_gen(o, True) for o in operands])

if not ifcond:
return ''
if isinstance(ifcond, str):
return 'defined(' + ifcond + ')'
return do_gen(ifcond, False)

oper, operands = next(iter(ifcond.items()))
if oper == 'not':
return '!' + cgen_ifcond(operands)
oper = {'all': '&&', 'any': '||'}[oper]
operands = [cgen_ifcond(o) for o in operands]
return '(' + (') ' + oper + ' (').join(operands) + ')'

def cgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
return gen_ifcond(ifcond, 'defined(%s)', '!%s', ' && ', ' || ')

def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:

def docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
# TODO Doc generated for conditions needs polish
if not ifcond:
return ''
if isinstance(ifcond, str):
return ifcond

oper, operands = next(iter(ifcond.items()))
if oper == 'not':
return '!' + docgen_ifcond(operands)
oper = {'all': ' and ', 'any': ' or '}[oper]
operands = [docgen_ifcond(o) for o in operands]
return '(' + oper.join(operands) + ')'
return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ')


def gen_if(cond: str) -> str:
Expand Down
32 changes: 13 additions & 19 deletions scripts/qapi/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None:

def _check_if(cond: Union[str, object]) -> None:
if isinstance(cond, str):
if not re.match(r'^[A-Z][A-Z0-9_]*$', cond):
if not re.fullmatch(r'[A-Z][A-Z0-9_]*', cond):
raise QAPISemError(
info,
"'if' condition '%s' of %s is not a valid identifier"
Expand All @@ -286,13 +286,12 @@ def _check_if(cond: Union[str, object]) -> None:
raise QAPISemError(
info,
"'if' condition of %s must be a string or an object" % source)
check_keys(cond, info, "'if' condition of %s" % source, [],
["all", "any", "not"])
if len(cond) != 1:
raise QAPISemError(
info,
"'if' condition dict of %s must have one key: "
"'all', 'any' or 'not'" % source)
check_keys(cond, info, "'if' condition", [],
["all", "any", "not"])
"'if' condition of %s has conflicting keys" % source)

oper, operands = next(iter(cond.items()))
if not operands:
Expand Down Expand Up @@ -630,20 +629,15 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
if 'include' in expr:
continue

if 'enum' in expr:
meta = 'enum'
elif 'union' in expr:
meta = 'union'
elif 'alternate' in expr:
meta = 'alternate'
elif 'struct' in expr:
meta = 'struct'
elif 'command' in expr:
meta = 'command'
elif 'event' in expr:
meta = 'event'
else:
raise QAPISemError(info, "expression is missing metatype")
metas = expr.keys() & {'enum', 'struct', 'union', 'alternate',
'command', 'event'}
if len(metas) != 1:
raise QAPISemError(
info,
"expression must have exactly one key"
" 'enum', 'struct', 'union', 'alternate',"
" 'command', 'event'")
meta = metas.pop()

check_name_is_str(expr[meta], info, "'%s'" % meta)
name = cast(str, expr[meta])
Expand Down
6 changes: 2 additions & 4 deletions scripts/qapi/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
from .common import (
c_fname,
c_name,
gen_endif,
gen_if,
guardend,
guardstart,
mcgen,
Expand Down Expand Up @@ -95,9 +93,9 @@ def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str:
if added[0] == '\n':
out += '\n'
added = added[1:]
out += gen_if(ifcond.cgen())
out += ifcond.gen_if()
out += added
out += gen_endif(ifcond.cgen())
out += ifcond.gen_endif()
return out


Expand Down
11 changes: 3 additions & 8 deletions scripts/qapi/introspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@
Union,
)

from .common import (
c_name,
gen_endif,
gen_if,
mcgen,
)
from .common import c_name, mcgen
from .gen import QAPISchemaMonolithicCVisitor
from .schema import (
QAPISchema,
Expand Down Expand Up @@ -124,10 +119,10 @@ def indent(level: int) -> str:
if obj.comment:
ret += indent(level) + f"/* {obj.comment} */\n"
if obj.ifcond.is_present():
ret += gen_if(obj.ifcond.cgen())
ret += obj.ifcond.gen_if()
ret += _tree_to_qlit(obj.value, level)
if obj.ifcond.is_present():
ret += '\n' + gen_endif(obj.ifcond.cgen())
ret += '\n' + obj.ifcond.gen_endif()
return ret

ret = ''
Expand Down
12 changes: 10 additions & 2 deletions scripts/qapi/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
c_name,
cgen_ifcond,
docgen_ifcond,
gen_endif,
gen_if,
)
from .error import QAPIError, QAPISemError, QAPISourceError
from .expr import check_exprs
Expand All @@ -32,11 +34,17 @@

class QAPISchemaIfCond:
def __init__(self, ifcond=None):
self.ifcond = ifcond or {}
self.ifcond = ifcond

def cgen(self):
def _cgen(self):
return cgen_ifcond(self.ifcond)

def gen_if(self):
return gen_if(self._cgen())

def gen_endif(self):
return gen_endif(self._cgen())

def docgen(self):
return docgen_ifcond(self.ifcond)

Expand Down
28 changes: 11 additions & 17 deletions scripts/qapi/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@

from typing import List, Optional

from .common import (
c_enum_const,
c_name,
gen_endif,
gen_if,
mcgen,
)
from .common import c_enum_const, c_name, mcgen
from .gen import QAPISchemaModularCVisitor, ifcontext
from .schema import (
QAPISchema,
Expand Down Expand Up @@ -51,13 +45,13 @@ def gen_enum_lookup(name: str,
''',
c_name=c_name(name))
for memb in members:
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
index = c_enum_const(name, memb.name, prefix)
ret += mcgen('''
[%(index)s] = "%(name)s",
''',
index=index, name=memb.name)
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()

ret += mcgen('''
},
Expand All @@ -81,12 +75,12 @@ def gen_enum(name: str,
c_name=c_name(name))

for memb in enum_members:
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
ret += mcgen('''
%(c_enum)s,
''',
c_enum=c_enum_const(name, memb.name, prefix))
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()

ret += mcgen('''
} %(c_name)s;
Expand Down Expand Up @@ -126,7 +120,7 @@ def gen_array(name: str, element_type: QAPISchemaType) -> str:
def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
ret = ''
for memb in members:
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
if memb.optional:
ret += mcgen('''
bool has_%(c_name)s;
Expand All @@ -136,7 +130,7 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
%(c_type)s %(c_name)s;
''',
c_type=memb.type.c_type(), c_name=c_name(memb.name))
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()
return ret


Expand All @@ -159,7 +153,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond,
ret += mcgen('''
''')
ret += gen_if(ifcond.cgen())
ret += ifcond.gen_if()
ret += mcgen('''
struct %(c_name)s {
''',
Expand Down Expand Up @@ -193,7 +187,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond,
ret += mcgen('''
};
''')
ret += gen_endif(ifcond.cgen())
ret += ifcond.gen_endif()

return ret

Expand All @@ -220,13 +214,13 @@ def gen_variants(variants: QAPISchemaVariants) -> str:
for var in variants.variants:
if var.type.name == 'q_empty':
continue
ret += gen_if(var.ifcond.cgen())
ret += var.ifcond.gen_if()
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
c_type=var.type.c_unboxed_type(),
c_name=c_name(var.name))
ret += gen_endif(var.ifcond.cgen())
ret += var.ifcond.gen_endif()

ret += mcgen('''
} u;
Expand Down
14 changes: 6 additions & 8 deletions scripts/qapi/visit.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
from .common import (
c_enum_const,
c_name,
gen_endif,
gen_if,
indent,
mcgen,
)
Expand Down Expand Up @@ -79,7 +77,7 @@ def gen_visit_object_members(name: str,

for memb in members:
deprecated = 'deprecated' in [f.name for f in memb.features]
ret += gen_if(memb.ifcond.cgen())
ret += memb.ifcond.gen_if()
if memb.optional:
ret += mcgen('''
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
Expand Down Expand Up @@ -112,7 +110,7 @@ def gen_visit_object_members(name: str,
ret += mcgen('''
}
''')
ret += gen_endif(memb.ifcond.cgen())
ret += memb.ifcond.gen_endif()

if variants:
tag_member = variants.tag_member
Expand All @@ -126,7 +124,7 @@ def gen_visit_object_members(name: str,
for var in variants.variants:
case_str = c_enum_const(tag_member.type.name, var.name,
tag_member.type.prefix)
ret += gen_if(var.ifcond.cgen())
ret += var.ifcond.gen_if()
if var.type.name == 'q_empty':
# valid variant and nothing to do
ret += mcgen('''
Expand All @@ -142,7 +140,7 @@ def gen_visit_object_members(name: str,
case=case_str,
c_type=var.type.c_name(), c_name=c_name(var.name))

ret += gen_endif(var.ifcond.cgen())
ret += var.ifcond.gen_endif()
ret += mcgen('''
default:
abort();
Expand Down Expand Up @@ -228,7 +226,7 @@ def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
c_name=c_name(name))

for var in variants.variants:
ret += gen_if(var.ifcond.cgen())
ret += var.ifcond.gen_if()
ret += mcgen('''
case %(case)s:
''',
Expand All @@ -254,7 +252,7 @@ def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
ret += mcgen('''
break;
''')
ret += gen_endif(var.ifcond.cgen())
ret += var.ifcond.gen_endif()

ret += mcgen('''
case QTYPE_NONE:
Expand Down
2 changes: 1 addition & 1 deletion tests/qapi-schema/bad-if-key.err
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
bad-if-key.json: In struct 'TestIfStruct':
bad-if-key.json:2: 'if' condition has unknown key 'value'
bad-if-key.json:2: 'if' condition of struct has unknown key 'value'
Valid keys are 'all', 'any', 'not'.
2 changes: 1 addition & 1 deletion tests/qapi-schema/bad-if-keys.err
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
bad-if-keys.json: In struct 'TestIfStruct':
bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all', 'any' or 'not'
bad-if-keys.json:2: 'if' condition of struct has conflicting keys
2 changes: 1 addition & 1 deletion tests/qapi-schema/doc-good.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
{ 'alternate': 'Alternate',
'features': [ 'alt-feat' ],
'data': { 'i': 'int', 'b': 'bool' },
'if': { 'not': 'IFNOT' } }
'if': { 'not': { 'any': [ 'IFONE', 'IFTWO' ] } } }

##
# == Another subsection
Expand Down
Loading

0 comments on commit 31ebff5

Please sign in to comment.