Skip to content

Commit 31ebff5

Browse files
committed
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-09-03' 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 "[email protected]" # gpg: Good signature from "Markus Armbruster <[email protected]>" [full] # gpg: aka "Markus Armbruster <[email protected]>" [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 <[email protected]>
2 parents 9c03aa8 + 34f7b25 commit 31ebff5

19 files changed

+120
-113
lines changed

qapi/trace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
# Example:
100100
#
101101
# -> { "execute": "trace-event-set-state",
102-
# "arguments": { "name": "qemu_memalign", "enable": "true" } }
102+
# "arguments": { "name": "qemu_memalign", "enable": true } }
103103
# <- { "return": {} }
104104
#
105105
##

scripts/qapi/common.py

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
Dict,
1818
Match,
1919
Optional,
20+
Sequence,
2021
Union,
2122
)
2223

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

202203

203-
def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:
204+
def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]],
205+
cond_fmt: str, not_fmt: str,
206+
all_operator: str, any_operator: str) -> str:
207+
208+
def do_gen(ifcond: Union[str, Dict[str, Any]], need_parens: bool):
209+
if isinstance(ifcond, str):
210+
return cond_fmt % ifcond
211+
assert isinstance(ifcond, dict) and len(ifcond) == 1
212+
if 'not' in ifcond:
213+
return not_fmt % do_gen(ifcond['not'], True)
214+
if 'all' in ifcond:
215+
gen = gen_infix(all_operator, ifcond['all'])
216+
else:
217+
gen = gen_infix(any_operator, ifcond['any'])
218+
if need_parens:
219+
gen = '(' + gen + ')'
220+
return gen
221+
222+
def gen_infix(operator: str, operands: Sequence[Any]) -> str:
223+
return operator.join([do_gen(o, True) for o in operands])
224+
204225
if not ifcond:
205226
return ''
206-
if isinstance(ifcond, str):
207-
return 'defined(' + ifcond + ')'
227+
return do_gen(ifcond, False)
208228

209-
oper, operands = next(iter(ifcond.items()))
210-
if oper == 'not':
211-
return '!' + cgen_ifcond(operands)
212-
oper = {'all': '&&', 'any': '||'}[oper]
213-
operands = [cgen_ifcond(o) for o in operands]
214-
return '(' + (') ' + oper + ' (').join(operands) + ')'
215229

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

217-
def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:
233+
234+
def docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
218235
# TODO Doc generated for conditions needs polish
219-
if not ifcond:
220-
return ''
221-
if isinstance(ifcond, str):
222-
return ifcond
223-
224-
oper, operands = next(iter(ifcond.items()))
225-
if oper == 'not':
226-
return '!' + docgen_ifcond(operands)
227-
oper = {'all': ' and ', 'any': ' or '}[oper]
228-
operands = [docgen_ifcond(o) for o in operands]
229-
return '(' + oper.join(operands) + ')'
236+
return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ')
230237

231238

232239
def gen_if(cond: str) -> str:

scripts/qapi/expr.py

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None:
275275

276276
def _check_if(cond: Union[str, object]) -> None:
277277
if isinstance(cond, str):
278-
if not re.match(r'^[A-Z][A-Z0-9_]*$', cond):
278+
if not re.fullmatch(r'[A-Z][A-Z0-9_]*', cond):
279279
raise QAPISemError(
280280
info,
281281
"'if' condition '%s' of %s is not a valid identifier"
@@ -286,13 +286,12 @@ def _check_if(cond: Union[str, object]) -> None:
286286
raise QAPISemError(
287287
info,
288288
"'if' condition of %s must be a string or an object" % source)
289+
check_keys(cond, info, "'if' condition of %s" % source, [],
290+
["all", "any", "not"])
289291
if len(cond) != 1:
290292
raise QAPISemError(
291293
info,
292-
"'if' condition dict of %s must have one key: "
293-
"'all', 'any' or 'not'" % source)
294-
check_keys(cond, info, "'if' condition", [],
295-
["all", "any", "not"])
294+
"'if' condition of %s has conflicting keys" % source)
296295

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

633-
if 'enum' in expr:
634-
meta = 'enum'
635-
elif 'union' in expr:
636-
meta = 'union'
637-
elif 'alternate' in expr:
638-
meta = 'alternate'
639-
elif 'struct' in expr:
640-
meta = 'struct'
641-
elif 'command' in expr:
642-
meta = 'command'
643-
elif 'event' in expr:
644-
meta = 'event'
645-
else:
646-
raise QAPISemError(info, "expression is missing metatype")
632+
metas = expr.keys() & {'enum', 'struct', 'union', 'alternate',
633+
'command', 'event'}
634+
if len(metas) != 1:
635+
raise QAPISemError(
636+
info,
637+
"expression must have exactly one key"
638+
" 'enum', 'struct', 'union', 'alternate',"
639+
" 'command', 'event'")
640+
meta = metas.pop()
647641

