qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

qapi: Add feature flags to remaining definitions

In v4.1.0, we added feature flags just to struct types (commit
6a8c0b5102^..f3ed93d545), to satisfy an immediate need (commit
c9d4070991 "file-posix: Add dynamic-auto-read-only QAPI feature"). In
v4.2.0, we added them to commands (commit 23394b4c39 "qapi: Add
feature flags to commands") to satisfy another immediate need (commit
d76744e65e "qapi: Allow introspecting fix for savevm's cooperation
with blockdev").

Add them to the remaining definitions: enumeration types, union types,
alternate types, and events.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200317115459.31821-13-armbru@redhat.com>

+242 -121
+38 -16
docs/devel/qapi-code-gen.txt
··· 172 172 ENUM = { 'enum': STRING, 173 173 'data': [ ENUM-VALUE, ... ], 174 174 '*prefix': STRING, 175 - '*if': COND } 175 + '*if': COND, 176 + '*features': FEATURES } 176 177 ENUM-VALUE = STRING 177 178 | { 'name': STRING, '*if': COND } 178 179 ··· 206 207 207 208 The optional 'if' member specifies a conditional. See "Configuring 208 209 the schema" below for more on this. 210 + 211 + The optional 'features' member specifies features. See "Features" 212 + below for more on this. 209 213 210 214 211 215 === Type references and array types === ··· 279 283 Syntax: 280 284 UNION = { 'union': STRING, 281 285 'data': BRANCHES, 282 - '*if': COND } 286 + '*if': COND, 287 + '*features': FEATURES } 283 288 | { 'union': STRING, 284 289 'data': BRANCHES, 285 290 'base': ( MEMBERS | STRING ), 286 291 'discriminator': STRING, 287 - '*if': COND } 292 + '*if': COND, 293 + '*features': FEATURES } 288 294 BRANCHES = { BRANCH, ... } 289 295 BRANCH = STRING : TYPE-REF 290 296 | STRING : { 'type': TYPE-REF, '*if': COND } ··· 391 397 The optional 'if' member specifies a conditional. See "Configuring 392 398 the schema" below for more on this. 393 399 400 + The optional 'features' member specifies features. See "Features" 401 + below for more on this. 402 + 394 403 395 404 === Alternate types === 396 405 397 406 Syntax: 398 407 ALTERNATE = { 'alternate': STRING, 399 408 'data': ALTERNATIVES, 400 - '*if': COND } 409 + '*if': COND, 410 + '*features': FEATURES } 401 411 ALTERNATIVES = { ALTERNATIVE, ... } 402 412 ALTERNATIVE = STRING : STRING 403 413 | STRING : { 'type': STRING, '*if': COND } ··· 440 450 441 451 The optional 'if' member specifies a conditional. See "Configuring 442 452 the schema" below for more on this. 453 + 454 + The optional 'features' member specifies features. See "Features" 455 + below for more on this. 443 456 444 457 445 458 === Commands === ··· 584 597 The optional 'if' member specifies a conditional. See "Configuring 585 598 the schema" below for more on this. 586 599 600 + The optional 'features' member specifies features. See "Features" 601 + below for more on this. 602 + 587 603 588 604 === Events === 589 605 ··· 595 611 'data': STRING, 596 612 'boxed': true, 597 613 ) 598 - '*if': COND } 614 + '*if': COND, 615 + '*features': FEATURES } 599 616 600 617 Member 'event' names the event. This is the event name used in the 601 618 Client JSON Protocol. ··· 628 645 The optional 'if' member specifies a conditional. See "Configuring 629 646 the schema" below for more on this. 630 647 648 + The optional 'features' member specifies features. See "Features" 649 + below for more on this. 650 + 631 651 632 652 === Features === 633 653 ··· 966 986 overview how things work. For details you need to consult the QAPI 967 987 schema. 968 988 969 - SchemaInfo objects have common members "name", "meta-type", and 970 - additional variant members depending on the value of meta-type. 989 + SchemaInfo objects have common members "name", "meta-type", 990 + "features", and additional variant members depending on the value of 991 + meta-type. 971 992 972 993 Each SchemaInfo object describes a wire ABI entity of a certain 973 994 meta-type: a command, event or one of several kinds of type. ··· 980 1001 meaningless names. For readability, the examples in this section use 981 1002 meaningful type names instead. 982 1003 1004 + Optional member "features" exposes the entity's feature strings as a 1005 + JSON array of strings. 1006 + 983 1007 To examine a type, start with a command or event using it, then follow 984 1008 references by name. 985 1009 986 1010 QAPI schema definitions not reachable that way are omitted. 987 1011 988 1012 The SchemaInfo for a command has meta-type "command", and variant 989 - members "arg-type", "ret-type", "allow-oob", and "features". On the 990 - wire, the "arguments" member of a client's "execute" command must 991 - conform to the object type named by "arg-type". The "return" member 992 - that the server passes in a success response conforms to the type 993 - named by "ret-type". When "allow-oob" is true, it means the command 994 - supports out-of-band execution. It defaults to false. "features" 995 - exposes the command's feature strings as a JSON array of strings. 1013 + members "arg-type", "ret-type" and "allow-oob". On the wire, the 1014 + "arguments" member of a client's "execute" command must conform to the 1015 + object type named by "arg-type". The "return" member that the server 1016 + passes in a success response conforms to the type named by "ret-type". 1017 + When "allow-oob" is true, it means the command supports out-of-band 1018 + execution. It defaults to false. 996 1019 997 1020 If the command takes no arguments, "arg-type" names an object type 998 1021 without members. Likewise, if the command returns nothing, "ret-type" ··· 1027 1050 1028 1051 The SchemaInfo for struct and union types has meta-type "object". 1029 1052 1030 - The SchemaInfo for a struct type has variant members "members" and 1031 - "features". 1053 + The SchemaInfo for a struct type has variant member "members". 1032 1054 1033 1055 The SchemaInfo for a union type additionally has variant members "tag" 1034 1056 and "variants".
+9 -11
qapi/introspect.json
··· 89 89 # 90 90 # @meta-type: the entity's meta type, inherited from @base. 91 91 # 92 + # @features: names of features associated with the entity, in no 93 + # particular order. 94 + # (since 4.1 for object types, 4.2 for commands, 5.0 for 95 + # the rest) 96 + # 92 97 # Additional members depend on the value of @meta-type. 93 98 # 94 99 # Since: 2.5 95 100 ## 96 101 { 'union': 'SchemaInfo', 97 - 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType' }, 102 + 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType', 103 + '*features': [ 'str' ] }, 98 104 'discriminator': 'meta-type', 99 105 'data': { 100 106 'builtin': 'SchemaInfoBuiltin', ··· 174 180 # and may even differ from the order of the values of the 175 181 # enum type of the @tag. 176 182 # 177 - # @features: names of features associated with the type, in no particular 178 - # order. (since: 4.1) 179 - # 180 183 # Values of this type are JSON object on the wire. 181 184 # 182 185 # Since: 2.5 ··· 184 187 { 'struct': 'SchemaInfoObject', 185 188 'data': { 'members': [ 'SchemaInfoObjectMember' ], 186 189 '*tag': 'str', 187 - '*variants': [ 'SchemaInfoObjectVariant' ], 188 - '*features': [ 'str' ] } } 190 + '*variants': [ 'SchemaInfoObjectVariant' ] } } 189 191 190 192 ## 191 193 # @SchemaInfoObjectMember: ··· 266 268 # @allow-oob: whether the command allows out-of-band execution, 267 269 # defaults to false (Since: 2.12) 268 270 # 269 - # @features: names of features associated with the command, in no particular 270 - # order. (since 4.2) 271 - # 272 271 # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) 273 272 # 274 273 # Since: 2.5 275 274 ## 276 275 { 'struct': 'SchemaInfoCommand', 277 276 'data': { 'arg-type': 'str', 'ret-type': 'str', 278 - '*allow-oob': 'bool', 279 - '*features': [ 'str' ] } } 277 + '*allow-oob': 'bool' } } 280 278 281 279 ## 282 280 # @SchemaInfoEvent:
+3 -3
scripts/qapi/doc.py
··· 243 243 def write(self, output_dir): 244 244 self._gen.write(output_dir) 245 245 246 - def visit_enum_type(self, name, info, ifcond, members, prefix): 246 + def visit_enum_type(self, name, info, ifcond, features, members, prefix): 247 247 doc = self.cur_doc 248 248 self._gen.add(texi_type('Enum', doc, ifcond, 249 249 texi_members(doc, 'Values', ··· 257 257 self._gen.add(texi_type('Object', doc, ifcond, 258 258 texi_members(doc, 'Members', base, variants))) 259 259 260 - def visit_alternate_type(self, name, info, ifcond, variants): 260 + def visit_alternate_type(self, name, info, ifcond, features, variants): 261 261 doc = self.cur_doc 262 262 self._gen.add(texi_type('Alternate', doc, ifcond, 263 263 texi_members(doc, 'Members'))) ··· 270 270 texi_arguments(doc, 271 271 arg_type if boxed else None))) 272 272 273 - def visit_event(self, name, info, ifcond, arg_type, boxed): 273 + def visit_event(self, name, info, ifcond, features, arg_type, boxed): 274 274 doc = self.cur_doc 275 275 self._gen.add(texi_msg('Event', doc, ifcond, 276 276 texi_arguments(doc,
+1 -1
scripts/qapi/events.py
··· 189 189 event_emit=self._event_emit_name, 190 190 event_enum=self._event_enum_name)) 191 191 192 - def visit_event(self, name, info, ifcond, arg_type, boxed): 192 + def visit_event(self, name, info, ifcond, features, arg_type, boxed): 193 193 with ifcontext(ifcond, self._genh, self._genc): 194 194 self._genh.add(gen_event_send_decl(name, arg_type, boxed)) 195 195 self._genc.add(gen_event_send(name, arg_type, boxed,
+5 -6
scripts/qapi/expr.py
··· 219 219 220 220 check_type(members, info, "'data'", allow_dict=name) 221 221 check_type(expr.get('base'), info, "'base'") 222 - check_features(expr.get('features'), info) 223 222 224 223 225 224 def check_union(expr, info): ··· 267 266 raise QAPISemError(info, "'boxed': true requires 'data'") 268 267 check_type(args, info, "'data'", allow_dict=not boxed) 269 268 check_type(rets, info, "'returns'", allow_array=True) 270 - check_features(expr.get('features'), info) 271 269 272 270 273 271 def check_event(expr, info): ··· 319 317 320 318 if meta == 'enum': 321 319 check_keys(expr, info, meta, 322 - ['enum', 'data'], ['if', 'prefix']) 320 + ['enum', 'data'], ['if', 'features', 'prefix']) 323 321 check_enum(expr, info) 324 322 elif meta == 'union': 325 323 check_keys(expr, info, meta, 326 324 ['union', 'data'], 327 - ['base', 'discriminator', 'if']) 325 + ['base', 'discriminator', 'if', 'features']) 328 326 normalize_members(expr.get('base')) 329 327 normalize_members(expr['data']) 330 328 check_union(expr, info) 331 329 elif meta == 'alternate': 332 330 check_keys(expr, info, meta, 333 - ['alternate', 'data'], ['if']) 331 + ['alternate', 'data'], ['if', 'features']) 334 332 normalize_members(expr['data']) 335 333 check_alternate(expr, info) 336 334 elif meta == 'struct': ··· 348 346 check_command(expr, info) 349 347 elif meta == 'event': 350 348 check_keys(expr, info, meta, 351 - ['event'], ['data', 'boxed', 'if']) 349 + ['event'], ['data', 'boxed', 'if', 'features']) 352 350 normalize_members(expr.get('data')) 353 351 check_event(expr, info) 354 352 else: 355 353 assert False, 'unexpected meta type' 356 354 357 355 check_if(expr, info, meta) 356 + check_features(expr.get('features'), info) 358 357 check_flags(expr, info) 359 358 360 359 return exprs
+14 -17
scripts/qapi/introspect.py
··· 144 144 return '[' + self._use_type(typ.element_type) + ']' 145 145 return self._name(typ.name) 146 146 147 - def _gen_qlit(self, name, mtype, obj, ifcond): 147 + def _gen_qlit(self, name, mtype, obj, ifcond, features): 148 148 extra = {} 149 149 if mtype not in ('command', 'event', 'builtin', 'array'): 150 150 if not self._unmask: ··· 154 154 name = self._name(name) 155 155 obj['name'] = name 156 156 obj['meta-type'] = mtype 157 + if features: 158 + obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] 157 159 if ifcond: 158 160 extra['if'] = ifcond 159 161 if extra: ··· 178 180 {'if': variant.ifcond}) 179 181 180 182 def visit_builtin_type(self, name, info, json_type): 181 - self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) 183 + self._gen_qlit(name, 'builtin', {'json-type': json_type}, [], None) 182 184 183 - def visit_enum_type(self, name, info, ifcond, members, prefix): 185 + def visit_enum_type(self, name, info, ifcond, features, members, prefix): 184 186 self._gen_qlit(name, 'enum', 185 187 {'values': 186 188 [(m.name, {'if': m.ifcond}) for m in members]}, 187 - ifcond) 189 + ifcond, features) 188 190 189 191 def visit_array_type(self, name, info, ifcond, element_type): 190 192 element = self._use_type(element_type) 191 193 self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, 192 - ifcond) 194 + ifcond, None) 193 195 194 196 def visit_object_type_flat(self, name, info, ifcond, members, variants, 195 197 features): ··· 197 199 if variants: 198 200 obj.update(self._gen_variants(variants.tag_member.name, 199 201 variants.variants)) 200 - if features: 201 - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] 202 202 203 - self._gen_qlit(name, 'object', obj, ifcond) 203 + self._gen_qlit(name, 'object', obj, ifcond, features) 204 204 205 - def visit_alternate_type(self, name, info, ifcond, variants): 205 + def visit_alternate_type(self, name, info, ifcond, features, variants): 206 206 self._gen_qlit(name, 'alternate', 207 207 {'members': [ 208 208 ({'type': self._use_type(m.type)}, {'if': m.ifcond}) 209 - for m in variants.variants]}, ifcond) 209 + for m in variants.variants]}, 210 + ifcond, features) 210 211 211 212 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, 212 213 success_response, boxed, allow_oob, allow_preconfig, ··· 217 218 'ret-type': self._use_type(ret_type)} 218 219 if allow_oob: 219 220 obj['allow-oob'] = allow_oob 221 + self._gen_qlit(name, 'command', obj, ifcond, features) 220 222 221 - if features: 222 - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] 223 - 224 - self._gen_qlit(name, 'command', obj, ifcond) 225 - 226 - def visit_event(self, name, info, ifcond, arg_type, boxed): 223 + def visit_event(self, name, info, ifcond, features, arg_type, boxed): 227 224 arg_type = arg_type or self._schema.the_empty_object_type 228 225 self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}, 229 - ifcond) 226 + ifcond, features) 230 227 231 228 232 229 def gen_introspect(schema, output_dir, prefix, opt_unmask):
+53 -43
scripts/qapi/schema.py
··· 109 109 def visit_builtin_type(self, name, info, json_type): 110 110 pass 111 111 112 - def visit_enum_type(self, name, info, ifcond, members, prefix): 112 + def visit_enum_type(self, name, info, ifcond, features, members, prefix): 113 113 pass 114 114 115 115 def visit_array_type(self, name, info, ifcond, element_type): ··· 123 123 features): 124 124 pass 125 125 126 - def visit_alternate_type(self, name, info, ifcond, variants): 126 + def visit_alternate_type(self, name, info, ifcond, features, variants): 127 127 pass 128 128 129 129 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, ··· 131 131 features): 132 132 pass 133 133 134 - def visit_event(self, name, info, ifcond, arg_type, boxed): 134 + def visit_event(self, name, info, ifcond, features, arg_type, boxed): 135 135 pass 136 136 137 137 ··· 234 234 class QAPISchemaEnumType(QAPISchemaType): 235 235 meta = 'enum' 236 236 237 - def __init__(self, name, info, doc, ifcond, members, prefix): 238 - super().__init__(name, info, doc, ifcond) 237 + def __init__(self, name, info, doc, ifcond, features, members, prefix): 238 + super().__init__(name, info, doc, ifcond, features) 239 239 for m in members: 240 240 assert isinstance(m, QAPISchemaEnumMember) 241 241 m.set_defined_in(name) ··· 271 271 272 272 def visit(self, visitor): 273 273 super().visit(visitor) 274 - visitor.visit_enum_type(self.name, self.info, self.ifcond, 275 - self.members, self.prefix) 274 + visitor.visit_enum_type( 275 + self.name, self.info, self.ifcond, self.features, 276 + self.members, self.prefix) 276 277 277 278 278 279 class QAPISchemaArrayType(QAPISchemaType): 279 280 meta = 'array' 280 281 281 282 def __init__(self, name, info, element_type): 282 - super().__init__(name, info, None, None) 283 + super().__init__(name, info, None) 283 284 assert isinstance(element_type, str) 284 285 self._element_type_name = element_type 285 286 self.element_type = None ··· 325 326 326 327 327 328 class QAPISchemaObjectType(QAPISchemaType): 328 - def __init__(self, name, info, doc, ifcond, 329 - base, local_members, variants, features): 329 + def __init__(self, name, info, doc, ifcond, features, 330 + base, local_members, variants): 330 331 # struct has local_members, optional base, and no variants 331 332 # flat union has base, variants, and no local_members 332 333 # simple union has local_members, variants, and no base ··· 622 623 class QAPISchemaAlternateType(QAPISchemaType): 623 624 meta = 'alternate' 624 625 625 - def __init__(self, name, info, doc, ifcond, variants): 626 - super().__init__(name, info, doc, ifcond) 626 + def __init__(self, name, info, doc, ifcond, features, variants): 627 + super().__init__(name, info, doc, ifcond, features) 627 628 assert isinstance(variants, QAPISchemaObjectTypeVariants) 628 629 assert variants.tag_member 629 630 variants.set_defined_in(name) ··· 683 684 684 685 def visit(self, visitor): 685 686 super().visit(visitor) 686 - visitor.visit_alternate_type(self.name, self.info, self.ifcond, 687 - self.variants) 687 + visitor.visit_alternate_type( 688 + self.name, self.info, self.ifcond, self.features, self.variants) 688 689 689 690 690 691 class QAPISchemaCommand(QAPISchemaEntity): 691 692 meta = 'command' 692 693 693 - def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 694 - gen, success_response, boxed, allow_oob, allow_preconfig, 695 - features): 694 + def __init__(self, name, info, doc, ifcond, features, 695 + arg_type, ret_type, 696 + gen, success_response, boxed, allow_oob, allow_preconfig): 696 697 super().__init__(name, info, doc, ifcond, features) 697 698 assert not arg_type or isinstance(arg_type, str) 698 699 assert not ret_type or isinstance(ret_type, str) ··· 755 756 class QAPISchemaEvent(QAPISchemaEntity): 756 757 meta = 'event' 757 758 758 - def __init__(self, name, info, doc, ifcond, arg_type, boxed): 759 - super().__init__(name, info, doc, ifcond) 759 + def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): 760 + super().__init__(name, info, doc, ifcond, features) 760 761 assert not arg_type or isinstance(arg_type, str) 761 762 self._arg_type_name = arg_type 762 763 self.arg_type = None ··· 787 788 788 789 def visit(self, visitor): 789 790 super().visit(visitor) 790 - visitor.visit_event(self.name, self.info, self.ifcond, 791 - self.arg_type, self.boxed) 791 + visitor.visit_event( 792 + self.name, self.info, self.ifcond, self.features, 793 + self.arg_type, self.boxed) 792 794 793 795 794 796 class QAPISchema: ··· 893 895 ('null', 'null', 'QNull' + pointer_suffix)]: 894 896 self._def_builtin_type(*t) 895 897 self.the_empty_object_type = QAPISchemaObjectType( 896 - 'q_empty', None, None, None, None, [], None, []) 898 + 'q_empty', None, None, None, None, None, [], None) 897 899 self._def_entity(self.the_empty_object_type) 898 900 899 901 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', ··· 901 903 qtype_values = self._make_enum_members( 902 904 [{'name': n} for n in qtypes], None) 903 905 904 - self._def_entity(QAPISchemaEnumType('QType', None, None, None, 906 + self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, 905 907 qtype_values, 'QTYPE')) 906 908 907 - def _make_features(self, features, info): 909 + def _make_features(self, expr, info): 910 + features = expr.get('features', []) 908 911 return [QAPISchemaFeature(f['name'], info, f.get('if')) 909 912 for f in features] 910 913 ··· 916 919 # See also QAPISchemaObjectTypeMember.describe() 917 920 name = name + 'Kind' # reserved by check_defn_name_str() 918 921 self._def_entity(QAPISchemaEnumType( 919 - name, info, None, ifcond, self._make_enum_members(values, info), 922 + name, info, None, ifcond, None, 923 + self._make_enum_members(values, info), 920 924 None)) 921 925 return name 922 926 ··· 944 948 # TODO kill simple unions or implement the disjunction 945 949 assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access 946 950 else: 947 - self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, 948 - None, members, None, [])) 951 + self._def_entity(QAPISchemaObjectType( 952 + name, info, None, ifcond, None, None, members, None)) 949 953 return name 950 954 951 955 def _def_enum_type(self, expr, info, doc): ··· 953 957 data = expr['data'] 954 958 prefix = expr.get('prefix') 955 959 ifcond = expr.get('if') 960 + features = self._make_features(expr, info) 956 961 self._def_entity(QAPISchemaEnumType( 957 - name, info, doc, ifcond, 962 + name, info, doc, ifcond, features, 958 963 self._make_enum_members(data, info), prefix)) 959 964 960 965 def _make_member(self, name, typ, ifcond, info): ··· 976 981 base = expr.get('base') 977 982 data = expr['data'] 978 983 ifcond = expr.get('if') 979 - features = expr.get('features', []) 984 + features = self._make_features(expr, info) 980 985 self._def_entity(QAPISchemaObjectType( 981 - name, info, doc, ifcond, base, 986 + name, info, doc, ifcond, features, base, 982 987 self._make_members(data, info), 983 - None, 984 - self._make_features(features, info))) 988 + None)) 985 989 986 990 def _make_variant(self, case, typ, ifcond, info): 987 991 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) ··· 1000 1004 data = expr['data'] 1001 1005 base = expr.get('base') 1002 1006 ifcond = expr.get('if') 1007 + features = self._make_features(expr, info) 1003 1008 tag_name = expr.get('discriminator') 1004 1009 tag_member = None 1005 1010 if isinstance(base, dict): ··· 1020 1025 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) 1021 1026 members = [tag_member] 1022 1027 self._def_entity( 1023 - QAPISchemaObjectType(name, info, doc, ifcond, base, members, 1028 + QAPISchemaObjectType(name, info, doc, ifcond, features, 1029 + base, members, 1024 1030 QAPISchemaObjectTypeVariants( 1025 - tag_name, info, tag_member, variants), 1026 - [])) 1031 + tag_name, info, tag_member, variants))) 1027 1032 1028 1033 def _def_alternate_type(self, expr, info, doc): 1029 1034 name = expr['alternate'] 1030 1035 data = expr['data'] 1031 1036 ifcond = expr.get('if') 1037 + features = self._make_features(expr, info) 1032 1038 variants = [self._make_variant(key, value['type'], value.get('if'), 1033 1039 info) 1034 1040 for (key, value) in data.items()] 1035 1041 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) 1036 1042 self._def_entity( 1037 - QAPISchemaAlternateType(name, info, doc, ifcond, 1043 + QAPISchemaAlternateType(name, info, doc, ifcond, features, 1038 1044 QAPISchemaObjectTypeVariants( 1039 1045 None, info, tag_member, variants))) 1040 1046 ··· 1048 1054 allow_oob = expr.get('allow-oob', False) 1049 1055 allow_preconfig = expr.get('allow-preconfig', False) 1050 1056 ifcond = expr.get('if') 1051 - features = expr.get('features', []) 1057 + features = self._make_features(expr, info) 1052 1058 if isinstance(data, OrderedDict): 1053 1059 data = self._make_implicit_object_type( 1054 - name, info, ifcond, 'arg', self._make_members(data, info)) 1060 + name, info, ifcond, 1061 + 'arg', self._make_members(data, info)) 1055 1062 if isinstance(rets, list): 1056 1063 assert len(rets) == 1 1057 1064 rets = self._make_array_type(rets[0], info) 1058 - self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, 1065 + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, 1066 + data, rets, 1059 1067 gen, success_response, 1060 - boxed, allow_oob, allow_preconfig, 1061 - self._make_features(features, info))) 1068 + boxed, allow_oob, allow_preconfig)) 1062 1069 1063 1070 def _def_event(self, expr, info, doc): 1064 1071 name = expr['event'] 1065 1072 data = expr.get('data') 1066 1073 boxed = expr.get('boxed', False) 1067 1074 ifcond = expr.get('if') 1075 + features = self._make_features(expr, info) 1068 1076 if isinstance(data, OrderedDict): 1069 1077 data = self._make_implicit_object_type( 1070 - name, info, ifcond, 'arg', self._make_members(data, info)) 1071 - self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) 1078 + name, info, ifcond, 1079 + 'arg', self._make_members(data, info)) 1080 + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, 1081 + data, boxed)) 1072 1082 1073 1083 def _def_exprs(self, exprs): 1074 1084 for expr_elem in exprs:
+2 -2
scripts/qapi/types.py
··· 278 278 self._genh.add(gen_type_cleanup_decl(name)) 279 279 self._genc.add(gen_type_cleanup(name)) 280 280 281 - def visit_enum_type(self, name, info, ifcond, members, prefix): 281 + def visit_enum_type(self, name, info, ifcond, features, members, prefix): 282 282 with ifcontext(ifcond, self._genh, self._genc): 283 283 self._genh.preamble_add(gen_enum(name, members, prefix)) 284 284 self._genc.add(gen_enum_lookup(name, members, prefix)) ··· 306 306 # implicit types won't be directly allocated/freed 307 307 self._gen_type_cleanup(name) 308 308 309 - def visit_alternate_type(self, name, info, ifcond, variants): 309 + def visit_alternate_type(self, name, info, ifcond, features, variants): 310 310 with ifcontext(ifcond, self._genh): 311 311 self._genh.preamble_add(gen_fwd_object_or_array(name)) 312 312 self._genh.add(gen_object(name, ifcond, None,
+2 -2
scripts/qapi/visit.py
··· 316 316 ''', 317 317 types=types)) 318 318 319 - def visit_enum_type(self, name, info, ifcond, members, prefix): 319 + def visit_enum_type(self, name, info, ifcond, features, members, prefix): 320 320 with ifcontext(ifcond, self._genh, self._genc): 321 321 self._genh.add(gen_visit_decl(name, scalar=True)) 322 322 self._genc.add(gen_visit_enum(name)) ··· 342 342 self._genh.add(gen_visit_decl(name)) 343 343 self._genc.add(gen_visit_object(name, base, members, variants)) 344 344 345 - def visit_alternate_type(self, name, info, ifcond, variants): 345 + def visit_alternate_type(self, name, info, ifcond, features, variants): 346 346 with ifcontext(ifcond, self._genh, self._genc): 347 347 self._genh.add(gen_visit_decl(name)) 348 348 self._genc.add(gen_visit_alternate(name, variants))
+1 -1
tests/qapi-schema/alternate-base.err
··· 1 1 alternate-base.json: In alternate 'Alt': 2 2 alternate-base.json:4: alternate has unknown key 'base' 3 - Valid keys are 'alternate', 'data', 'if'. 3 + Valid keys are 'alternate', 'data', 'features', 'if'.
+17
tests/qapi-schema/doc-good.json
··· 53 53 # @Enum: 54 54 # @one: The _one_ {and only} 55 55 # 56 + # Features: 57 + # @enum-feat: Also _one_ {and only} 58 + # 56 59 # @two is undocumented 57 60 ## 58 61 { 'enum': 'Enum', 'data': 59 62 [ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ], 63 + 'features': [ 'enum-feat' ], 60 64 'if': 'defined(IFCOND)' } 61 65 62 66 ## ··· 86 90 87 91 ## 88 92 # @Object: 93 + # Features: 94 + # @union-feat1: a feature 89 95 ## 90 96 { 'union': 'Object', 97 + 'features': [ 'union-feat1' ], 91 98 'base': 'Base', 92 99 'discriminator': 'base1', 93 100 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } 94 101 95 102 ## 96 103 # @SugaredUnion: 104 + # Features: 105 + # @union-feat2: a feature 97 106 ## 98 107 { 'union': 'SugaredUnion', 108 + 'features': [ 'union-feat2' ], 99 109 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } 100 110 101 111 ## 102 112 # @Alternate: 103 113 # @i: an integer 104 114 # @b is undocumented 115 + # 116 + # Features: 117 + # @alt-feat: a feature 105 118 ## 106 119 { 'alternate': 'Alternate', 120 + 'features': [ 'alt-feat' ], 107 121 'data': { 'i': 'int', 'b': 'bool' } } 108 122 109 123 ## ··· 160 174 161 175 ## 162 176 # @EVT-BOXED: 177 + # Features: 178 + # @feat3: a feature 163 179 ## 164 180 { 'event': 'EVT-BOXED', 'boxed': true, 181 + 'features': [ 'feat3' ], 165 182 'data': 'Object' }
+15
tests/qapi-schema/doc-good.out
··· 15 15 if ['defined(IFONE)'] 16 16 member two 17 17 if ['defined(IFCOND)'] 18 + feature enum-feat 18 19 object Base 19 20 member base1: Enum optional=False 20 21 object Variant1 ··· 28 29 case one: Variant1 29 30 case two: Variant2 30 31 if ['IFTWO'] 32 + feature union-feat1 31 33 object q_obj_Variant1-wrapper 32 34 member data: Variant1 optional=False 33 35 object q_obj_Variant2-wrapper ··· 42 44 case one: q_obj_Variant1-wrapper 43 45 case two: q_obj_Variant2-wrapper 44 46 if ['IFTWO'] 47 + feature union-feat2 45 48 alternate Alternate 46 49 tag type 47 50 case i: int 48 51 case b: bool 52 + feature alt-feat 49 53 object q_obj_cmd-arg 50 54 member arg1: int optional=False 51 55 member arg2: str optional=True ··· 60 64 feature cmd-feat2 61 65 event EVT-BOXED Object 62 66 boxed=True 67 + feature feat3 63 68 doc freeform 64 69 body= 65 70 = Section ··· 112 117 The _one_ {and only} 113 118 arg=two 114 119 120 + feature=enum-feat 121 + Also _one_ {and only} 115 122 section=None 116 123 @two is undocumented 117 124 doc symbol=Base ··· 134 141 doc symbol=Object 135 142 body= 136 143 144 + feature=union-feat1 145 + a feature 137 146 doc symbol=SugaredUnion 138 147 body= 139 148 140 149 arg=type 141 150 151 + feature=union-feat2 152 + a feature 142 153 doc symbol=Alternate 143 154 body= 144 155 ··· 147 158 @b is undocumented 148 159 arg=b 149 160 161 + feature=alt-feat 162 + a feature 150 163 doc freeform 151 164 body= 152 165 == Another subsection ··· 197 210 doc symbol=EVT-BOXED 198 211 body= 199 212 213 + feature=feat3 214 + a feature
+30
tests/qapi-schema/doc-good.texi
··· 88 88 @item @code{two} 89 89 Not documented 90 90 @end table 91 + 92 + @b{Features:} 93 + @table @asis 94 + @item @code{enum-feat} 95 + Also @emph{one} @{and only@} 96 + @end table 91 97 @code{two} is undocumented 92 98 93 99 @b{If:} @code{defined(IFCOND)} ··· 151 157 @item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO}) 152 158 @end table 153 159 160 + @b{Features:} 161 + @table @asis 162 + @item @code{union-feat1} 163 + a feature 164 + @end table 165 + 154 166 @end deftp 155 167 156 168 ··· 167 179 @item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO}) 168 180 @end table 169 181 182 + @b{Features:} 183 + @table @asis 184 + @item @code{union-feat2} 185 + a feature 186 + @end table 187 + 170 188 @end deftp 171 189 172 190 ··· 182 200 @code{b} is undocumented 183 201 @item @code{b: boolean} 184 202 Not documented 203 + @end table 204 + 205 + @b{Features:} 206 + @table @asis 207 + @item @code{alt-feat} 208 + a feature 185 209 @end table 186 210 187 211 @end deftp ··· 282 306 283 307 284 308 @b{Arguments:} the members of @code{Object} 309 + 310 + @b{Features:} 311 + @table @asis 312 + @item @code{feat3} 313 + a feature 314 + @end table 285 315 286 316 @end deftypefn 287 317
+22 -7
tests/qapi-schema/qapi-schema-test.json
··· 252 252 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, 253 253 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } 254 254 255 - # test 'features' for structs 255 + # test 'features' 256 256 257 257 { 'struct': 'FeatureStruct0', 258 258 'data': { 'foo': 'int' }, ··· 281 281 'data': { 'foo': 'int' }, 282 282 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 283 283 'defined(TEST_IF_COND_2)'] } ] } 284 - { 'command': 'test-features', 284 + 285 + { 'enum': 'FeatureEnum1', 286 + 'data': [ 'eins', 'zwei', 'drei' ], 287 + 'features': [ 'feature1' ] } 288 + 289 + { 'union': 'FeatureUnion1', 290 + 'base': { 'tag': 'FeatureEnum1' }, 291 + 'discriminator': 'tag', 292 + 'data': { 'eins': 'FeatureStruct1' }, 293 + 'features': [ 'feature1' ] } 294 + 295 + { 'alternate': 'FeatureAlternate1', 296 + 'data': { 'eins': 'FeatureStruct1' }, 297 + 'features': [ 'feature1' ] } 298 + 299 + { 'command': 'test-features0', 285 300 'data': { 'fs0': 'FeatureStruct0', 286 301 'fs1': 'FeatureStruct1', 287 302 'fs2': 'FeatureStruct2', ··· 289 304 'fs4': 'FeatureStruct4', 290 305 'cfs1': 'CondFeatureStruct1', 291 306 'cfs2': 'CondFeatureStruct2', 292 - 'cfs3': 'CondFeatureStruct3' } } 293 - 294 - # test 'features' for command 295 - 296 - { 'command': 'test-command-features0', 307 + 'cfs3': 'CondFeatureStruct3' }, 297 308 'features': [] } 309 + 298 310 { 'command': 'test-command-features1', 299 311 'features': [ 'feature1' ] } 300 312 { 'command': 'test-command-features3', ··· 308 320 { 'command': 'test-command-cond-features3', 309 321 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 310 322 'defined(TEST_IF_COND_2)'] } ] } 323 + 324 + { 'event': 'TEST-EVENT-FEATURES1', 325 + 'features': [ 'feature1' ] }
+23 -4
tests/qapi-schema/qapi-schema-test.out
··· 387 387 member foo: int optional=False 388 388 feature feature1 389 389 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] 390 - object q_obj_test-features-arg 390 + enum FeatureEnum1 391 + member eins 392 + member zwei 393 + member drei 394 + feature feature1 395 + object q_obj_FeatureUnion1-base 396 + member tag: FeatureEnum1 optional=False 397 + object FeatureUnion1 398 + base q_obj_FeatureUnion1-base 399 + tag tag 400 + case eins: FeatureStruct1 401 + case zwei: q_empty 402 + case drei: q_empty 403 + feature feature1 404 + alternate FeatureAlternate1 405 + tag type 406 + case eins: FeatureStruct1 407 + feature feature1 408 + object q_obj_test-features0-arg 391 409 member fs0: FeatureStruct0 optional=False 392 410 member fs1: FeatureStruct1 optional=False 393 411 member fs2: FeatureStruct2 optional=False ··· 396 414 member cfs1: CondFeatureStruct1 optional=False 397 415 member cfs2: CondFeatureStruct2 optional=False 398 416 member cfs3: CondFeatureStruct3 optional=False 399 - command test-features q_obj_test-features-arg -> None 400 - gen=True success_response=True boxed=False oob=False preconfig=False 401 - command test-command-features0 None -> None 417 + command test-features0 q_obj_test-features0-arg -> None 402 418 gen=True success_response=True boxed=False oob=False preconfig=False 403 419 command test-command-features1 None -> None 404 420 gen=True success_response=True boxed=False oob=False preconfig=False ··· 421 437 gen=True success_response=True boxed=False oob=False preconfig=False 422 438 feature feature1 423 439 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] 440 + event TEST-EVENT-FEATURES1 None 441 + boxed=False 442 + feature feature1 424 443 module include/sub-module.json 425 444 include sub-sub-module.json 426 445 object SecondArrayRef
+6 -3
tests/qapi-schema/test-qapi.py
··· 30 30 def visit_include(self, name, info): 31 31 print('include %s' % name) 32 32 33 - def visit_enum_type(self, name, info, ifcond, members, prefix): 33 + def visit_enum_type(self, name, info, ifcond, features, members, prefix): 34 34 print('enum %s' % name) 35 35 if prefix: 36 36 print(' prefix %s' % prefix) ··· 38 38 print(' member %s' % m.name) 39 39 self._print_if(m.ifcond, indent=8) 40 40 self._print_if(ifcond) 41 + self._print_features(features) 41 42 42 43 def visit_array_type(self, name, info, ifcond, element_type): 43 44 if not info: ··· 58 59 self._print_if(ifcond) 59 60 self._print_features(features) 60 61 61 - def visit_alternate_type(self, name, info, ifcond, variants): 62 + def visit_alternate_type(self, name, info, ifcond, features, variants): 62 63 print('alternate %s' % name) 63 64 self._print_variants(variants) 64 65 self._print_if(ifcond) 66 + self._print_features(features) 65 67 66 68 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, 67 69 success_response, boxed, allow_oob, allow_preconfig, ··· 74 76 self._print_if(ifcond) 75 77 self._print_features(features) 76 78 77 - def visit_event(self, name, info, ifcond, arg_type, boxed): 79 + def visit_event(self, name, info, ifcond, features, arg_type, boxed): 78 80 print('event %s %s' % (name, arg_type and arg_type.name)) 79 81 print(' boxed=%s' % boxed) 80 82 self._print_if(ifcond) 83 + self._print_features(features) 81 84 82 85 @staticmethod 83 86 def _print_variants(variants):
+1 -5
tests/test-qmp-cmds.c
··· 45 45 { 46 46 } 47 47 48 - void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, 48 + void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1, 49 49 FeatureStruct2 *fs2, FeatureStruct3 *fs3, 50 50 FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, 51 51 CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, 52 52 Error **errp) 53 - { 54 - } 55 - 56 - void qmp_test_command_features0(Error **errp) 57 53 { 58 54 } 59 55