blob: e97c978d38d94a7b4b797606e0f00da100a4635d [file] [log] [blame]
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001# -*- coding: utf-8 -*-
2#
3# QAPI schema internal representation
4#
5# Copyright (c) 2015-2019 Red Hat Inc.
6#
7# Authors:
8# Markus Armbruster <armbru@redhat.com>
9# Eric Blake <eblake@redhat.com>
10# Marc-André Lureau <marcandre.lureau@redhat.com>
11#
12# This work is licensed under the terms of the GNU GPL, version 2.
13# See the COPYING file in the top-level directory.
14
John Snowce7fde02024-03-15 16:22:40 +010015# pylint: disable=too-many-lines
16
Markus Armbrustere6c42b92019-10-18 09:43:44 +020017# TODO catching name collisions in generated code would be nice
18
John Snow4ed3fe02024-03-15 16:22:57 +010019from __future__ import annotations
20
John Snowd150be32024-03-15 16:22:44 +010021from abc import ABC, abstractmethod
John Snow67fea572020-10-09 12:15:29 -040022from collections import OrderedDict
Markus Armbrustere6c42b92019-10-18 09:43:44 +020023import os
24import re
John Snow4ed3fe02024-03-15 16:22:57 +010025from typing import (
26 Any,
27 Callable,
28 Dict,
29 List,
30 Optional,
31 Union,
32 cast,
33)
Markus Armbrustere6c42b92019-10-18 09:43:44 +020034
Marc-André Lureaud806f892021-08-04 12:31:00 +040035from .common import (
36 POINTER_SUFFIX,
37 c_name,
38 cgen_ifcond,
39 docgen_ifcond,
Markus Armbruster1889e572021-08-31 14:37:58 +020040 gen_endif,
41 gen_if,
Marc-André Lureaud806f892021-08-04 12:31:00 +040042)
John Snow3404e572021-05-19 14:39:37 -040043from .error import QAPIError, QAPISemError, QAPISourceError
John Snow7137a962020-10-09 12:15:27 -040044from .expr import check_exprs
John Snow4ed3fe02024-03-15 16:22:57 +010045from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser
46from .source import QAPISourceInfo
Markus Armbrustere6c42b92019-10-18 09:43:44 +020047
48
Marc-André Lureauf17539c2021-08-04 12:30:57 +040049class QAPISchemaIfCond:
John Snow4ed3fe02024-03-15 16:22:57 +010050 def __init__(
51 self,
52 ifcond: Optional[Union[str, Dict[str, object]]] = None,
53 ) -> None:
Markus Armbrustere46c9302021-08-31 14:37:59 +020054 self.ifcond = ifcond
Marc-André Lureauf17539c2021-08-04 12:30:57 +040055
John Snow4ed3fe02024-03-15 16:22:57 +010056 def _cgen(self) -> str:
Marc-André Lureau6cc2e482021-08-04 12:30:59 +040057 return cgen_ifcond(self.ifcond)
58
John Snow4ed3fe02024-03-15 16:22:57 +010059 def gen_if(self) -> str:
Markus Armbruster1889e572021-08-31 14:37:58 +020060 return gen_if(self._cgen())
61
John Snow4ed3fe02024-03-15 16:22:57 +010062 def gen_endif(self) -> str:
Markus Armbruster1889e572021-08-31 14:37:58 +020063 return gen_endif(self._cgen())
64
John Snow4ed3fe02024-03-15 16:22:57 +010065 def docgen(self) -> str:
Marc-André Lureaud806f892021-08-04 12:31:00 +040066 return docgen_ifcond(self.ifcond)
67
John Snow4ed3fe02024-03-15 16:22:57 +010068 def is_present(self) -> bool:
Marc-André Lureau33aa3262021-08-04 12:30:58 +040069 return bool(self.ifcond)
70
Marc-André Lureauf17539c2021-08-04 12:30:57 +040071
Markus Armbrusterbaa310f2020-03-04 16:59:29 +010072class QAPISchemaEntity:
John Snow2418d1c2024-03-15 16:22:41 +010073 """
74 A schema entity.
Markus Armbrustere6c42b92019-10-18 09:43:44 +020075
John Snow2418d1c2024-03-15 16:22:41 +010076 This is either a directive, such as include, or a definition.
77 The latter uses sub-class `QAPISchemaDefinition`.
78 """
John Snow4ed3fe02024-03-15 16:22:57 +010079 def __init__(self, info: Optional[QAPISourceInfo]):
80 self._module: Optional[QAPISchemaModule] = None
Markus Armbrustere6c42b92019-10-18 09:43:44 +020081 # For explicitly defined entities, info points to the (explicit)
82 # definition. For builtins (and their arrays), info is None.
83 # For implicitly defined entities, info points to a place that
84 # triggered the implicit definition (there may be more than one
85 # such place).
86 self.info = info
Markus Armbrustere6c42b92019-10-18 09:43:44 +020087 self._checked = False
88
John Snow4ed3fe02024-03-15 16:22:57 +010089 def __repr__(self) -> str:
John Snow2418d1c2024-03-15 16:22:41 +010090 return "<%s at 0x%x>" % (type(self).__name__, id(self))
Markus Armbrustere6c42b92019-10-18 09:43:44 +020091
John Snow4ed3fe02024-03-15 16:22:57 +010092 def check(self, schema: QAPISchema) -> None:
John Snowce7fde02024-03-15 16:22:40 +010093 # pylint: disable=unused-argument
Markus Armbrustere6c42b92019-10-18 09:43:44 +020094 self._checked = True
95
John Snow4ed3fe02024-03-15 16:22:57 +010096 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
John Snow2418d1c2024-03-15 16:22:41 +010097 pass
Markus Armbrusteree1e6a12019-10-24 13:02:26 +020098
John Snow4ed3fe02024-03-15 16:22:57 +010099 def _set_module(
100 self, schema: QAPISchema, info: Optional[QAPISourceInfo]
101 ) -> None:
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100102 assert self._checked
John Snow39b2d832021-02-01 14:37:41 -0500103 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
John Snowe2bbc4e2021-02-01 14:37:39 -0500104 self._module = schema.module_by_fname(fname)
Markus Armbruster3e7fb582019-11-20 19:25:50 +0100105 self._module.add_entity(self)
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100106
John Snow4ed3fe02024-03-15 16:22:57 +0100107 def set_module(self, schema: QAPISchema) -> None:
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100108 self._set_module(schema, self.info)
109
John Snow4ed3fe02024-03-15 16:22:57 +0100110 def visit(self, visitor: QAPISchemaVisitor) -> None:
John Snow2418d1c2024-03-15 16:22:41 +0100111 # pylint: disable=unused-argument
112 assert self._checked
113
114
115class QAPISchemaDefinition(QAPISchemaEntity):
John Snow4ed3fe02024-03-15 16:22:57 +0100116 meta: str
John Snow2418d1c2024-03-15 16:22:41 +0100117
John Snow4ed3fe02024-03-15 16:22:57 +0100118 def __init__(
119 self,
120 name: str,
121 info: Optional[QAPISourceInfo],
122 doc: Optional[QAPIDoc],
123 ifcond: Optional[QAPISchemaIfCond] = None,
124 features: Optional[List[QAPISchemaFeature]] = None,
125 ):
John Snow2418d1c2024-03-15 16:22:41 +0100126 super().__init__(info)
127 for f in features or []:
John Snow2418d1c2024-03-15 16:22:41 +0100128 f.set_defined_in(name)
129 self.name = name
130 self.doc = doc
131 self._ifcond = ifcond or QAPISchemaIfCond()
132 self.features = features or []
133
John Snow4ed3fe02024-03-15 16:22:57 +0100134 def __repr__(self) -> str:
John Snow2418d1c2024-03-15 16:22:41 +0100135 return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
136 id(self))
137
John Snow4ed3fe02024-03-15 16:22:57 +0100138 def c_name(self) -> str:
John Snow2418d1c2024-03-15 16:22:41 +0100139 return c_name(self.name)
140
John Snow4ed3fe02024-03-15 16:22:57 +0100141 def check(self, schema: QAPISchema) -> None:
John Snow2418d1c2024-03-15 16:22:41 +0100142 assert not self._checked
143 super().check(schema)
John Snow4ed3fe02024-03-15 16:22:57 +0100144 seen: Dict[str, QAPISchemaMember] = {}
John Snow2418d1c2024-03-15 16:22:41 +0100145 for f in self.features:
146 f.check_clash(self.info, seen)
147
John Snow4ed3fe02024-03-15 16:22:57 +0100148 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
John Snow2418d1c2024-03-15 16:22:41 +0100149 super().connect_doc(doc)
150 doc = doc or self.doc
151 if doc:
152 for f in self.features:
153 doc.connect_feature(f)
154
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200155 @property
John Snow4ed3fe02024-03-15 16:22:57 +0100156 def ifcond(self) -> QAPISchemaIfCond:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200157 assert self._checked
158 return self._ifcond
159
John Snow4ed3fe02024-03-15 16:22:57 +0100160 def is_implicit(self) -> bool:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200161 return not self.info
162
John Snow4ed3fe02024-03-15 16:22:57 +0100163 def describe(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200164 return "%s '%s'" % (self.meta, self.name)
165
166
Markus Armbrusterbaa310f2020-03-04 16:59:29 +0100167class QAPISchemaVisitor:
John Snow4ed3fe02024-03-15 16:22:57 +0100168 def visit_begin(self, schema: QAPISchema) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200169 pass
170
John Snow4ed3fe02024-03-15 16:22:57 +0100171 def visit_end(self) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200172 pass
173
John Snow4ed3fe02024-03-15 16:22:57 +0100174 def visit_module(self, name: str) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200175 pass
176
John Snow4ed3fe02024-03-15 16:22:57 +0100177 def visit_needed(self, entity: QAPISchemaEntity) -> bool:
John Snowce7fde02024-03-15 16:22:40 +0100178 # pylint: disable=unused-argument
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200179 # Default to visiting everything
180 return True
181
John Snow4ed3fe02024-03-15 16:22:57 +0100182 def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200183 pass
184
John Snow4ed3fe02024-03-15 16:22:57 +0100185 def visit_builtin_type(
186 self, name: str, info: Optional[QAPISourceInfo], json_type: str
187 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200188 pass
189
John Snow4ed3fe02024-03-15 16:22:57 +0100190 def visit_enum_type(
191 self,
192 name: str,
193 info: Optional[QAPISourceInfo],
194 ifcond: QAPISchemaIfCond,
195 features: List[QAPISchemaFeature],
196 members: List[QAPISchemaEnumMember],
197 prefix: Optional[str],
198 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200199 pass
200
John Snow4ed3fe02024-03-15 16:22:57 +0100201 def visit_array_type(
202 self,
203 name: str,
204 info: Optional[QAPISourceInfo],
205 ifcond: QAPISchemaIfCond,
206 element_type: QAPISchemaType,
207 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200208 pass
209
John Snow4ed3fe02024-03-15 16:22:57 +0100210 def visit_object_type(
211 self,
212 name: str,
213 info: Optional[QAPISourceInfo],
214 ifcond: QAPISchemaIfCond,
215 features: List[QAPISchemaFeature],
216 base: Optional[QAPISchemaObjectType],
217 members: List[QAPISchemaObjectTypeMember],
Markus Armbrusterd1da8af2024-03-15 16:28:22 +0100218 branches: Optional[QAPISchemaBranches],
John Snow4ed3fe02024-03-15 16:22:57 +0100219 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200220 pass
221
John Snow4ed3fe02024-03-15 16:22:57 +0100222 def visit_object_type_flat(
223 self,
224 name: str,
225 info: Optional[QAPISourceInfo],
226 ifcond: QAPISchemaIfCond,
227 features: List[QAPISchemaFeature],
228 members: List[QAPISchemaObjectTypeMember],
Markus Armbrusterd1da8af2024-03-15 16:28:22 +0100229 branches: Optional[QAPISchemaBranches],
John Snow4ed3fe02024-03-15 16:22:57 +0100230 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200231 pass
232
John Snow4ed3fe02024-03-15 16:22:57 +0100233 def visit_alternate_type(
234 self,
235 name: str,
236 info: Optional[QAPISourceInfo],
237 ifcond: QAPISchemaIfCond,
238 features: List[QAPISchemaFeature],
Markus Armbruster41d0ad12024-03-16 07:43:36 +0100239 alternatives: QAPISchemaAlternatives,
John Snow4ed3fe02024-03-15 16:22:57 +0100240 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200241 pass
242
John Snow4ed3fe02024-03-15 16:22:57 +0100243 def visit_command(
244 self,
245 name: str,
246 info: Optional[QAPISourceInfo],
247 ifcond: QAPISchemaIfCond,
248 features: List[QAPISchemaFeature],
249 arg_type: Optional[QAPISchemaObjectType],
250 ret_type: Optional[QAPISchemaType],
251 gen: bool,
252 success_response: bool,
253 boxed: bool,
254 allow_oob: bool,
255 allow_preconfig: bool,
256 coroutine: bool,
257 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200258 pass
259
John Snow4ed3fe02024-03-15 16:22:57 +0100260 def visit_event(
261 self,
262 name: str,
263 info: Optional[QAPISourceInfo],
264 ifcond: QAPISchemaIfCond,
265 features: List[QAPISchemaFeature],
266 arg_type: Optional[QAPISchemaObjectType],
267 boxed: bool,
268 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200269 pass
270
271
Markus Armbrusterbaa310f2020-03-04 16:59:29 +0100272class QAPISchemaModule:
John Snow39b2d832021-02-01 14:37:41 -0500273
274 BUILTIN_MODULE_NAME = './builtin'
275
John Snow4ed3fe02024-03-15 16:22:57 +0100276 def __init__(self, name: str):
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100277 self.name = name
John Snow4ed3fe02024-03-15 16:22:57 +0100278 self._entity_list: List[QAPISchemaEntity] = []
Markus Armbruster3e7fb582019-11-20 19:25:50 +0100279
John Snow98967c22021-02-01 14:37:36 -0500280 @staticmethod
John Snowe2bbc4e2021-02-01 14:37:39 -0500281 def is_system_module(name: str) -> bool:
John Snow98967c22021-02-01 14:37:36 -0500282 """
283 System modules are internally defined modules.
284
285 Their names start with the "./" prefix.
286 """
John Snowe2bbc4e2021-02-01 14:37:39 -0500287 return name.startswith('./')
John Snow98967c22021-02-01 14:37:36 -0500288
289 @classmethod
John Snowe2bbc4e2021-02-01 14:37:39 -0500290 def is_user_module(cls, name: str) -> bool:
John Snow98967c22021-02-01 14:37:36 -0500291 """
292 User modules are those defined by the user in qapi JSON files.
293
294 They do not start with the "./" prefix.
295 """
296 return not cls.is_system_module(name)
297
John Snow39b2d832021-02-01 14:37:41 -0500298 @classmethod
299 def is_builtin_module(cls, name: str) -> bool:
John Snow98967c22021-02-01 14:37:36 -0500300 """
301 The built-in module is a single System module for the built-in types.
302
John Snowe2bbc4e2021-02-01 14:37:39 -0500303 It is always "./builtin".
John Snow98967c22021-02-01 14:37:36 -0500304 """
John Snow39b2d832021-02-01 14:37:41 -0500305 return name == cls.BUILTIN_MODULE_NAME
John Snow98967c22021-02-01 14:37:36 -0500306
John Snow4ed3fe02024-03-15 16:22:57 +0100307 def add_entity(self, ent: QAPISchemaEntity) -> None:
Markus Armbruster3e7fb582019-11-20 19:25:50 +0100308 self._entity_list.append(ent)
309
John Snow4ed3fe02024-03-15 16:22:57 +0100310 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster3e7fb582019-11-20 19:25:50 +0100311 visitor.visit_module(self.name)
312 for entity in self._entity_list:
313 if visitor.visit_needed(entity):
314 entity.visit(visitor)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200315
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100316
317class QAPISchemaInclude(QAPISchemaEntity):
John Snow4ed3fe02024-03-15 16:22:57 +0100318 def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo):
John Snow2418d1c2024-03-15 16:22:41 +0100319 super().__init__(info)
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100320 self._sub_module = sub_module
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200321
John Snow4ed3fe02024-03-15 16:22:57 +0100322 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100323 super().visit(visitor)
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100324 visitor.visit_include(self._sub_module.name, self.info)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200325
326
John Snowd150be32024-03-15 16:22:44 +0100327class QAPISchemaType(QAPISchemaDefinition, ABC):
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200328 # Return the C type for common use.
329 # For the types we commonly box, this is a pointer type.
John Snowd150be32024-03-15 16:22:44 +0100330 @abstractmethod
John Snow4ed3fe02024-03-15 16:22:57 +0100331 def c_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200332 pass
333
334 # Return the C type to be used in a parameter list.
John Snow4ed3fe02024-03-15 16:22:57 +0100335 def c_param_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200336 return self.c_type()
337
338 # Return the C type to be used where we suppress boxing.
John Snow4ed3fe02024-03-15 16:22:57 +0100339 def c_unboxed_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200340 return self.c_type()
341
John Snowd150be32024-03-15 16:22:44 +0100342 @abstractmethod
John Snow4ed3fe02024-03-15 16:22:57 +0100343 def json_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200344 pass
345
John Snow4ed3fe02024-03-15 16:22:57 +0100346 def alternate_qtype(self) -> Optional[str]:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200347 json2qtype = {
348 'null': 'QTYPE_QNULL',
349 'string': 'QTYPE_QSTRING',
350 'number': 'QTYPE_QNUM',
351 'int': 'QTYPE_QNUM',
352 'boolean': 'QTYPE_QBOOL',
Paolo Bonzinia5806942022-03-21 17:42:41 +0100353 'array': 'QTYPE_QLIST',
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200354 'object': 'QTYPE_QDICT'
355 }
356 return json2qtype.get(self.json_type())
357
John Snow4ed3fe02024-03-15 16:22:57 +0100358 def doc_type(self) -> Optional[str]:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200359 if self.is_implicit():
360 return None
361 return self.name
362
John Snow4ed3fe02024-03-15 16:22:57 +0100363 def need_has_if_optional(self) -> bool:
Markus Armbruster44ea9d92022-11-04 17:06:46 +0100364 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
365 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
366 return not self.c_type().endswith(POINTER_SUFFIX)
367
John Snow4ed3fe02024-03-15 16:22:57 +0100368 def check(self, schema: QAPISchema) -> None:
Markus Armbrusterecee5682023-03-16 08:13:13 +0100369 super().check(schema)
Markus Armbruster57df0df2021-10-28 12:25:20 +0200370 for feat in self.features:
371 if feat.is_special():
372 raise QAPISemError(
373 self.info,
374 f"feature '{feat.name}' is not supported for types")
Markus Armbrusterf965e8f2020-03-17 12:54:50 +0100375
John Snow4ed3fe02024-03-15 16:22:57 +0100376 def describe(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200377 return "%s type '%s'" % (self.meta, self.name)
378
379
380class QAPISchemaBuiltinType(QAPISchemaType):
381 meta = 'built-in'
382
John Snow4ed3fe02024-03-15 16:22:57 +0100383 def __init__(self, name: str, json_type: str, c_type: str):
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100384 super().__init__(name, None, None)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200385 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
386 'value')
387 self._json_type_name = json_type
388 self._c_type_name = c_type
389
John Snow4ed3fe02024-03-15 16:22:57 +0100390 def c_name(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200391 return self.name
392
John Snow4ed3fe02024-03-15 16:22:57 +0100393 def c_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200394 return self._c_type_name
395
John Snow4ed3fe02024-03-15 16:22:57 +0100396 def c_param_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200397 if self.name == 'str':
398 return 'const ' + self._c_type_name
399 return self._c_type_name
400
John Snow4ed3fe02024-03-15 16:22:57 +0100401 def json_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200402 return self._json_type_name
403
John Snow4ed3fe02024-03-15 16:22:57 +0100404 def doc_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200405 return self.json_type()
406
John Snow4ed3fe02024-03-15 16:22:57 +0100407 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100408 super().visit(visitor)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200409 visitor.visit_builtin_type(self.name, self.info, self.json_type())
410
411
412class QAPISchemaEnumType(QAPISchemaType):
413 meta = 'enum'
414
John Snow4ed3fe02024-03-15 16:22:57 +0100415 def __init__(
416 self,
417 name: str,
418 info: Optional[QAPISourceInfo],
419 doc: Optional[QAPIDoc],
420 ifcond: Optional[QAPISchemaIfCond],
421 features: Optional[List[QAPISchemaFeature]],
422 members: List[QAPISchemaEnumMember],
423 prefix: Optional[str],
424 ):
Markus Armbruster013b4ef2020-03-17 12:54:37 +0100425 super().__init__(name, info, doc, ifcond, features)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200426 for m in members:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200427 m.set_defined_in(name)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200428 self.members = members
429 self.prefix = prefix
430
John Snow4ed3fe02024-03-15 16:22:57 +0100431 def check(self, schema: QAPISchema) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100432 super().check(schema)
John Snow4ed3fe02024-03-15 16:22:57 +0100433 seen: Dict[str, QAPISchemaMember] = {}
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200434 for m in self.members:
435 m.check_clash(self.info, seen)
Markus Armbrusteree1e6a12019-10-24 13:02:26 +0200436
John Snow4ed3fe02024-03-15 16:22:57 +0100437 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
Markus Armbrustere4405b32020-03-17 12:54:36 +0100438 super().connect_doc(doc)
Markus Armbruster7faefad2019-10-24 13:02:28 +0200439 doc = doc or self.doc
Markus Armbruster645178c2020-03-17 12:54:44 +0100440 for m in self.members:
441 m.connect_doc(doc)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200442
John Snow4ed3fe02024-03-15 16:22:57 +0100443 def is_implicit(self) -> bool:
Markus Armbruster4e99f4b2021-09-17 16:31:32 +0200444 # See QAPISchema._def_predefineds()
445 return self.name == 'QType'
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200446
John Snow4ed3fe02024-03-15 16:22:57 +0100447 def c_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200448 return c_name(self.name)
449
John Snow4ed3fe02024-03-15 16:22:57 +0100450 def member_names(self) -> List[str]:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200451 return [m.name for m in self.members]
452
John Snow4ed3fe02024-03-15 16:22:57 +0100453 def json_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200454 return 'string'
455
John Snow4ed3fe02024-03-15 16:22:57 +0100456 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100457 super().visit(visitor)
Markus Armbruster013b4ef2020-03-17 12:54:37 +0100458 visitor.visit_enum_type(
459 self.name, self.info, self.ifcond, self.features,
460 self.members, self.prefix)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200461
462
463class QAPISchemaArrayType(QAPISchemaType):
464 meta = 'array'
465
John Snow4ed3fe02024-03-15 16:22:57 +0100466 def __init__(
467 self, name: str, info: Optional[QAPISourceInfo], element_type: str
468 ):
Markus Armbruster013b4ef2020-03-17 12:54:37 +0100469 super().__init__(name, info, None)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200470 self._element_type_name = element_type
John Snow578cd932024-03-15 16:22:43 +0100471 self.element_type: QAPISchemaType
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200472
John Snow4ed3fe02024-03-15 16:22:57 +0100473 def need_has_if_optional(self) -> bool:
Markus Armbruster44ea9d92022-11-04 17:06:46 +0100474 # When FOO is an array, we still need has_FOO to distinguish
475 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
476 return True
477
John Snow4ed3fe02024-03-15 16:22:57 +0100478 def check(self, schema: QAPISchema) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100479 super().check(schema)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200480 self.element_type = schema.resolve_type(
481 self._element_type_name, self.info,
John Snow8c913292024-03-15 16:22:49 +0100482 self.info.defn_meta if self.info else None)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200483 assert not isinstance(self.element_type, QAPISchemaArrayType)
484
John Snow4ed3fe02024-03-15 16:22:57 +0100485 def set_module(self, schema: QAPISchema) -> None:
Markus Armbrustera9f1dd72019-11-20 19:25:49 +0100486 self._set_module(schema, self.element_type.info)
487
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200488 @property
John Snow4ed3fe02024-03-15 16:22:57 +0100489 def ifcond(self) -> QAPISchemaIfCond:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200490 assert self._checked
491 return self.element_type.ifcond
492
John Snow4ed3fe02024-03-15 16:22:57 +0100493 def is_implicit(self) -> bool:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200494 return True
495
John Snow4ed3fe02024-03-15 16:22:57 +0100496 def c_type(self) -> str:
John Snowa7aa64a2020-10-09 12:15:34 -0400497 return c_name(self.name) + POINTER_SUFFIX
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200498
John Snow4ed3fe02024-03-15 16:22:57 +0100499 def json_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200500 return 'array'
501
John Snow4ed3fe02024-03-15 16:22:57 +0100502 def doc_type(self) -> Optional[str]:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200503 elt_doc_type = self.element_type.doc_type()
504 if not elt_doc_type:
505 return None
506 return 'array of ' + elt_doc_type
507
John Snow4ed3fe02024-03-15 16:22:57 +0100508 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100509 super().visit(visitor)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200510 visitor.visit_array_type(self.name, self.info, self.ifcond,
511 self.element_type)
512
John Snow4ed3fe02024-03-15 16:22:57 +0100513 def describe(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200514 return "%s type ['%s']" % (self.meta, self._element_type_name)
515
516
517class QAPISchemaObjectType(QAPISchemaType):
John Snow4ed3fe02024-03-15 16:22:57 +0100518 def __init__(
519 self,
520 name: str,
521 info: Optional[QAPISourceInfo],
522 doc: Optional[QAPIDoc],
523 ifcond: Optional[QAPISchemaIfCond],
524 features: Optional[List[QAPISchemaFeature]],
525 base: Optional[str],
526 local_members: List[QAPISchemaObjectTypeMember],
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100527 branches: Optional[QAPISchemaBranches],
John Snow4ed3fe02024-03-15 16:22:57 +0100528 ):
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100529 # struct has local_members, optional base, and no branches
530 # union has base, branches, and no local_members
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100531 super().__init__(name, info, doc, ifcond, features)
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100532 self.meta = 'union' if branches else 'struct'
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200533 for m in local_members:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200534 m.set_defined_in(name)
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100535 if branches is not None:
536 branches.set_defined_in(name)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200537 self._base_name = base
538 self.base = None
539 self.local_members = local_members
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100540 self.branches = branches
John Snow9beda222024-03-15 16:22:52 +0100541 self.members: List[QAPISchemaObjectTypeMember]
John Snow875f6242024-03-15 16:22:51 +0100542 self._check_complete = False
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200543
John Snow4ed3fe02024-03-15 16:22:57 +0100544 def check(self, schema: QAPISchema) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200545 # This calls another type T's .check() exactly when the C
546 # struct emitted by gen_object() contains that T's C struct
547 # (pointers don't count).
John Snow875f6242024-03-15 16:22:51 +0100548 if self._check_complete:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200549 # A previous .check() completed: nothing to do
550 return
551 if self._checked:
552 # Recursed: C struct contains itself
553 raise QAPISemError(self.info,
554 "object %s contains itself" % self.name)
555
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100556 super().check(schema)
John Snow875f6242024-03-15 16:22:51 +0100557 assert self._checked and not self._check_complete
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200558
559 seen = OrderedDict()
560 if self._base_name:
561 self.base = schema.resolve_type(self._base_name, self.info,
562 "'base'")
563 if (not isinstance(self.base, QAPISchemaObjectType)
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100564 or self.base.branches):
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200565 raise QAPISemError(
566 self.info,
567 "'base' requires a struct type, %s isn't"
568 % self.base.describe())
569 self.base.check(schema)
570 self.base.check_clash(self.info, seen)
571 for m in self.local_members:
572 m.check(schema)
573 m.check_clash(self.info, seen)
John Snow9beda222024-03-15 16:22:52 +0100574
575 # self.check_clash() works in terms of the supertype, but
576 # self.members is declared List[QAPISchemaObjectTypeMember].
577 # Cast down to the subtype.
578 members = cast(List[QAPISchemaObjectTypeMember], list(seen.values()))
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200579
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100580 if self.branches:
581 self.branches.check(schema, seen)
582 self.branches.check_clash(self.info, seen)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200583
John Snow875f6242024-03-15 16:22:51 +0100584 self.members = members
585 self._check_complete = True # mark completed
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200586
587 # Check that the members of this type do not cause duplicate JSON members,
588 # and update seen to track the members seen so far. Report any errors
589 # on behalf of info, which is not necessarily self.info
John Snow4ed3fe02024-03-15 16:22:57 +0100590 def check_clash(
591 self,
592 info: Optional[QAPISourceInfo],
593 seen: Dict[str, QAPISchemaMember],
594 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200595 assert self._checked
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200596 for m in self.members:
597 m.check_clash(info, seen)
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100598 if self.branches:
599 self.branches.check_clash(info, seen)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200600
John Snow4ed3fe02024-03-15 16:22:57 +0100601 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
Markus Armbrustere4405b32020-03-17 12:54:36 +0100602 super().connect_doc(doc)
Markus Armbruster7faefad2019-10-24 13:02:28 +0200603 doc = doc or self.doc
Markus Armbruster645178c2020-03-17 12:54:44 +0100604 if self.base and self.base.is_implicit():
605 self.base.connect_doc(doc)
606 for m in self.local_members:
607 m.connect_doc(doc)
Markus Armbrusteree1e6a12019-10-24 13:02:26 +0200608
John Snow4ed3fe02024-03-15 16:22:57 +0100609 def is_implicit(self) -> bool:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200610 # See QAPISchema._make_implicit_object_type(), as well as
611 # _def_predefineds()
612 return self.name.startswith('q_')
613
John Snow4ed3fe02024-03-15 16:22:57 +0100614 def is_empty(self) -> bool:
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100615 return not self.members and not self.branches
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200616
John Snow4ed3fe02024-03-15 16:22:57 +0100617 def has_conditional_members(self) -> bool:
Markus Armbrusterde3b3f52023-03-16 08:13:25 +0100618 return any(m.ifcond.is_present() for m in self.members)
619
John Snow4ed3fe02024-03-15 16:22:57 +0100620 def c_name(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200621 assert self.name != 'q_empty'
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100622 return super().c_name()
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200623
John Snow4ed3fe02024-03-15 16:22:57 +0100624 def c_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200625 assert not self.is_implicit()
John Snowa7aa64a2020-10-09 12:15:34 -0400626 return c_name(self.name) + POINTER_SUFFIX
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200627
John Snow4ed3fe02024-03-15 16:22:57 +0100628 def c_unboxed_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200629 return c_name(self.name)
630
John Snow4ed3fe02024-03-15 16:22:57 +0100631 def json_type(self) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200632 return 'object'
633
John Snow4ed3fe02024-03-15 16:22:57 +0100634 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100635 super().visit(visitor)
Markus Armbruster7b3bc9e2020-03-17 12:54:38 +0100636 visitor.visit_object_type(
637 self.name, self.info, self.ifcond, self.features,
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100638 self.base, self.local_members, self.branches)
Markus Armbruster7b3bc9e2020-03-17 12:54:38 +0100639 visitor.visit_object_type_flat(
640 self.name, self.info, self.ifcond, self.features,
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +0100641 self.members, self.branches)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200642
643
Markus Armbruster226b5be2020-03-17 12:54:42 +0100644class QAPISchemaAlternateType(QAPISchemaType):
645 meta = 'alternate'
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200646
John Snow4ed3fe02024-03-15 16:22:57 +0100647 def __init__(
648 self,
649 name: str,
650 info: QAPISourceInfo,
651 doc: Optional[QAPIDoc],
652 ifcond: Optional[QAPISchemaIfCond],
653 features: List[QAPISchemaFeature],
Markus Armbrustere0a28f32024-03-15 16:36:02 +0100654 alternatives: QAPISchemaAlternatives,
John Snow4ed3fe02024-03-15 16:22:57 +0100655 ):
Markus Armbruster226b5be2020-03-17 12:54:42 +0100656 super().__init__(name, info, doc, ifcond, features)
Markus Armbrustere0a28f32024-03-15 16:36:02 +0100657 assert alternatives.tag_member
658 alternatives.set_defined_in(name)
659 alternatives.tag_member.set_defined_in(self.name)
660 self.alternatives = alternatives
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200661
John Snow4ed3fe02024-03-15 16:22:57 +0100662 def check(self, schema: QAPISchema) -> None:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100663 super().check(schema)
Markus Armbrustere0a28f32024-03-15 16:36:02 +0100664 self.alternatives.tag_member.check(schema)
665 # Not calling self.alternatives.check_clash(), because there's
666 # nothing to clash with
667 self.alternatives.check(schema, {})
Markus Armbruster226b5be2020-03-17 12:54:42 +0100668 # Alternate branch names have no relation to the tag enum values;
669 # so we have to check for potential name collisions ourselves.
John Snow4ed3fe02024-03-15 16:22:57 +0100670 seen: Dict[str, QAPISchemaMember] = {}
671 types_seen: Dict[str, str] = {}
Markus Armbrustere0a28f32024-03-15 16:36:02 +0100672 for v in self.alternatives.variants:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100673 v.check_clash(self.info, seen)
674 qtype = v.type.alternate_qtype()
675 if not qtype:
676 raise QAPISemError(
677 self.info,
678 "%s cannot use %s"
679 % (v.describe(self.info), v.type.describe()))
680 conflicting = set([qtype])
681 if qtype == 'QTYPE_QSTRING':
682 if isinstance(v.type, QAPISchemaEnumType):
683 for m in v.type.members:
684 if m.name in ['on', 'off']:
685 conflicting.add('QTYPE_QBOOL')
686 if re.match(r'[-+0-9.]', m.name):
687 # lazy, could be tightened
688 conflicting.add('QTYPE_QNUM')
689 else:
690 conflicting.add('QTYPE_QNUM')
691 conflicting.add('QTYPE_QBOOL')
692 for qt in conflicting:
693 if qt in types_seen:
694 raise QAPISemError(
695 self.info,
696 "%s can't be distinguished from '%s'"
697 % (v.describe(self.info), types_seen[qt]))
698 types_seen[qt] = v.name
699
John Snow4ed3fe02024-03-15 16:22:57 +0100700 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100701 super().connect_doc(doc)
702 doc = doc or self.doc
Markus Armbrustere0a28f32024-03-15 16:36:02 +0100703 for v in self.alternatives.variants:
Markus Armbruster645178c2020-03-17 12:54:44 +0100704 v.connect_doc(doc)
Markus Armbruster226b5be2020-03-17 12:54:42 +0100705
John Snow4ed3fe02024-03-15 16:22:57 +0100706 def c_type(self) -> str:
John Snowa7aa64a2020-10-09 12:15:34 -0400707 return c_name(self.name) + POINTER_SUFFIX
Markus Armbruster226b5be2020-03-17 12:54:42 +0100708
John Snow4ed3fe02024-03-15 16:22:57 +0100709 def json_type(self) -> str:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100710 return 'value'
711
John Snow4ed3fe02024-03-15 16:22:57 +0100712 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100713 super().visit(visitor)
714 visitor.visit_alternate_type(
Markus Armbrustere0a28f32024-03-15 16:36:02 +0100715 self.name, self.info, self.ifcond, self.features,
716 self.alternatives)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200717
718
Markus Armbruster5858fd12020-03-17 12:54:43 +0100719class QAPISchemaVariants:
John Snow4ed3fe02024-03-15 16:22:57 +0100720 def __init__(
721 self,
John Snow4ed3fe02024-03-15 16:22:57 +0100722 info: QAPISourceInfo,
John Snow4ed3fe02024-03-15 16:22:57 +0100723 variants: List[QAPISchemaVariant],
724 ):
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200725 self.info = info
Markus Armbruster285a8f22024-03-16 08:46:12 +0100726 self.tag_member: QAPISchemaObjectTypeMember
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200727 self.variants = variants
728
John Snow4ed3fe02024-03-15 16:22:57 +0100729 def set_defined_in(self, name: str) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200730 for v in self.variants:
731 v.set_defined_in(name)
732
John Snowf64e7532024-06-26 18:21:08 -0400733 # pylint: disable=unused-argument
John Snow4ed3fe02024-03-15 16:22:57 +0100734 def check(
Markus Armbruster8152bc72024-03-15 20:57:56 +0100735 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
John Snow4ed3fe02024-03-15 16:22:57 +0100736 ) -> None:
Markus Armbruster8152bc72024-03-15 20:57:56 +0100737 for v in self.variants:
738 v.check(schema)
John Snow583f4d62024-03-15 16:22:53 +0100739
Markus Armbruster8152bc72024-03-15 20:57:56 +0100740
741class QAPISchemaBranches(QAPISchemaVariants):
742 def __init__(self,
743 info: QAPISourceInfo,
744 variants: List[QAPISchemaVariant],
745 tag_name: str):
746 super().__init__(info, variants)
747 self._tag_name = tag_name
748
749 def check(
750 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
751 ) -> None:
752 # We need to narrow the member type:
Markus Armbruster285a8f22024-03-16 08:46:12 +0100753 tag_member = seen.get(c_name(self._tag_name))
754 assert (tag_member is None
755 or isinstance(tag_member, QAPISchemaObjectTypeMember))
Markus Armbruster8152bc72024-03-15 20:57:56 +0100756
757 base = "'base'"
758 # Pointing to the base type when not implicit would be
759 # nice, but we don't know it here
Markus Armbruster285a8f22024-03-16 08:46:12 +0100760 if not tag_member or self._tag_name != tag_member.name:
Markus Armbruster8152bc72024-03-15 20:57:56 +0100761 raise QAPISemError(
762 self.info,
763 "discriminator '%s' is not a member of %s"
764 % (self._tag_name, base))
Markus Armbruster285a8f22024-03-16 08:46:12 +0100765 self.tag_member = tag_member
Markus Armbruster8152bc72024-03-15 20:57:56 +0100766 # Here we do:
Markus Armbruster285a8f22024-03-16 08:46:12 +0100767 assert tag_member.defined_in
768 base_type = schema.lookup_type(tag_member.defined_in)
Markus Armbruster8152bc72024-03-15 20:57:56 +0100769 assert base_type
770 if not base_type.is_implicit():
Markus Armbruster285a8f22024-03-16 08:46:12 +0100771 base = "base type '%s'" % tag_member.defined_in
772 if not isinstance(tag_member.type, QAPISchemaEnumType):
Markus Armbruster8152bc72024-03-15 20:57:56 +0100773 raise QAPISemError(
774 self.info,
775 "discriminator member '%s' of %s must be of enum type"
776 % (self._tag_name, base))
Markus Armbruster285a8f22024-03-16 08:46:12 +0100777 if tag_member.optional:
Markus Armbruster8152bc72024-03-15 20:57:56 +0100778 raise QAPISemError(
779 self.info,
780 "discriminator member '%s' of %s must not be optional"
781 % (self._tag_name, base))
Markus Armbruster285a8f22024-03-16 08:46:12 +0100782 if tag_member.ifcond.is_present():
Markus Armbruster8152bc72024-03-15 20:57:56 +0100783 raise QAPISemError(
784 self.info,
785 "discriminator member '%s' of %s must not be conditional"
786 % (self._tag_name, base))
787 # branches that are not explicitly covered get an empty type
Markus Armbruster285a8f22024-03-16 08:46:12 +0100788 assert tag_member.defined_in
Markus Armbruster8152bc72024-03-15 20:57:56 +0100789 cases = {v.name for v in self.variants}
Markus Armbruster285a8f22024-03-16 08:46:12 +0100790 for m in tag_member.type.members:
Markus Armbruster8152bc72024-03-15 20:57:56 +0100791 if m.name not in cases:
792 v = QAPISchemaVariant(m.name, self.info,
793 'q_empty', m.ifcond)
Markus Armbruster285a8f22024-03-16 08:46:12 +0100794 v.set_defined_in(tag_member.defined_in)
Markus Armbruster8152bc72024-03-15 20:57:56 +0100795 self.variants.append(v)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200796 if not self.variants:
797 raise QAPISemError(self.info, "union has no branches")
798 for v in self.variants:
799 v.check(schema)
800 # Union names must match enum values; alternate names are
801 # checked separately. Use 'seen' to tell the two apart.
802 if seen:
Markus Armbruster285a8f22024-03-16 08:46:12 +0100803 if v.name not in tag_member.type.member_names():
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200804 raise QAPISemError(
805 self.info,
806 "branch '%s' is not a value of %s"
Markus Armbruster285a8f22024-03-16 08:46:12 +0100807 % (v.name, tag_member.type.describe()))
Daniel P. Berrangéa17dbc42023-04-20 11:26:19 +0100808 if not isinstance(v.type, QAPISchemaObjectType):
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200809 raise QAPISemError(
810 self.info,
811 "%s cannot use %s"
812 % (v.describe(self.info), v.type.describe()))
813 v.type.check(schema)
814
John Snow4ed3fe02024-03-15 16:22:57 +0100815 def check_clash(
816 self,
817 info: Optional[QAPISourceInfo],
818 seen: Dict[str, QAPISchemaMember],
819 ) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200820 for v in self.variants:
821 # Reset seen map for each variant, since qapi names from one
John Snow7e09dd62024-03-15 16:22:54 +0100822 # branch do not affect another branch.
823 #
824 # v.type's typing is enforced in check() above.
825 assert isinstance(v.type, QAPISchemaObjectType)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200826 v.type.check_clash(info, dict(seen))
827
828
Markus Armbruster1d067e32024-03-15 20:32:41 +0100829class QAPISchemaAlternatives(QAPISchemaVariants):
830 def __init__(self,
831 info: QAPISourceInfo,
832 variants: List[QAPISchemaVariant],
833 tag_member: QAPISchemaObjectTypeMember):
Markus Armbruster8152bc72024-03-15 20:57:56 +0100834 super().__init__(info, variants)
Markus Armbruster285a8f22024-03-16 08:46:12 +0100835 self.tag_member = tag_member
Markus Armbruster8152bc72024-03-15 20:57:56 +0100836
837 def check(
838 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
839 ) -> None:
840 super().check(schema, seen)
841 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
842 assert not self.tag_member.optional
843 assert not self.tag_member.ifcond.is_present()
Markus Armbruster1d067e32024-03-15 20:32:41 +0100844
845
Markus Armbruster226b5be2020-03-17 12:54:42 +0100846class QAPISchemaMember:
847 """ Represents object members, enum members and features """
848 role = 'member'
849
John Snow4ed3fe02024-03-15 16:22:57 +0100850 def __init__(
851 self,
852 name: str,
853 info: Optional[QAPISourceInfo],
854 ifcond: Optional[QAPISchemaIfCond] = None,
855 ):
Markus Armbruster226b5be2020-03-17 12:54:42 +0100856 self.name = name
857 self.info = info
Marc-André Lureauf17539c2021-08-04 12:30:57 +0400858 self.ifcond = ifcond or QAPISchemaIfCond()
John Snow4ed3fe02024-03-15 16:22:57 +0100859 self.defined_in: Optional[str] = None
Markus Armbruster226b5be2020-03-17 12:54:42 +0100860
John Snow4ed3fe02024-03-15 16:22:57 +0100861 def set_defined_in(self, name: str) -> None:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100862 assert not self.defined_in
863 self.defined_in = name
864
John Snow4ed3fe02024-03-15 16:22:57 +0100865 def check_clash(
866 self,
867 info: Optional[QAPISourceInfo],
868 seen: Dict[str, QAPISchemaMember],
869 ) -> None:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100870 cname = c_name(self.name)
871 if cname in seen:
872 raise QAPISemError(
873 info,
874 "%s collides with %s"
875 % (self.describe(info), seen[cname].describe(info)))
876 seen[cname] = self
877
John Snow4ed3fe02024-03-15 16:22:57 +0100878 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
Markus Armbruster645178c2020-03-17 12:54:44 +0100879 if doc:
880 doc.connect_member(self)
881
John Snow4ed3fe02024-03-15 16:22:57 +0100882 def describe(self, info: Optional[QAPISourceInfo]) -> str:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100883 role = self.role
Markus Armbruster1e148b52023-04-25 15:10:28 +0200884 meta = 'type'
Markus Armbruster226b5be2020-03-17 12:54:42 +0100885 defined_in = self.defined_in
886 assert defined_in
887
888 if defined_in.startswith('q_obj_'):
889 # See QAPISchema._make_implicit_object_type() - reverse the
890 # mapping there to create a nice human-readable description
891 defined_in = defined_in[6:]
892 if defined_in.endswith('-arg'):
893 # Implicit type created for a command's dict 'data'
894 assert role == 'member'
895 role = 'parameter'
Markus Armbruster1e148b52023-04-25 15:10:28 +0200896 meta = 'command'
897 defined_in = defined_in[:-4]
Markus Armbruster226b5be2020-03-17 12:54:42 +0100898 elif defined_in.endswith('-base'):
Markus Armbruster4e99f4b2021-09-17 16:31:32 +0200899 # Implicit type created for a union's dict 'base'
Markus Armbruster226b5be2020-03-17 12:54:42 +0100900 role = 'base ' + role
Markus Armbruster1e148b52023-04-25 15:10:28 +0200901 defined_in = defined_in[:-5]
Markus Armbruster226b5be2020-03-17 12:54:42 +0100902 else:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100903 assert False
Markus Armbruster1e148b52023-04-25 15:10:28 +0200904
John Snow8b9e7fd2024-03-15 16:22:50 +0100905 assert info is not None
Markus Armbruster1e148b52023-04-25 15:10:28 +0200906 if defined_in != info.defn_name:
907 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
Markus Armbruster226b5be2020-03-17 12:54:42 +0100908 return "%s '%s'" % (role, self.name)
909
910
911class QAPISchemaEnumMember(QAPISchemaMember):
912 role = 'value'
913
John Snow4ed3fe02024-03-15 16:22:57 +0100914 def __init__(
915 self,
916 name: str,
917 info: Optional[QAPISourceInfo],
918 ifcond: Optional[QAPISchemaIfCond] = None,
919 features: Optional[List[QAPISchemaFeature]] = None,
920 ):
Markus Armbrusterb6c18752021-10-25 06:24:02 +0200921 super().__init__(name, info, ifcond)
922 for f in features or []:
Markus Armbrusterb6c18752021-10-25 06:24:02 +0200923 f.set_defined_in(name)
924 self.features = features or []
925
John Snow4ed3fe02024-03-15 16:22:57 +0100926 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
Markus Armbrusterb6c18752021-10-25 06:24:02 +0200927 super().connect_doc(doc)
928 if doc:
929 for f in self.features:
930 doc.connect_feature(f)
931
Markus Armbruster226b5be2020-03-17 12:54:42 +0100932
933class QAPISchemaFeature(QAPISchemaMember):
934 role = 'feature'
935
John Snow4ed3fe02024-03-15 16:22:57 +0100936 def is_special(self) -> bool:
Markus Armbruster57df0df2021-10-28 12:25:20 +0200937 return self.name in ('deprecated', 'unstable')
Markus Armbrusterc67db1e2021-10-28 12:25:15 +0200938
Markus Armbruster226b5be2020-03-17 12:54:42 +0100939
940class QAPISchemaObjectTypeMember(QAPISchemaMember):
John Snow4ed3fe02024-03-15 16:22:57 +0100941 def __init__(
942 self,
943 name: str,
944 info: QAPISourceInfo,
945 typ: str,
946 optional: bool,
947 ifcond: Optional[QAPISchemaIfCond] = None,
948 features: Optional[List[QAPISchemaFeature]] = None,
949 ):
Markus Armbruster226b5be2020-03-17 12:54:42 +0100950 super().__init__(name, info, ifcond)
Markus Armbruster84ab0082020-03-17 12:54:45 +0100951 for f in features or []:
Markus Armbruster84ab0082020-03-17 12:54:45 +0100952 f.set_defined_in(name)
Markus Armbruster226b5be2020-03-17 12:54:42 +0100953 self._type_name = typ
John Snowec103962024-03-15 16:22:42 +0100954 self.type: QAPISchemaType # set during check()
Markus Armbruster226b5be2020-03-17 12:54:42 +0100955 self.optional = optional
Markus Armbruster84ab0082020-03-17 12:54:45 +0100956 self.features = features or []
Markus Armbruster226b5be2020-03-17 12:54:42 +0100957
John Snow4ed3fe02024-03-15 16:22:57 +0100958 def need_has(self) -> bool:
Markus Armbruster44ea9d92022-11-04 17:06:46 +0100959 return self.optional and self.type.need_has_if_optional()
960
John Snow4ed3fe02024-03-15 16:22:57 +0100961 def check(self, schema: QAPISchema) -> None:
Markus Armbruster226b5be2020-03-17 12:54:42 +0100962 assert self.defined_in
963 self.type = schema.resolve_type(self._type_name, self.info,
964 self.describe)
John Snow4ed3fe02024-03-15 16:22:57 +0100965 seen: Dict[str, QAPISchemaMember] = {}
Markus Armbruster84ab0082020-03-17 12:54:45 +0100966 for f in self.features:
967 f.check_clash(self.info, seen)
968
John Snow4ed3fe02024-03-15 16:22:57 +0100969 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
Markus Armbruster84ab0082020-03-17 12:54:45 +0100970 super().connect_doc(doc)
971 if doc:
972 for f in self.features:
973 doc.connect_feature(f)
Markus Armbruster226b5be2020-03-17 12:54:42 +0100974
975
Markus Armbruster5858fd12020-03-17 12:54:43 +0100976class QAPISchemaVariant(QAPISchemaObjectTypeMember):
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200977 role = 'branch'
978
John Snow4ed3fe02024-03-15 16:22:57 +0100979 def __init__(
980 self,
981 name: str,
982 info: QAPISourceInfo,
983 typ: str,
984 ifcond: QAPISchemaIfCond,
985 ):
Markus Armbruster2cae67b2020-03-04 16:59:31 +0100986 super().__init__(name, info, typ, False, ifcond)
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200987
988
John Snow2418d1c2024-03-15 16:22:41 +0100989class QAPISchemaCommand(QAPISchemaDefinition):
Markus Armbrustere6c42b92019-10-18 09:43:44 +0200990 meta = 'command'
991
John Snow4ed3fe02024-03-15 16:22:57 +0100992 def __init__(
993 self,
994 name: str,
995 info: QAPISourceInfo,
996 doc: Optional[QAPIDoc],
997 ifcond: QAPISchemaIfCond,
998 features: List[QAPISchemaFeature],
999 arg_type: Optional[str],
1000 ret_type: Optional[str],
1001 gen: bool,
1002 success_response: bool,
1003 boxed: bool,
1004 allow_oob: bool,
1005 allow_preconfig: bool,
1006 coroutine: bool,
1007 ):
Markus Armbruster2cae67b2020-03-04 16:59:31 +01001008 super().__init__(name, info, doc, ifcond, features)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001009 self._arg_type_name = arg_type
John Snow4ed3fe02024-03-15 16:22:57 +01001010 self.arg_type: Optional[QAPISchemaObjectType] = None
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001011 self._ret_type_name = ret_type
John Snow4ed3fe02024-03-15 16:22:57 +01001012 self.ret_type: Optional[QAPISchemaType] = None
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001013 self.gen = gen
1014 self.success_response = success_response
1015 self.boxed = boxed
1016 self.allow_oob = allow_oob
1017 self.allow_preconfig = allow_preconfig
Kevin Wolf04f22362020-10-05 17:58:49 +02001018 self.coroutine = coroutine
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001019
John Snow4ed3fe02024-03-15 16:22:57 +01001020 def check(self, schema: QAPISchema) -> None:
John Snow8b9e7fd2024-03-15 16:22:50 +01001021 assert self.info is not None
Markus Armbruster2cae67b2020-03-04 16:59:31 +01001022 super().check(schema)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001023 if self._arg_type_name:
John Snow9bda6c72024-03-15 16:22:45 +01001024 arg_type = schema.resolve_type(
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001025 self._arg_type_name, self.info, "command's 'data'")
John Snow9bda6c72024-03-15 16:22:45 +01001026 if not isinstance(arg_type, QAPISchemaObjectType):
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001027 raise QAPISemError(
1028 self.info,
1029 "command's 'data' cannot take %s"
John Snow9bda6c72024-03-15 16:22:45 +01001030 % arg_type.describe())
1031 self.arg_type = arg_type
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +01001032 if self.arg_type.branches and not self.boxed:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001033 raise QAPISemError(
1034 self.info,
1035 "command's 'data' can take %s only with 'boxed': true"
1036 % self.arg_type.describe())
Markus Armbrusterde3b3f52023-03-16 08:13:25 +01001037 self.arg_type.check(schema)
1038 if self.arg_type.has_conditional_members() and not self.boxed:
1039 raise QAPISemError(
1040 self.info,
1041 "conditional command arguments require 'boxed': true")
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001042 if self._ret_type_name:
1043 self.ret_type = schema.resolve_type(
1044 self._ret_type_name, self.info, "command's 'returns'")
Markus Armbrusterb86df372021-03-23 10:40:16 +01001045 if self.name not in self.info.pragma.command_returns_exceptions:
Markus Armbruster7e9c1702019-11-20 19:25:46 +01001046 typ = self.ret_type
1047 if isinstance(typ, QAPISchemaArrayType):
John Snow9bda6c72024-03-15 16:22:45 +01001048 typ = typ.element_type
Markus Armbruster7e9c1702019-11-20 19:25:46 +01001049 if not isinstance(typ, QAPISchemaObjectType):
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001050 raise QAPISemError(
1051 self.info,
1052 "command's 'returns' cannot take %s"
1053 % self.ret_type.describe())
1054
John Snow4ed3fe02024-03-15 16:22:57 +01001055 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
Markus Armbrustere4405b32020-03-17 12:54:36 +01001056 super().connect_doc(doc)
Markus Armbrusterbf83f042019-10-24 13:02:29 +02001057 doc = doc or self.doc
1058 if doc:
1059 if self.arg_type and self.arg_type.is_implicit():
1060 self.arg_type.connect_doc(doc)
1061
John Snow4ed3fe02024-03-15 16:22:57 +01001062 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +01001063 super().visit(visitor)
Markus Armbruster7b3bc9e2020-03-17 12:54:38 +01001064 visitor.visit_command(
1065 self.name, self.info, self.ifcond, self.features,
1066 self.arg_type, self.ret_type, self.gen, self.success_response,
Kevin Wolf04f22362020-10-05 17:58:49 +02001067 self.boxed, self.allow_oob, self.allow_preconfig,
1068 self.coroutine)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001069
1070
John Snow2418d1c2024-03-15 16:22:41 +01001071class QAPISchemaEvent(QAPISchemaDefinition):
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001072 meta = 'event'
1073
John Snow4ed3fe02024-03-15 16:22:57 +01001074 def __init__(
1075 self,
1076 name: str,
1077 info: QAPISourceInfo,
1078 doc: Optional[QAPIDoc],
1079 ifcond: QAPISchemaIfCond,
1080 features: List[QAPISchemaFeature],
1081 arg_type: Optional[str],
1082 boxed: bool,
1083 ):
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001084 super().__init__(name, info, doc, ifcond, features)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001085 self._arg_type_name = arg_type
John Snow4ed3fe02024-03-15 16:22:57 +01001086 self.arg_type: Optional[QAPISchemaObjectType] = None
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001087 self.boxed = boxed
1088
John Snow4ed3fe02024-03-15 16:22:57 +01001089 def check(self, schema: QAPISchema) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +01001090 super().check(schema)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001091 if self._arg_type_name:
John Snow9bda6c72024-03-15 16:22:45 +01001092 typ = schema.resolve_type(
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001093 self._arg_type_name, self.info, "event's 'data'")
John Snow9bda6c72024-03-15 16:22:45 +01001094 if not isinstance(typ, QAPISchemaObjectType):
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001095 raise QAPISemError(
1096 self.info,
1097 "event's 'data' cannot take %s"
John Snow9bda6c72024-03-15 16:22:45 +01001098 % typ.describe())
1099 self.arg_type = typ
Markus Armbruster3ff2a5a2024-03-15 16:33:23 +01001100 if self.arg_type.branches and not self.boxed:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001101 raise QAPISemError(
1102 self.info,
1103 "event's 'data' can take %s only with 'boxed': true"
1104 % self.arg_type.describe())
Markus Armbrusterde3b3f52023-03-16 08:13:25 +01001105 self.arg_type.check(schema)
1106 if self.arg_type.has_conditional_members() and not self.boxed:
1107 raise QAPISemError(
1108 self.info,
1109 "conditional event arguments require 'boxed': true")
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001110
John Snow4ed3fe02024-03-15 16:22:57 +01001111 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
Markus Armbrustere4405b32020-03-17 12:54:36 +01001112 super().connect_doc(doc)
Markus Armbrusterbf83f042019-10-24 13:02:29 +02001113 doc = doc or self.doc
1114 if doc:
1115 if self.arg_type and self.arg_type.is_implicit():
1116 self.arg_type.connect_doc(doc)
1117
John Snow4ed3fe02024-03-15 16:22:57 +01001118 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbruster2cae67b2020-03-04 16:59:31 +01001119 super().visit(visitor)
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001120 visitor.visit_event(
1121 self.name, self.info, self.ifcond, self.features,
1122 self.arg_type, self.boxed)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001123
1124
Markus Armbrusterbaa310f2020-03-04 16:59:29 +01001125class QAPISchema:
John Snow4ed3fe02024-03-15 16:22:57 +01001126 def __init__(self, fname: str):
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001127 self.fname = fname
John Snow3404e572021-05-19 14:39:37 -04001128
1129 try:
1130 parser = QAPISchemaParser(fname)
1131 except OSError as err:
1132 raise QAPIError(
1133 f"can't read schema file '{fname}': {err.strerror}"
1134 ) from err
1135
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001136 exprs = check_exprs(parser.exprs)
1137 self.docs = parser.docs
John Snow4ed3fe02024-03-15 16:22:57 +01001138 self._entity_list: List[QAPISchemaEntity] = []
1139 self._entity_dict: Dict[str, QAPISchemaDefinition] = {}
1140 self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict()
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001141 self._schema_dir = os.path.dirname(fname)
John Snow39b2d832021-02-01 14:37:41 -05001142 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001143 self._make_module(fname)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001144 self._predefining = True
1145 self._def_predefineds()
1146 self._predefining = False
1147 self._def_exprs(exprs)
1148 self.check()
1149
John Snow4ed3fe02024-03-15 16:22:57 +01001150 def _def_entity(self, ent: QAPISchemaEntity) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001151 self._entity_list.append(ent)
John Snow2418d1c2024-03-15 16:22:41 +01001152
John Snow4ed3fe02024-03-15 16:22:57 +01001153 def _def_definition(self, defn: QAPISchemaDefinition) -> None:
John Snow2418d1c2024-03-15 16:22:41 +01001154 # Only the predefined types are allowed to not have info
1155 assert defn.info or self._predefining
1156 self._def_entity(defn)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001157 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1158 # because they're liable to clash in generated C.
John Snow2418d1c2024-03-15 16:22:41 +01001159 other_defn = self._entity_dict.get(defn.name)
1160 if other_defn:
1161 if other_defn.info:
1162 where = QAPISourceError(other_defn.info, "previous definition")
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001163 raise QAPISemError(
John Snow2418d1c2024-03-15 16:22:41 +01001164 defn.info,
1165 "'%s' is already defined\n%s" % (defn.name, where))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001166 raise QAPISemError(
John Snow2418d1c2024-03-15 16:22:41 +01001167 defn.info, "%s is already defined" % other_defn.describe())
1168 self._entity_dict[defn.name] = defn
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001169
John Snowf64e7532024-06-26 18:21:08 -04001170 def lookup_entity(self, name: str) -> Optional[QAPISchemaEntity]:
Markus Armbruster060b5a92024-03-15 16:23:01 +01001171 return self._entity_dict.get(name)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001172
John Snow4ed3fe02024-03-15 16:22:57 +01001173 def lookup_type(self, name: str) -> Optional[QAPISchemaType]:
Markus Armbruster060b5a92024-03-15 16:23:01 +01001174 typ = self.lookup_entity(name)
1175 if isinstance(typ, QAPISchemaType):
1176 return typ
1177 return None
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001178
John Snow4ed3fe02024-03-15 16:22:57 +01001179 def resolve_type(
1180 self,
1181 name: str,
1182 info: Optional[QAPISourceInfo],
1183 what: Union[None, str, Callable[[QAPISourceInfo], str]],
1184 ) -> QAPISchemaType:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001185 typ = self.lookup_type(name)
1186 if not typ:
John Snow802a3e32024-03-15 16:22:47 +01001187 assert info and what # built-in types must not fail lookup
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001188 if callable(what):
1189 what = what(info)
1190 raise QAPISemError(
1191 info, "%s uses unknown type '%s'" % (what, name))
1192 return typ
1193
John Snowe2bbc4e2021-02-01 14:37:39 -05001194 def _module_name(self, fname: str) -> str:
John Snow98967c22021-02-01 14:37:36 -05001195 if QAPISchemaModule.is_system_module(fname):
1196 return fname
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001197 return os.path.relpath(fname, self._schema_dir)
1198
John Snow4ed3fe02024-03-15 16:22:57 +01001199 def _make_module(self, fname: str) -> QAPISchemaModule:
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001200 name = self._module_name(fname)
Markus Armbruster8ec0e1a2020-03-04 16:59:32 +01001201 if name not in self._module_dict:
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001202 self._module_dict[name] = QAPISchemaModule(name)
1203 return self._module_dict[name]
1204
John Snow4ed3fe02024-03-15 16:22:57 +01001205 def module_by_fname(self, fname: str) -> QAPISchemaModule:
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001206 name = self._module_name(fname)
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001207 return self._module_dict[name]
1208
John Snow4ed3fe02024-03-15 16:22:57 +01001209 def _def_include(self, expr: QAPIExpression) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001210 include = expr['include']
John Snow42011052023-02-14 19:00:09 -05001211 assert expr.doc is None
1212 self._def_entity(
1213 QAPISchemaInclude(self._make_module(include), expr.info))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001214
John Snow4ed3fe02024-03-15 16:22:57 +01001215 def _def_builtin_type(
1216 self, name: str, json_type: str, c_type: str
1217 ) -> None:
John Snow2418d1c2024-03-15 16:22:41 +01001218 self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001219 # Instantiating only the arrays that are actually used would
1220 # be nice, but we can't as long as their generated code
1221 # (qapi-builtin-types.[ch]) may be shared by some other
1222 # schema.
1223 self._make_array_type(name, None)
1224
John Snow4ed3fe02024-03-15 16:22:57 +01001225 def _def_predefineds(self) -> None:
John Snowa7aa64a2020-10-09 12:15:34 -04001226 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001227 ('number', 'number', 'double'),
1228 ('int', 'int', 'int64_t'),
1229 ('int8', 'int', 'int8_t'),
1230 ('int16', 'int', 'int16_t'),
1231 ('int32', 'int', 'int32_t'),
1232 ('int64', 'int', 'int64_t'),
1233 ('uint8', 'int', 'uint8_t'),
1234 ('uint16', 'int', 'uint16_t'),
1235 ('uint32', 'int', 'uint32_t'),
1236 ('uint64', 'int', 'uint64_t'),
1237 ('size', 'int', 'uint64_t'),
1238 ('bool', 'boolean', 'bool'),
John Snowa7aa64a2020-10-09 12:15:34 -04001239 ('any', 'value', 'QObject' + POINTER_SUFFIX),
1240 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001241 self._def_builtin_type(*t)
1242 self.the_empty_object_type = QAPISchemaObjectType(
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001243 'q_empty', None, None, None, None, None, [], None)
John Snow2418d1c2024-03-15 16:22:41 +01001244 self._def_definition(self.the_empty_object_type)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001245
1246 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1247 'qbool']
1248 qtype_values = self._make_enum_members(
1249 [{'name': n} for n in qtypes], None)
1250
John Snow2418d1c2024-03-15 16:22:41 +01001251 self._def_definition(QAPISchemaEnumType(
Markus Armbruster7b293532024-09-04 13:18:18 +02001252 'QType', None, None, None, None, qtype_values, None))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001253
John Snow4ed3fe02024-03-15 16:22:57 +01001254 def _make_features(
1255 self,
1256 features: Optional[List[Dict[str, Any]]],
1257 info: Optional[QAPISourceInfo],
1258 ) -> List[QAPISchemaFeature]:
Markus Armbrustered30f582020-03-17 12:54:41 +01001259 if features is None:
1260 return []
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001261 return [QAPISchemaFeature(f['name'], info,
1262 QAPISchemaIfCond(f.get('if')))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001263 for f in features]
1264
John Snow4ed3fe02024-03-15 16:22:57 +01001265 def _make_enum_member(
1266 self,
1267 name: str,
1268 ifcond: Optional[Union[str, Dict[str, Any]]],
1269 features: Optional[List[Dict[str, Any]]],
1270 info: Optional[QAPISourceInfo],
1271 ) -> QAPISchemaEnumMember:
Markus Armbrusterb6c18752021-10-25 06:24:02 +02001272 return QAPISchemaEnumMember(name, info,
1273 QAPISchemaIfCond(ifcond),
1274 self._make_features(features, info))
1275
John Snow4ed3fe02024-03-15 16:22:57 +01001276 def _make_enum_members(
1277 self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo]
1278 ) -> List[QAPISchemaEnumMember]:
Markus Armbrusterb6c18752021-10-25 06:24:02 +02001279 return [self._make_enum_member(v['name'], v.get('if'),
1280 v.get('features'), info)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001281 for v in values]
1282
John Snow4ed3fe02024-03-15 16:22:57 +01001283 def _make_array_type(
1284 self, element_type: str, info: Optional[QAPISourceInfo]
1285 ) -> str:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001286 name = element_type + 'List' # reserved by check_defn_name_str()
1287 if not self.lookup_type(name):
John Snow2418d1c2024-03-15 16:22:41 +01001288 self._def_definition(QAPISchemaArrayType(
1289 name, info, element_type))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001290 return name
1291
John Snow4ed3fe02024-03-15 16:22:57 +01001292 def _make_implicit_object_type(
1293 self,
1294 name: str,
1295 info: QAPISourceInfo,
1296 ifcond: QAPISchemaIfCond,
1297 role: str,
1298 members: List[QAPISchemaObjectTypeMember],
1299 ) -> Optional[str]:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001300 if not members:
1301 return None
1302 # See also QAPISchemaObjectTypeMember.describe()
1303 name = 'q_obj_%s-%s' % (name, role)
Markus Armbruster99e75d82024-03-15 16:23:00 +01001304 typ = self.lookup_entity(name)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001305 if typ:
John Snowf64e7532024-06-26 18:21:08 -04001306 assert isinstance(typ, QAPISchemaObjectType)
Markus Armbruster4e99f4b2021-09-17 16:31:32 +02001307 # The implicit object type has multiple users. This can
1308 # only be a duplicate definition, which will be flagged
1309 # later.
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001310 else:
John Snow2418d1c2024-03-15 16:22:41 +01001311 self._def_definition(QAPISchemaObjectType(
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001312 name, info, None, ifcond, None, None, members, None))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001313 return name
1314
John Snow4ed3fe02024-03-15 16:22:57 +01001315 def _def_enum_type(self, expr: QAPIExpression) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001316 name = expr['enum']
1317 data = expr['data']
1318 prefix = expr.get('prefix')
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001319 ifcond = QAPISchemaIfCond(expr.get('if'))
John Snow42011052023-02-14 19:00:09 -05001320 info = expr.info
Markus Armbrustered30f582020-03-17 12:54:41 +01001321 features = self._make_features(expr.get('features'), info)
John Snow2418d1c2024-03-15 16:22:41 +01001322 self._def_definition(QAPISchemaEnumType(
John Snow42011052023-02-14 19:00:09 -05001323 name, info, expr.doc, ifcond, features,
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001324 self._make_enum_members(data, info), prefix))
1325
John Snow4ed3fe02024-03-15 16:22:57 +01001326 def _make_member(
1327 self,
1328 name: str,
1329 typ: Union[List[str], str],
1330 ifcond: QAPISchemaIfCond,
1331 features: Optional[List[Dict[str, Any]]],
1332 info: QAPISourceInfo,
1333 ) -> QAPISchemaObjectTypeMember:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001334 optional = False
1335 if name.startswith('*'):
1336 name = name[1:]
1337 optional = True
1338 if isinstance(typ, list):
1339 assert len(typ) == 1
1340 typ = self._make_array_type(typ[0], info)
Markus Armbruster84ab0082020-03-17 12:54:45 +01001341 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1342 self._make_features(features, info))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001343
John Snow4ed3fe02024-03-15 16:22:57 +01001344 def _make_members(
1345 self,
1346 data: Dict[str, Any],
1347 info: QAPISourceInfo,
1348 ) -> List[QAPISchemaObjectTypeMember]:
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001349 return [self._make_member(key, value['type'],
1350 QAPISchemaIfCond(value.get('if')),
Markus Armbruster84ab0082020-03-17 12:54:45 +01001351 value.get('features'), info)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001352 for (key, value) in data.items()]
1353
John Snow4ed3fe02024-03-15 16:22:57 +01001354 def _def_struct_type(self, expr: QAPIExpression) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001355 name = expr['struct']
1356 base = expr.get('base')
1357 data = expr['data']
John Snow42011052023-02-14 19:00:09 -05001358 info = expr.info
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001359 ifcond = QAPISchemaIfCond(expr.get('if'))
Markus Armbrustered30f582020-03-17 12:54:41 +01001360 features = self._make_features(expr.get('features'), info)
John Snow2418d1c2024-03-15 16:22:41 +01001361 self._def_definition(QAPISchemaObjectType(
John Snow42011052023-02-14 19:00:09 -05001362 name, info, expr.doc, ifcond, features, base,
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001363 self._make_members(data, info),
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001364 None))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001365
John Snow4ed3fe02024-03-15 16:22:57 +01001366 def _make_variant(
1367 self,
1368 case: str,
1369 typ: str,
1370 ifcond: QAPISchemaIfCond,
1371 info: QAPISourceInfo,
1372 ) -> QAPISchemaVariant:
Paolo Bonzinia5806942022-03-21 17:42:41 +01001373 if isinstance(typ, list):
1374 assert len(typ) == 1
1375 typ = self._make_array_type(typ[0], info)
Markus Armbruster5858fd12020-03-17 12:54:43 +01001376 return QAPISchemaVariant(case, info, typ, ifcond)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001377
John Snow4ed3fe02024-03-15 16:22:57 +01001378 def _def_union_type(self, expr: QAPIExpression) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001379 name = expr['union']
Markus Armbruster4e99f4b2021-09-17 16:31:32 +02001380 base = expr['base']
1381 tag_name = expr['discriminator']
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001382 data = expr['data']
John Snow42011052023-02-14 19:00:09 -05001383 assert isinstance(data, dict)
1384 info = expr.info
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001385 ifcond = QAPISchemaIfCond(expr.get('if'))
Markus Armbrustered30f582020-03-17 12:54:41 +01001386 features = self._make_features(expr.get('features'), info)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001387 if isinstance(base, dict):
1388 base = self._make_implicit_object_type(
Markus Armbrustera710e1c2019-10-24 13:02:30 +02001389 name, info, ifcond,
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001390 'base', self._make_members(base, info))
Markus Armbruster4e99f4b2021-09-17 16:31:32 +02001391 variants = [
1392 self._make_variant(key, value['type'],
1393 QAPISchemaIfCond(value.get('if')),
1394 info)
1395 for (key, value) in data.items()]
John Snow42011052023-02-14 19:00:09 -05001396 members: List[QAPISchemaObjectTypeMember] = []
John Snow2418d1c2024-03-15 16:22:41 +01001397 self._def_definition(
John Snow42011052023-02-14 19:00:09 -05001398 QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001399 base, members,
Markus Armbruster1d067e32024-03-15 20:32:41 +01001400 QAPISchemaBranches(
1401 info, variants, tag_name)))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001402
John Snow4ed3fe02024-03-15 16:22:57 +01001403 def _def_alternate_type(self, expr: QAPIExpression) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001404 name = expr['alternate']
1405 data = expr['data']
John Snow42011052023-02-14 19:00:09 -05001406 assert isinstance(data, dict)
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001407 ifcond = QAPISchemaIfCond(expr.get('if'))
John Snow42011052023-02-14 19:00:09 -05001408 info = expr.info
Markus Armbrustered30f582020-03-17 12:54:41 +01001409 features = self._make_features(expr.get('features'), info)
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001410 variants = [
1411 self._make_variant(key, value['type'],
1412 QAPISchemaIfCond(value.get('if')),
1413 info)
1414 for (key, value) in data.items()]
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001415 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
John Snow2418d1c2024-03-15 16:22:41 +01001416 self._def_definition(
John Snow42011052023-02-14 19:00:09 -05001417 QAPISchemaAlternateType(
1418 name, info, expr.doc, ifcond, features,
Markus Armbruster1d067e32024-03-15 20:32:41 +01001419 QAPISchemaAlternatives(info, variants, tag_member)))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001420
John Snow4ed3fe02024-03-15 16:22:57 +01001421 def _def_command(self, expr: QAPIExpression) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001422 name = expr['command']
1423 data = expr.get('data')
1424 rets = expr.get('returns')
1425 gen = expr.get('gen', True)
1426 success_response = expr.get('success-response', True)
1427 boxed = expr.get('boxed', False)
1428 allow_oob = expr.get('allow-oob', False)
1429 allow_preconfig = expr.get('allow-preconfig', False)
Kevin Wolf04f22362020-10-05 17:58:49 +02001430 coroutine = expr.get('coroutine', False)
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001431 ifcond = QAPISchemaIfCond(expr.get('if'))
John Snow42011052023-02-14 19:00:09 -05001432 info = expr.info
Markus Armbrustered30f582020-03-17 12:54:41 +01001433 features = self._make_features(expr.get('features'), info)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001434 if isinstance(data, OrderedDict):
1435 data = self._make_implicit_object_type(
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001436 name, info, ifcond,
1437 'arg', self._make_members(data, info))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001438 if isinstance(rets, list):
1439 assert len(rets) == 1
1440 rets = self._make_array_type(rets[0], info)
John Snow2418d1c2024-03-15 16:22:41 +01001441 self._def_definition(
1442 QAPISchemaCommand(name, info, expr.doc, ifcond, features, data,
1443 rets, gen, success_response, boxed, allow_oob,
1444 allow_preconfig, coroutine))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001445
John Snow4ed3fe02024-03-15 16:22:57 +01001446 def _def_event(self, expr: QAPIExpression) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001447 name = expr['event']
1448 data = expr.get('data')
1449 boxed = expr.get('boxed', False)
Marc-André Lureauf17539c2021-08-04 12:30:57 +04001450 ifcond = QAPISchemaIfCond(expr.get('if'))
John Snow42011052023-02-14 19:00:09 -05001451 info = expr.info
Markus Armbrustered30f582020-03-17 12:54:41 +01001452 features = self._make_features(expr.get('features'), info)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001453 if isinstance(data, OrderedDict):
1454 data = self._make_implicit_object_type(
Markus Armbruster013b4ef2020-03-17 12:54:37 +01001455 name, info, ifcond,
1456 'arg', self._make_members(data, info))
John Snow2418d1c2024-03-15 16:22:41 +01001457 self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond,
1458 features, data, boxed))
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001459
John Snow4ed3fe02024-03-15 16:22:57 +01001460 def _def_exprs(self, exprs: List[QAPIExpression]) -> None:
John Snow42011052023-02-14 19:00:09 -05001461 for expr in exprs:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001462 if 'enum' in expr:
John Snow42011052023-02-14 19:00:09 -05001463 self._def_enum_type(expr)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001464 elif 'struct' in expr:
John Snow42011052023-02-14 19:00:09 -05001465 self._def_struct_type(expr)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001466 elif 'union' in expr:
John Snow42011052023-02-14 19:00:09 -05001467 self._def_union_type(expr)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001468 elif 'alternate' in expr:
John Snow42011052023-02-14 19:00:09 -05001469 self._def_alternate_type(expr)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001470 elif 'command' in expr:
John Snow42011052023-02-14 19:00:09 -05001471 self._def_command(expr)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001472 elif 'event' in expr:
John Snow42011052023-02-14 19:00:09 -05001473 self._def_event(expr)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001474 elif 'include' in expr:
John Snow42011052023-02-14 19:00:09 -05001475 self._def_include(expr)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001476 else:
1477 assert False
1478
John Snow4ed3fe02024-03-15 16:22:57 +01001479 def check(self) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001480 for ent in self._entity_list:
1481 ent.check(self)
Markus Armbrusteree1e6a12019-10-24 13:02:26 +02001482 ent.connect_doc()
Markus Armbrustera9f1dd72019-11-20 19:25:49 +01001483 for ent in self._entity_list:
1484 ent.set_module(self)
Markus Armbrusterfedc04c2024-02-16 15:58:36 +01001485 for doc in self.docs:
1486 doc.check()
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001487
John Snow4ed3fe02024-03-15 16:22:57 +01001488 def visit(self, visitor: QAPISchemaVisitor) -> None:
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001489 visitor.visit_begin(self)
Markus Armbruster3e7fb582019-11-20 19:25:50 +01001490 for mod in self._module_dict.values():
1491 mod.visit(visitor)
Markus Armbrustere6c42b92019-10-18 09:43:44 +02001492 visitor.visit_end()