648642
check_name_is_str(expr[meta], info, "'%s'" % meta)
649643
name = cast(str, expr[meta])

scripts/qapi/gen.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
from .common import (
2525
c_fname,
2626
c_name,
27-
gen_endif,
28-
gen_if,
2927
guardend,
3028
guardstart,
3129
mcgen,
@@ -95,9 +93,9 @@ def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str:
9593
if added[0] == '\n':
9694
out += '\n'
9795
added = added[1:]
98-
out += gen_if(ifcond.cgen())
96+
out += ifcond.gen_if()
9997
out += added
100-
out += gen_endif(ifcond.cgen())
98+
out += ifcond.gen_endif()
10199
return out
102100

103101

scripts/qapi/introspect.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@
2222
Union,
2323
)
2424

25-
from .common import (
26-
c_name,
27-
gen_endif,
28-
gen_if,
29-
mcgen,
30-
)
25+
from .common import c_name, mcgen
3126
from .gen import QAPISchemaMonolithicCVisitor
3227
from .schema import (
3328
QAPISchema,
@@ -124,10 +119,10 @@ def indent(level: int) -> str:
124119
if obj.comment:
125120
ret += indent(level) + f"/* {obj.comment} */\n"
126121
if obj.ifcond.is_present():
127-
ret += gen_if(obj.ifcond.cgen())
122+
ret += obj.ifcond.gen_if()
128123
ret += _tree_to_qlit(obj.value, level)
129124
if obj.ifcond.is_present():
130-
ret += '\n' + gen_endif(obj.ifcond.cgen())
125+
ret += '\n' + obj.ifcond.gen_endif()
131126
return ret
132127

133128
ret = ''

scripts/qapi/schema.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
c_name,
2525
cgen_ifcond,
2626
docgen_ifcond,
27+
gen_endif,
28+
gen_if,
2729
)
2830
from .error import QAPIError, QAPISemError, QAPISourceError
2931
from .expr import check_exprs
@@ -32,11 +34,17 @@
3234

3335
class QAPISchemaIfCond:
3436
def __init__(self, ifcond=None):
35-
self.ifcond = ifcond or {}
37+
self.ifcond = ifcond
3638

37-
def cgen(self):
39+
def _cgen(self):
3840
return cgen_ifcond(self.ifcond)
3941

42+
def gen_if(self):
43+
return gen_if(self._cgen())
44+
45+
def gen_endif(self):
46+
return gen_endif(self._cgen())
47+
4048
def docgen(self):
4149
return docgen_ifcond(self.ifcond)
4250

scripts/qapi/types.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,7 @@
1515

1616
from typing import List, Optional
1717

18-
from .common import (
19-
c_enum_const,
20-
c_name,
21-
gen_endif,
22-
gen_if,
23-
mcgen,
24-
)
18+
from .common import c_enum_const, c_name, mcgen
2519
from .gen import QAPISchemaModularCVisitor, ifcontext
2620
from .schema import (
2721
QAPISchema,
@@ -51,13 +45,13 @@ def gen_enum_lookup(name: str,
5145
''',
5246
c_name=c_name(name))
5347
for memb in members:
54-
ret += gen_if(memb.ifcond.cgen())
48+
ret += memb.ifcond.gen_if()
5549
index = c_enum_const(name, memb.name, prefix)
5650
ret += mcgen('''
5751
[%(index)s] = "%(name)s",
5852
''',
5953
index=index, name=memb.name)
60-
ret += gen_endif(memb.ifcond.cgen())
54+
ret += memb.ifcond.gen_endif()
6155

6256
ret += mcgen('''
6357
},
@@ -81,12 +75,12 @@ def gen_enum(name: str,
8175
c_name=c_name(name))
8276

8377
for memb in enum_members:
84-
ret += gen_if(memb.ifcond.cgen())
78+
ret += memb.ifcond.gen_if()
8579
ret += mcgen('''
8680
%(c_enum)s,
8781
''',
8882
c_enum=c_enum_const(name, memb.name, prefix))
89-
ret += gen_endif(memb.ifcond.cgen())
83+
ret += memb.ifcond.gen_endif()
9084

9185
ret += mcgen('''
9286
} %(c_name)s;
@@ -126,7 +120,7 @@ def gen_array(name: str, element_type: QAPISchemaType) -> str:
126120
def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
127121
ret = ''
128122
for memb in members:
129-
ret += gen_if(memb.ifcond.cgen())
123+
ret += memb.ifcond.gen_if()
130124
if memb.optional:
131125
ret += mcgen('''
132126
bool has_%(c_name)s;
@@ -136,7 +130,7 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
136130
%(c_type)s %(c_name)s;
137131
''',
138132
c_type=memb.type.c_type(), c_name=c_name(memb.name))
139-
ret += gen_endif(memb.ifcond.cgen())
133+
ret += memb.ifcond.gen_endif()
140134
return ret
141135

142136

@@ -159,7 +153,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond,
159153
ret += mcgen('''
160154
161155
''')
162-
ret += gen_if(ifcond.cgen())
156+
ret += ifcond.gen_if()
163157
ret += mcgen('''
164158
struct %(c_name)s {
165159
''',
@@ -193,7 +187,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond,
193187
ret += mcgen('''
194188
};
195189
''')
196-
ret += gen_endif(ifcond.cgen())
190+
ret += ifcond.gen_endif()
197191

198192
return ret
199193

@@ -220,13 +214,13 @@ def gen_variants(variants: QAPISchemaVariants) -> str:
220214
for var in variants.variants:
221215
if var.type.name == 'q_empty':
222216
continue
223-
ret += gen_if(var.ifcond.cgen())
217+
ret += var.ifcond.gen_if()
224218
ret += mcgen('''
225219
%(c_type)s %(c_name)s;
226220
''',
227221
c_type=var.type.c_unboxed_type(),
228222
c_name=c_name(var.name))
229-
ret += gen_endif(var.ifcond.cgen())
223+
ret += var.ifcond.gen_endif()
230224

231225
ret += mcgen('''
232226
} u;

scripts/qapi/visit.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
from .common import (
1919
c_enum_const,
2020
c_name,
21-
gen_endif,
22-
gen_if,
2321
indent,
2422
mcgen,
2523
)
@@ -79,7 +77,7 @@ def gen_visit_object_members(name: str,
7977

8078
for memb in members:
8179
deprecated = 'deprecated' in [f.name for f in memb.features]
82-
ret += gen_if(memb.ifcond.cgen())
80+
ret += memb.ifcond.gen_if()
8381
if memb.optional:
8482
ret += mcgen('''
8583
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
@@ -112,7 +110,7 @@ def gen_visit_object_members(name: str,
112110
ret += mcgen('''
113111
}
114112
''')
115-
ret += gen_endif(memb.ifcond.cgen())
113+
ret += memb.ifcond.gen_endif()
116114

117115
if variants:
118116
tag_member = variants.tag_member
@@ -126,7 +124,7 @@ def gen_visit_object_members(name: str,
126124
for var in variants.variants:
127125
case_str = c_enum_const(tag_member.type.name, var.name,
128126
tag_member.type.prefix)
129-
ret += gen_if(var.ifcond.cgen())
127+
ret += var.ifcond.gen_if()
130128
if var.type.name == 'q_empty':
131129
# valid variant and nothing to do
132130
ret += mcgen('''
@@ -142,7 +140,7 @@ def gen_visit_object_members(name: str,
142140
case=case_str,
143141
c_type=var.type.c_name(), c_name=c_name(var.name))
144142

145-
ret += gen_endif(var.ifcond.cgen())
143+
ret += var.ifcond.gen_endif()
146144
ret += mcgen('''
147145
default:
148146
abort();
@@ -228,7 +226,7 @@ def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
228226
c_name=c_name(name))
229227

230228
for var in variants.variants:
231-
ret += gen_if(var.ifcond.cgen())
229+
ret += var.ifcond.gen_if()
232230
ret += mcgen('''
233231
case %(case)s:
234232
''',
@@ -254,7 +252,7 @@ def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
254252
ret += mcgen('''
255253
break;
256254
''')
257-
ret += gen_endif(var.ifcond.cgen())
255+
ret += var.ifcond.gen_endif()
258256

259257
ret += mcgen('''
260258
case QTYPE_NONE:

tests/qapi-schema/bad-if-key.err

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
bad-if-key.json: In struct 'TestIfStruct':
2-
bad-if-key.json:2: 'if' condition has unknown key 'value'
2+
bad-if-key.json:2: 'if' condition of struct has unknown key 'value'
33
Valid keys are 'all', 'any', 'not'.

tests/qapi-schema/bad-if-keys.err

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
bad-if-keys.json: In struct 'TestIfStruct':
2-
bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all', 'any' or 'not'
2+
bad-if-keys.json:2: 'if' condition of struct has conflicting keys

0 commit comments

Comments
 (0)