schema.py 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521
  1. # -*- 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. # pylint: disable=too-many-lines
  15. # TODO catching name collisions in generated code would be nice
  16. from __future__ import annotations
  17. from abc import ABC, abstractmethod
  18. from collections import OrderedDict
  19. import os
  20. import re
  21. from typing import (
  22. Any,
  23. Callable,
  24. Dict,
  25. List,
  26. Optional,
  27. Union,
  28. ValuesView,
  29. cast,
  30. )
  31. from .common import (
  32. POINTER_SUFFIX,
  33. c_name,
  34. cgen_ifcond,
  35. docgen_ifcond,
  36. gen_endif,
  37. gen_if,
  38. )
  39. from .error import QAPIError, QAPISemError, QAPISourceError
  40. from .expr import check_exprs
  41. from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser
  42. from .source import QAPISourceInfo
  43. class QAPISchemaIfCond:
  44. def __init__(
  45. self,
  46. ifcond: Optional[Union[str, Dict[str, object]]] = None,
  47. ) -> None:
  48. self.ifcond = ifcond
  49. def _cgen(self) -> str:
  50. return cgen_ifcond(self.ifcond)
  51. def gen_if(self) -> str:
  52. return gen_if(self._cgen())
  53. def gen_endif(self) -> str:
  54. return gen_endif(self._cgen())
  55. def docgen(self) -> str:
  56. return docgen_ifcond(self.ifcond)
  57. def is_present(self) -> bool:
  58. return bool(self.ifcond)
  59. class QAPISchemaEntity:
  60. """
  61. A schema entity.
  62. This is either a directive, such as include, or a definition.
  63. The latter uses sub-class `QAPISchemaDefinition`.
  64. """
  65. def __init__(self, info: Optional[QAPISourceInfo]):
  66. self._module: Optional[QAPISchemaModule] = None
  67. # For explicitly defined entities, info points to the (explicit)
  68. # definition. For builtins (and their arrays), info is None.
  69. # For implicitly defined entities, info points to a place that
  70. # triggered the implicit definition (there may be more than one
  71. # such place).
  72. self.info = info
  73. self._checked = False
  74. def __repr__(self) -> str:
  75. return "<%s at 0x%x>" % (type(self).__name__, id(self))
  76. def check(self, schema: QAPISchema) -> None:
  77. # pylint: disable=unused-argument
  78. self._checked = True
  79. def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
  80. pass
  81. def _set_module(
  82. self, schema: QAPISchema, info: Optional[QAPISourceInfo]
  83. ) -> None:
  84. assert self._checked
  85. fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
  86. self._module = schema.module_by_fname(fname)
  87. self._module.add_entity(self)
  88. def set_module(self, schema: QAPISchema) -> None:
  89. self._set_module(schema, self.info)
  90. def visit(self, visitor: QAPISchemaVisitor) -> None:
  91. # pylint: disable=unused-argument
  92. assert self._checked
  93. class QAPISchemaDefinition(QAPISchemaEntity):
  94. meta: str
  95. def __init__(
  96. self,
  97. name: str,
  98. info: Optional[QAPISourceInfo],
  99. doc: Optional[QAPIDoc],
  100. ifcond: Optional[QAPISchemaIfCond] = None,
  101. features: Optional[List[QAPISchemaFeature]] = None,
  102. ):
  103. super().__init__(info)
  104. for f in features or []:
  105. f.set_defined_in(name)
  106. self.name = name
  107. self.doc = doc
  108. self._ifcond = ifcond or QAPISchemaIfCond()
  109. self.features = features or []
  110. def __repr__(self) -> str:
  111. return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
  112. id(self))
  113. def c_name(self) -> str:
  114. return c_name(self.name)
  115. def check(self, schema: QAPISchema) -> None:
  116. assert not self._checked
  117. super().check(schema)
  118. seen: Dict[str, QAPISchemaMember] = {}
  119. for f in self.features:
  120. f.check_clash(self.info, seen)
  121. def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
  122. super().connect_doc(doc)
  123. doc = doc or self.doc
  124. if doc:
  125. for f in self.features:
  126. doc.connect_feature(f)
  127. @property
  128. def ifcond(self) -> QAPISchemaIfCond:
  129. assert self._checked
  130. return self._ifcond
  131. def is_implicit(self) -> bool:
  132. return not self.info
  133. def describe(self) -> str:
  134. return "%s '%s'" % (self.meta, self.name)
  135. class QAPISchemaVisitor:
  136. def visit_begin(self, schema: QAPISchema) -> None:
  137. pass
  138. def visit_end(self) -> None:
  139. pass
  140. def visit_module(self, name: str) -> None:
  141. pass
  142. def visit_needed(self, entity: QAPISchemaEntity) -> bool:
  143. # pylint: disable=unused-argument
  144. # Default to visiting everything
  145. return True
  146. def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None:
  147. pass
  148. def visit_builtin_type(
  149. self, name: str, info: Optional[QAPISourceInfo], json_type: str
  150. ) -> None:
  151. pass
  152. def visit_enum_type(
  153. self,
  154. name: str,
  155. info: Optional[QAPISourceInfo],
  156. ifcond: QAPISchemaIfCond,
  157. features: List[QAPISchemaFeature],
  158. members: List[QAPISchemaEnumMember],
  159. prefix: Optional[str],
  160. ) -> None:
  161. pass
  162. def visit_array_type(
  163. self,
  164. name: str,
  165. info: Optional[QAPISourceInfo],
  166. ifcond: QAPISchemaIfCond,
  167. element_type: QAPISchemaType,
  168. ) -> None:
  169. pass
  170. def visit_object_type(
  171. self,
  172. name: str,
  173. info: Optional[QAPISourceInfo],
  174. ifcond: QAPISchemaIfCond,
  175. features: List[QAPISchemaFeature],
  176. base: Optional[QAPISchemaObjectType],
  177. members: List[QAPISchemaObjectTypeMember],
  178. branches: Optional[QAPISchemaBranches],
  179. ) -> None:
  180. pass
  181. def visit_object_type_flat(
  182. self,
  183. name: str,
  184. info: Optional[QAPISourceInfo],
  185. ifcond: QAPISchemaIfCond,
  186. features: List[QAPISchemaFeature],
  187. members: List[QAPISchemaObjectTypeMember],
  188. branches: Optional[QAPISchemaBranches],
  189. ) -> None:
  190. pass
  191. def visit_alternate_type(
  192. self,
  193. name: str,
  194. info: Optional[QAPISourceInfo],
  195. ifcond: QAPISchemaIfCond,
  196. features: List[QAPISchemaFeature],
  197. alternatives: QAPISchemaAlternatives,
  198. ) -> None:
  199. pass
  200. def visit_command(
  201. self,
  202. name: str,
  203. info: Optional[QAPISourceInfo],
  204. ifcond: QAPISchemaIfCond,
  205. features: List[QAPISchemaFeature],
  206. arg_type: Optional[QAPISchemaObjectType],
  207. ret_type: Optional[QAPISchemaType],
  208. gen: bool,
  209. success_response: bool,
  210. boxed: bool,
  211. allow_oob: bool,
  212. allow_preconfig: bool,
  213. coroutine: bool,
  214. ) -> None:
  215. pass
  216. def visit_event(
  217. self,
  218. name: str,
  219. info: Optional[QAPISourceInfo],
  220. ifcond: QAPISchemaIfCond,
  221. features: List[QAPISchemaFeature],
  222. arg_type: Optional[QAPISchemaObjectType],
  223. boxed: bool,
  224. ) -> None:
  225. pass
  226. class QAPISchemaModule:
  227. BUILTIN_MODULE_NAME = './builtin'
  228. def __init__(self, name: str):
  229. self.name = name
  230. self._entity_list: List[QAPISchemaEntity] = []
  231. @staticmethod
  232. def is_system_module(name: str) -> bool:
  233. """
  234. System modules are internally defined modules.
  235. Their names start with the "./" prefix.
  236. """
  237. return name.startswith('./')
  238. @classmethod
  239. def is_user_module(cls, name: str) -> bool:
  240. """
  241. User modules are those defined by the user in qapi JSON files.
  242. They do not start with the "./" prefix.
  243. """
  244. return not cls.is_system_module(name)
  245. @classmethod
  246. def is_builtin_module(cls, name: str) -> bool:
  247. """
  248. The built-in module is a single System module for the built-in types.
  249. It is always "./builtin".
  250. """
  251. return name == cls.BUILTIN_MODULE_NAME
  252. def add_entity(self, ent: QAPISchemaEntity) -> None:
  253. self._entity_list.append(ent)
  254. def visit(self, visitor: QAPISchemaVisitor) -> None:
  255. visitor.visit_module(self.name)
  256. for entity in self._entity_list:
  257. if visitor.visit_needed(entity):
  258. entity.visit(visitor)
  259. class QAPISchemaInclude(QAPISchemaEntity):
  260. def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo):
  261. super().__init__(info)
  262. self._sub_module = sub_module
  263. def visit(self, visitor: QAPISchemaVisitor) -> None:
  264. super().visit(visitor)
  265. visitor.visit_include(self._sub_module.name, self.info)
  266. class QAPISchemaType(QAPISchemaDefinition, ABC):
  267. # Return the C type for common use.
  268. # For the types we commonly box, this is a pointer type.
  269. @abstractmethod
  270. def c_type(self) -> str:
  271. pass
  272. # Return the C type to be used in a parameter list.
  273. def c_param_type(self) -> str:
  274. return self.c_type()
  275. # Return the C type to be used where we suppress boxing.
  276. def c_unboxed_type(self) -> str:
  277. return self.c_type()
  278. @abstractmethod
  279. def json_type(self) -> str:
  280. pass
  281. def alternate_qtype(self) -> Optional[str]:
  282. json2qtype = {
  283. 'null': 'QTYPE_QNULL',
  284. 'string': 'QTYPE_QSTRING',
  285. 'number': 'QTYPE_QNUM',
  286. 'int': 'QTYPE_QNUM',
  287. 'boolean': 'QTYPE_QBOOL',
  288. 'array': 'QTYPE_QLIST',
  289. 'object': 'QTYPE_QDICT'
  290. }
  291. return json2qtype.get(self.json_type())
  292. def doc_type(self) -> Optional[str]:
  293. if self.is_implicit():
  294. return None
  295. return self.name
  296. def need_has_if_optional(self) -> bool:
  297. # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
  298. # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
  299. return not self.c_type().endswith(POINTER_SUFFIX)
  300. def check(self, schema: QAPISchema) -> None:
  301. super().check(schema)
  302. for feat in self.features:
  303. if feat.is_special():
  304. raise QAPISemError(
  305. self.info,
  306. f"feature '{feat.name}' is not supported for types")
  307. def describe(self) -> str:
  308. return "%s type '%s'" % (self.meta, self.name)
  309. class QAPISchemaBuiltinType(QAPISchemaType):
  310. meta = 'built-in'
  311. def __init__(self, name: str, json_type: str, c_type: str):
  312. super().__init__(name, None, None)
  313. assert json_type in ('string', 'number', 'int', 'boolean', 'null',
  314. 'value')
  315. self._json_type_name = json_type
  316. self._c_type_name = c_type
  317. def c_name(self) -> str:
  318. return self.name
  319. def c_type(self) -> str:
  320. return self._c_type_name
  321. def c_param_type(self) -> str:
  322. if self.name == 'str':
  323. return 'const ' + self._c_type_name
  324. return self._c_type_name
  325. def json_type(self) -> str:
  326. return self._json_type_name
  327. def doc_type(self) -> str:
  328. return self.json_type()
  329. def visit(self, visitor: QAPISchemaVisitor) -> None:
  330. super().visit(visitor)
  331. visitor.visit_builtin_type(self.name, self.info, self.json_type())
  332. class QAPISchemaEnumType(QAPISchemaType):
  333. meta = 'enum'
  334. def __init__(
  335. self,
  336. name: str,
  337. info: Optional[QAPISourceInfo],
  338. doc: Optional[QAPIDoc],
  339. ifcond: Optional[QAPISchemaIfCond],
  340. features: Optional[List[QAPISchemaFeature]],
  341. members: List[QAPISchemaEnumMember],
  342. prefix: Optional[str],
  343. ):
  344. super().__init__(name, info, doc, ifcond, features)
  345. for m in members:
  346. m.set_defined_in(name)
  347. self.members = members
  348. self.prefix = prefix
  349. def check(self, schema: QAPISchema) -> None:
  350. super().check(schema)
  351. seen: Dict[str, QAPISchemaMember] = {}
  352. for m in self.members:
  353. m.check_clash(self.info, seen)
  354. def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
  355. super().connect_doc(doc)
  356. doc = doc or self.doc
  357. for m in self.members:
  358. m.connect_doc(doc)
  359. def is_implicit(self) -> bool:
  360. # See QAPISchema._def_predefineds()
  361. return self.name == 'QType'
  362. def c_type(self) -> str:
  363. return c_name(self.name)
  364. def member_names(self) -> List[str]:
  365. return [m.name for m in self.members]
  366. def json_type(self) -> str:
  367. return 'string'
  368. def visit(self, visitor: QAPISchemaVisitor) -> None:
  369. super().visit(visitor)
  370. visitor.visit_enum_type(
  371. self.name, self.info, self.ifcond, self.features,
  372. self.members, self.prefix)
  373. class QAPISchemaArrayType(QAPISchemaType):
  374. meta = 'array'
  375. def __init__(
  376. self, name: str, info: Optional[QAPISourceInfo], element_type: str
  377. ):
  378. super().__init__(name, info, None)
  379. self._element_type_name = element_type
  380. self.element_type: QAPISchemaType
  381. def need_has_if_optional(self) -> bool:
  382. # When FOO is an array, we still need has_FOO to distinguish
  383. # absent (!has_FOO) from present and empty (has_FOO && !FOO).
  384. return True
  385. def check(self, schema: QAPISchema) -> None:
  386. super().check(schema)
  387. self.element_type = schema.resolve_type(
  388. self._element_type_name, self.info,
  389. self.info.defn_meta if self.info else None)
  390. assert not isinstance(self.element_type, QAPISchemaArrayType)
  391. def set_module(self, schema: QAPISchema) -> None:
  392. self._set_module(schema, self.element_type.info)
  393. @property
  394. def ifcond(self) -> QAPISchemaIfCond:
  395. assert self._checked
  396. return self.element_type.ifcond
  397. def is_implicit(self) -> bool:
  398. return True
  399. def c_type(self) -> str:
  400. return c_name(self.name) + POINTER_SUFFIX
  401. def json_type(self) -> str:
  402. return 'array'
  403. def doc_type(self) -> Optional[str]:
  404. elt_doc_type = self.element_type.doc_type()
  405. if not elt_doc_type:
  406. return None
  407. return 'array of ' + elt_doc_type
  408. def visit(self, visitor: QAPISchemaVisitor) -> None:
  409. super().visit(visitor)
  410. visitor.visit_array_type(self.name, self.info, self.ifcond,
  411. self.element_type)
  412. def describe(self) -> str:
  413. return "%s type ['%s']" % (self.meta, self._element_type_name)
  414. class QAPISchemaObjectType(QAPISchemaType):
  415. 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. base: Optional[str],
  423. local_members: List[QAPISchemaObjectTypeMember],
  424. branches: Optional[QAPISchemaBranches],
  425. ):
  426. # struct has local_members, optional base, and no branches
  427. # union has base, branches, and no local_members
  428. super().__init__(name, info, doc, ifcond, features)
  429. self.meta = 'union' if branches else 'struct'
  430. for m in local_members:
  431. m.set_defined_in(name)
  432. if branches is not None:
  433. branches.set_defined_in(name)
  434. self._base_name = base
  435. self.base = None
  436. self.local_members = local_members
  437. self.branches = branches
  438. self.members: List[QAPISchemaObjectTypeMember]
  439. self._check_complete = False
  440. def check(self, schema: QAPISchema) -> None:
  441. # This calls another type T's .check() exactly when the C
  442. # struct emitted by gen_object() contains that T's C struct
  443. # (pointers don't count).
  444. if self._check_complete:
  445. # A previous .check() completed: nothing to do
  446. return
  447. if self._checked:
  448. # Recursed: C struct contains itself
  449. raise QAPISemError(self.info,
  450. "object %s contains itself" % self.name)
  451. super().check(schema)
  452. assert self._checked and not self._check_complete
  453. seen = OrderedDict()
  454. if self._base_name:
  455. self.base = schema.resolve_type(self._base_name, self.info,
  456. "'base'")
  457. if (not isinstance(self.base, QAPISchemaObjectType)
  458. or self.base.branches):
  459. raise QAPISemError(
  460. self.info,
  461. "'base' requires a struct type, %s isn't"
  462. % self.base.describe())
  463. self.base.check(schema)
  464. self.base.check_clash(self.info, seen)
  465. for m in self.local_members:
  466. m.check(schema)
  467. m.check_clash(self.info, seen)
  468. # self.check_clash() works in terms of the supertype, but
  469. # self.members is declared List[QAPISchemaObjectTypeMember].
  470. # Cast down to the subtype.
  471. members = cast(List[QAPISchemaObjectTypeMember], list(seen.values()))
  472. if self.branches:
  473. self.branches.check(schema, seen)
  474. self.branches.check_clash(self.info, seen)
  475. self.members = members
  476. self._check_complete = True # mark completed
  477. # Check that the members of this type do not cause duplicate JSON members,
  478. # and update seen to track the members seen so far. Report any errors
  479. # on behalf of info, which is not necessarily self.info
  480. def check_clash(
  481. self,
  482. info: Optional[QAPISourceInfo],
  483. seen: Dict[str, QAPISchemaMember],
  484. ) -> None:
  485. assert self._checked
  486. for m in self.members:
  487. m.check_clash(info, seen)
  488. if self.branches:
  489. self.branches.check_clash(info, seen)
  490. def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
  491. super().connect_doc(doc)
  492. doc = doc or self.doc
  493. if self.base and self.base.is_implicit():
  494. self.base.connect_doc(doc)
  495. for m in self.local_members:
  496. m.connect_doc(doc)
  497. def is_implicit(self) -> bool:
  498. # See QAPISchema._make_implicit_object_type(), as well as
  499. # _def_predefineds()
  500. return self.name.startswith('q_')
  501. def is_empty(self) -> bool:
  502. return not self.members and not self.branches
  503. def has_conditional_members(self) -> bool:
  504. return any(m.ifcond.is_present() for m in self.members)
  505. def c_name(self) -> str:
  506. assert self.name != 'q_empty'
  507. return super().c_name()
  508. def c_type(self) -> str:
  509. assert not self.is_implicit()
  510. return c_name(self.name) + POINTER_SUFFIX
  511. def c_unboxed_type(self) -> str:
  512. return c_name(self.name)
  513. def json_type(self) -> str:
  514. return 'object'
  515. def visit(self, visitor: QAPISchemaVisitor) -> None:
  516. super().visit(visitor)
  517. visitor.visit_object_type(
  518. self.name, self.info, self.ifcond, self.features,
  519. self.base, self.local_members, self.branches)
  520. visitor.visit_object_type_flat(
  521. self.name, self.info, self.ifcond, self.features,
  522. self.members, self.branches)
  523. class QAPISchemaAlternateType(QAPISchemaType):
  524. meta = 'alternate'
  525. def __init__(
  526. self,
  527. name: str,
  528. info: QAPISourceInfo,
  529. doc: Optional[QAPIDoc],
  530. ifcond: Optional[QAPISchemaIfCond],
  531. features: List[QAPISchemaFeature],
  532. alternatives: QAPISchemaAlternatives,
  533. ):
  534. super().__init__(name, info, doc, ifcond, features)
  535. assert alternatives.tag_member
  536. alternatives.set_defined_in(name)
  537. alternatives.tag_member.set_defined_in(self.name)
  538. self.alternatives = alternatives
  539. def check(self, schema: QAPISchema) -> None:
  540. super().check(schema)
  541. self.alternatives.tag_member.check(schema)
  542. # Not calling self.alternatives.check_clash(), because there's
  543. # nothing to clash with
  544. self.alternatives.check(schema, {})
  545. # Alternate branch names have no relation to the tag enum values;
  546. # so we have to check for potential name collisions ourselves.
  547. seen: Dict[str, QAPISchemaMember] = {}
  548. types_seen: Dict[str, str] = {}
  549. for v in self.alternatives.variants:
  550. v.check_clash(self.info, seen)
  551. qtype = v.type.alternate_qtype()
  552. if not qtype:
  553. raise QAPISemError(
  554. self.info,
  555. "%s cannot use %s"
  556. % (v.describe(self.info), v.type.describe()))
  557. conflicting = set([qtype])
  558. if qtype == 'QTYPE_QSTRING':
  559. if isinstance(v.type, QAPISchemaEnumType):
  560. for m in v.type.members:
  561. if m.name in ['on', 'off']:
  562. conflicting.add('QTYPE_QBOOL')
  563. if re.match(r'[-+0-9.]', m.name):
  564. # lazy, could be tightened
  565. conflicting.add('QTYPE_QNUM')
  566. else:
  567. conflicting.add('QTYPE_QNUM')
  568. conflicting.add('QTYPE_QBOOL')
  569. for qt in conflicting:
  570. if qt in types_seen:
  571. raise QAPISemError(
  572. self.info,
  573. "%s can't be distinguished from '%s'"
  574. % (v.describe(self.info), types_seen[qt]))
  575. types_seen[qt] = v.name
  576. def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
  577. super().connect_doc(doc)
  578. doc = doc or self.doc
  579. for v in self.alternatives.variants:
  580. v.connect_doc(doc)
  581. def c_type(self) -> str:
  582. return c_name(self.name) + POINTER_SUFFIX
  583. def json_type(self) -> str:
  584. return 'value'
  585. def visit(self, visitor: QAPISchemaVisitor) -> None:
  586. super().visit(visitor)
  587. visitor.visit_alternate_type(
  588. self.name, self.info, self.ifcond, self.features,
  589. self.alternatives)
  590. class QAPISchemaVariants:
  591. def __init__(
  592. self,
  593. info: QAPISourceInfo,
  594. variants: List[QAPISchemaVariant],
  595. ):
  596. self.info = info
  597. self.tag_member: QAPISchemaObjectTypeMember
  598. self.variants = variants
  599. def set_defined_in(self, name: str) -> None:
  600. for v in self.variants:
  601. v.set_defined_in(name)
  602. # pylint: disable=unused-argument
  603. def check(
  604. self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
  605. ) -> None:
  606. for v in self.variants:
  607. v.check(schema)
  608. class QAPISchemaBranches(QAPISchemaVariants):
  609. def __init__(self,
  610. info: QAPISourceInfo,
  611. variants: List[QAPISchemaVariant],
  612. tag_name: str):
  613. super().__init__(info, variants)
  614. self._tag_name = tag_name
  615. def check(
  616. self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
  617. ) -> None:
  618. # We need to narrow the member type:
  619. tag_member = seen.get(c_name(self._tag_name))
  620. assert (tag_member is None
  621. or isinstance(tag_member, QAPISchemaObjectTypeMember))
  622. base = "'base'"
  623. # Pointing to the base type when not implicit would be
  624. # nice, but we don't know it here
  625. if not tag_member or self._tag_name != tag_member.name:
  626. raise QAPISemError(
  627. self.info,
  628. "discriminator '%s' is not a member of %s"
  629. % (self._tag_name, base))
  630. self.tag_member = tag_member
  631. # Here we do:
  632. assert tag_member.defined_in
  633. base_type = schema.lookup_type(tag_member.defined_in)
  634. assert base_type
  635. if not base_type.is_implicit():
  636. base = "base type '%s'" % tag_member.defined_in
  637. if not isinstance(tag_member.type, QAPISchemaEnumType):
  638. raise QAPISemError(
  639. self.info,
  640. "discriminator member '%s' of %s must be of enum type"
  641. % (self._tag_name, base))
  642. if tag_member.optional:
  643. raise QAPISemError(
  644. self.info,
  645. "discriminator member '%s' of %s must not be optional"
  646. % (self._tag_name, base))
  647. if tag_member.ifcond.is_present():
  648. raise QAPISemError(
  649. self.info,
  650. "discriminator member '%s' of %s must not be conditional"
  651. % (self._tag_name, base))
  652. # branches that are not explicitly covered get an empty type
  653. assert tag_member.defined_in
  654. cases = {v.name for v in self.variants}
  655. for m in tag_member.type.members:
  656. if m.name not in cases:
  657. v = QAPISchemaVariant(m.name, self.info,
  658. 'q_empty', m.ifcond)
  659. v.set_defined_in(tag_member.defined_in)
  660. self.variants.append(v)
  661. if not self.variants:
  662. raise QAPISemError(self.info, "union has no branches")
  663. for v in self.variants:
  664. v.check(schema)
  665. # Union names must match enum values; alternate names are
  666. # checked separately. Use 'seen' to tell the two apart.
  667. if seen:
  668. if v.name not in tag_member.type.member_names():
  669. raise QAPISemError(
  670. self.info,
  671. "branch '%s' is not a value of %s"
  672. % (v.name, tag_member.type.describe()))
  673. if not isinstance(v.type, QAPISchemaObjectType):
  674. raise QAPISemError(
  675. self.info,
  676. "%s cannot use %s"
  677. % (v.describe(self.info), v.type.describe()))
  678. v.type.check(schema)
  679. def check_clash(
  680. self,
  681. info: Optional[QAPISourceInfo],
  682. seen: Dict[str, QAPISchemaMember],
  683. ) -> None:
  684. for v in self.variants:
  685. # Reset seen map for each variant, since qapi names from one
  686. # branch do not affect another branch.
  687. #
  688. # v.type's typing is enforced in check() above.
  689. assert isinstance(v.type, QAPISchemaObjectType)
  690. v.type.check_clash(info, dict(seen))
  691. class QAPISchemaAlternatives(QAPISchemaVariants):
  692. def __init__(self,
  693. info: QAPISourceInfo,
  694. variants: List[QAPISchemaVariant],
  695. tag_member: QAPISchemaObjectTypeMember):
  696. super().__init__(info, variants)
  697. self.tag_member = tag_member
  698. def check(
  699. self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
  700. ) -> None:
  701. super().check(schema, seen)
  702. assert isinstance(self.tag_member.type, QAPISchemaEnumType)
  703. assert not self.tag_member.optional
  704. assert not self.tag_member.ifcond.is_present()
  705. class QAPISchemaMember:
  706. """ Represents object members, enum members and features """
  707. role = 'member'
  708. def __init__(
  709. self,
  710. name: str,
  711. info: Optional[QAPISourceInfo],
  712. ifcond: Optional[QAPISchemaIfCond] = None,
  713. ):
  714. self.name = name
  715. self.info = info
  716. self.ifcond = ifcond or QAPISchemaIfCond()
  717. self.defined_in: Optional[str] = None
  718. def set_defined_in(self, name: str) -> None:
  719. assert not self.defined_in
  720. self.defined_in = name
  721. def check_clash(
  722. self,
  723. info: Optional[QAPISourceInfo],
  724. seen: Dict[str, QAPISchemaMember],
  725. ) -> None:
  726. cname = c_name(self.name)
  727. if cname in seen:
  728. raise QAPISemError(
  729. info,
  730. "%s collides with %s"
  731. % (self.describe(info), seen[cname].describe(info)))
  732. seen[cname] = self
  733. def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
  734. if doc:
  735. doc.connect_member(self)
  736. def describe(self, info: Optional[QAPISourceInfo]) -> str:
  737. role = self.role
  738. meta = 'type'
  739. defined_in = self.defined_in
  740. assert defined_in
  741. if defined_in.startswith('q_obj_'):
  742. # See QAPISchema._make_implicit_object_type() - reverse the
  743. # mapping there to create a nice human-readable description
  744. defined_in = defined_in[6:]
  745. if defined_in.endswith('-arg'):
  746. # Implicit type created for a command's dict 'data'
  747. assert role == 'member'
  748. role = 'parameter'
  749. meta = 'command'
  750. defined_in = defined_in[:-4]
  751. elif defined_in.endswith('-base'):
  752. # Implicit type created for a union's dict 'base'
  753. role = 'base ' + role
  754. defined_in = defined_in[:-5]
  755. else:
  756. assert False
  757. assert info is not None
  758. if defined_in != info.defn_name:
  759. return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
  760. return "%s '%s'" % (role, self.name)
  761. class QAPISchemaEnumMember(QAPISchemaMember):
  762. role = 'value'
  763. def __init__(
  764. self,
  765. name: str,
  766. info: Optional[QAPISourceInfo],
  767. ifcond: Optional[QAPISchemaIfCond] = None,
  768. features: Optional[List[QAPISchemaFeature]] = None,
  769. ):
  770. super().__init__(name, info, ifcond)
  771. for f in features or []:
  772. f.set_defined_in(name)
  773. self.features = features or []
  774. def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
  775. super().connect_doc(doc)
  776. if doc:
  777. for f in self.features:
  778. doc.connect_feature(f)
  779. class QAPISchemaFeature(QAPISchemaMember):
  780. role = 'feature'
  781. # Features which are standardized across all schemas
  782. SPECIAL_NAMES = ['deprecated', 'unstable']
  783. def is_special(self) -> bool:
  784. return self.name in QAPISchemaFeature.SPECIAL_NAMES
  785. class QAPISchemaObjectTypeMember(QAPISchemaMember):
  786. def __init__(
  787. self,
  788. name: str,
  789. info: QAPISourceInfo,
  790. typ: str,
  791. optional: bool,
  792. ifcond: Optional[QAPISchemaIfCond] = None,
  793. features: Optional[List[QAPISchemaFeature]] = None,
  794. ):
  795. super().__init__(name, info, ifcond)
  796. for f in features or []:
  797. f.set_defined_in(name)
  798. self._type_name = typ
  799. self.type: QAPISchemaType # set during check()
  800. self.optional = optional
  801. self.features = features or []
  802. def need_has(self) -> bool:
  803. return self.optional and self.type.need_has_if_optional()
  804. def check(self, schema: QAPISchema) -> None:
  805. assert self.defined_in
  806. self.type = schema.resolve_type(self._type_name, self.info,
  807. self.describe)
  808. seen: Dict[str, QAPISchemaMember] = {}
  809. for f in self.features:
  810. f.check_clash(self.info, seen)
  811. def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
  812. super().connect_doc(doc)
  813. if doc:
  814. for f in self.features:
  815. doc.connect_feature(f)
  816. class QAPISchemaVariant(QAPISchemaObjectTypeMember):
  817. role = 'branch'
  818. def __init__(
  819. self,
  820. name: str,
  821. info: QAPISourceInfo,
  822. typ: str,
  823. ifcond: QAPISchemaIfCond,
  824. ):
  825. super().__init__(name, info, typ, False, ifcond)
  826. class QAPISchemaCommand(QAPISchemaDefinition):
  827. meta = 'command'
  828. def __init__(
  829. self,
  830. name: str,
  831. info: QAPISourceInfo,
  832. doc: Optional[QAPIDoc],
  833. ifcond: QAPISchemaIfCond,
  834. features: List[QAPISchemaFeature],
  835. arg_type: Optional[str],
  836. ret_type: Optional[str],
  837. gen: bool,
  838. success_response: bool,
  839. boxed: bool,
  840. allow_oob: bool,
  841. allow_preconfig: bool,
  842. coroutine: bool,
  843. ):
  844. super().__init__(name, info, doc, ifcond, features)
  845. self._arg_type_name = arg_type
  846. self.arg_type: Optional[QAPISchemaObjectType] = None
  847. self._ret_type_name = ret_type
  848. self.ret_type: Optional[QAPISchemaType] = None
  849. self.gen = gen
  850. self.success_response = success_response
  851. self.boxed = boxed
  852. self.allow_oob = allow_oob
  853. self.allow_preconfig = allow_preconfig
  854. self.coroutine = coroutine
  855. def check(self, schema: QAPISchema) -> None:
  856. assert self.info is not None
  857. super().check(schema)
  858. if self._arg_type_name:
  859. arg_type = schema.resolve_type(
  860. self._arg_type_name, self.info, "command's 'data'")
  861. if not isinstance(arg_type, QAPISchemaObjectType):
  862. raise QAPISemError(
  863. self.info,
  864. "command's 'data' cannot take %s"
  865. % arg_type.describe())
  866. self.arg_type = arg_type
  867. if self.arg_type.branches and not self.boxed:
  868. raise QAPISemError(
  869. self.info,
  870. "command's 'data' can take %s only with 'boxed': true"
  871. % self.arg_type.describe())
  872. self.arg_type.check(schema)
  873. if self.arg_type.has_conditional_members() and not self.boxed:
  874. raise QAPISemError(
  875. self.info,
  876. "conditional command arguments require 'boxed': true")
  877. if self._ret_type_name:
  878. self.ret_type = schema.resolve_type(
  879. self._ret_type_name, self.info, "command's 'returns'")
  880. if self.name not in self.info.pragma.command_returns_exceptions:
  881. typ = self.ret_type
  882. if isinstance(typ, QAPISchemaArrayType):
  883. typ = typ.element_type
  884. if not isinstance(typ, QAPISchemaObjectType):
  885. raise QAPISemError(
  886. self.info,
  887. "command's 'returns' cannot take %s"
  888. % self.ret_type.describe())
  889. def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
  890. super().connect_doc(doc)
  891. doc = doc or self.doc
  892. if doc:
  893. if self.arg_type and self.arg_type.is_implicit():
  894. self.arg_type.connect_doc(doc)
  895. def visit(self, visitor: QAPISchemaVisitor) -> None:
  896. super().visit(visitor)
  897. visitor.visit_command(
  898. self.name, self.info, self.ifcond, self.features,
  899. self.arg_type, self.ret_type, self.gen, self.success_response,
  900. self.boxed, self.allow_oob, self.allow_preconfig,
  901. self.coroutine)
  902. class QAPISchemaEvent(QAPISchemaDefinition):
  903. meta = 'event'
  904. def __init__(
  905. self,
  906. name: str,
  907. info: QAPISourceInfo,
  908. doc: Optional[QAPIDoc],
  909. ifcond: QAPISchemaIfCond,
  910. features: List[QAPISchemaFeature],
  911. arg_type: Optional[str],
  912. boxed: bool,
  913. ):
  914. super().__init__(name, info, doc, ifcond, features)
  915. self._arg_type_name = arg_type
  916. self.arg_type: Optional[QAPISchemaObjectType] = None
  917. self.boxed = boxed
  918. def check(self, schema: QAPISchema) -> None:
  919. super().check(schema)
  920. if self._arg_type_name:
  921. typ = schema.resolve_type(
  922. self._arg_type_name, self.info, "event's 'data'")
  923. if not isinstance(typ, QAPISchemaObjectType):
  924. raise QAPISemError(
  925. self.info,
  926. "event's 'data' cannot take %s"
  927. % typ.describe())
  928. self.arg_type = typ
  929. if self.arg_type.branches and not self.boxed:
  930. raise QAPISemError(
  931. self.info,
  932. "event's 'data' can take %s only with 'boxed': true"
  933. % self.arg_type.describe())
  934. self.arg_type.check(schema)
  935. if self.arg_type.has_conditional_members() and not self.boxed:
  936. raise QAPISemError(
  937. self.info,
  938. "conditional event arguments require 'boxed': true")
  939. def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
  940. super().connect_doc(doc)
  941. doc = doc or self.doc
  942. if doc:
  943. if self.arg_type and self.arg_type.is_implicit():
  944. self.arg_type.connect_doc(doc)
  945. def visit(self, visitor: QAPISchemaVisitor) -> None:
  946. super().visit(visitor)
  947. visitor.visit_event(
  948. self.name, self.info, self.ifcond, self.features,
  949. self.arg_type, self.boxed)
  950. class QAPISchema:
  951. def __init__(self, fname: str):
  952. self.fname = fname
  953. try:
  954. parser = QAPISchemaParser(fname)
  955. except OSError as err:
  956. raise QAPIError(
  957. f"can't read schema file '{fname}': {err.strerror}"
  958. ) from err
  959. exprs = check_exprs(parser.exprs)
  960. self.docs = parser.docs
  961. self._entity_list: List[QAPISchemaEntity] = []
  962. self._entity_dict: Dict[str, QAPISchemaDefinition] = {}
  963. self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict()
  964. # NB, values in the dict will identify the first encountered
  965. # usage of a named feature only
  966. self._feature_dict: Dict[str, QAPISchemaFeature] = OrderedDict()
  967. # All schemas get the names defined in the QapiSpecialFeature enum.
  968. # Rely on dict iteration order matching insertion order so that
  969. # the special names are emitted first when generating code.
  970. for f in QAPISchemaFeature.SPECIAL_NAMES:
  971. self._feature_dict[f] = QAPISchemaFeature(f, None)
  972. self._schema_dir = os.path.dirname(fname)
  973. self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
  974. self._make_module(fname)
  975. self._predefining = True
  976. self._def_predefineds()
  977. self._predefining = False
  978. self._def_exprs(exprs)
  979. self.check()
  980. def features(self) -> ValuesView[QAPISchemaFeature]:
  981. return self._feature_dict.values()
  982. def _def_entity(self, ent: QAPISchemaEntity) -> None:
  983. self._entity_list.append(ent)
  984. def _def_definition(self, defn: QAPISchemaDefinition) -> None:
  985. # Only the predefined types are allowed to not have info
  986. assert defn.info or self._predefining
  987. self._def_entity(defn)
  988. # TODO reject names that differ only in '_' vs. '.' vs. '-',
  989. # because they're liable to clash in generated C.
  990. other_defn = self._entity_dict.get(defn.name)
  991. if other_defn:
  992. if other_defn.info:
  993. where = QAPISourceError(other_defn.info, "previous definition")
  994. raise QAPISemError(
  995. defn.info,
  996. "'%s' is already defined\n%s" % (defn.name, where))
  997. raise QAPISemError(
  998. defn.info, "%s is already defined" % other_defn.describe())
  999. self._entity_dict[defn.name] = defn
  1000. def lookup_entity(self, name: str) -> Optional[QAPISchemaEntity]:
  1001. return self._entity_dict.get(name)
  1002. def lookup_type(self, name: str) -> Optional[QAPISchemaType]:
  1003. typ = self.lookup_entity(name)
  1004. if isinstance(typ, QAPISchemaType):
  1005. return typ
  1006. return None
  1007. def resolve_type(
  1008. self,
  1009. name: str,
  1010. info: Optional[QAPISourceInfo],
  1011. what: Union[None, str, Callable[[QAPISourceInfo], str]],
  1012. ) -> QAPISchemaType:
  1013. typ = self.lookup_type(name)
  1014. if not typ:
  1015. assert info and what # built-in types must not fail lookup
  1016. if callable(what):
  1017. what = what(info)
  1018. raise QAPISemError(
  1019. info, "%s uses unknown type '%s'" % (what, name))
  1020. return typ
  1021. def _module_name(self, fname: str) -> str:
  1022. if QAPISchemaModule.is_system_module(fname):
  1023. return fname
  1024. return os.path.relpath(fname, self._schema_dir)
  1025. def _make_module(self, fname: str) -> QAPISchemaModule:
  1026. name = self._module_name(fname)
  1027. if name not in self._module_dict:
  1028. self._module_dict[name] = QAPISchemaModule(name)
  1029. return self._module_dict[name]
  1030. def module_by_fname(self, fname: str) -> QAPISchemaModule:
  1031. name = self._module_name(fname)
  1032. return self._module_dict[name]
  1033. def _def_include(self, expr: QAPIExpression) -> None:
  1034. include = expr['include']
  1035. assert expr.doc is None
  1036. self._def_entity(
  1037. QAPISchemaInclude(self._make_module(include), expr.info))
  1038. def _def_builtin_type(
  1039. self, name: str, json_type: str, c_type: str
  1040. ) -> None:
  1041. self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type))
  1042. # Instantiating only the arrays that are actually used would
  1043. # be nice, but we can't as long as their generated code
  1044. # (qapi-builtin-types.[ch]) may be shared by some other
  1045. # schema.
  1046. self._make_array_type(name, None)
  1047. def _def_predefineds(self) -> None:
  1048. for t in [('str', 'string', 'char' + POINTER_SUFFIX),
  1049. ('number', 'number', 'double'),
  1050. ('int', 'int', 'int64_t'),
  1051. ('int8', 'int', 'int8_t'),
  1052. ('int16', 'int', 'int16_t'),
  1053. ('int32', 'int', 'int32_t'),
  1054. ('int64', 'int', 'int64_t'),
  1055. ('uint8', 'int', 'uint8_t'),
  1056. ('uint16', 'int', 'uint16_t'),
  1057. ('uint32', 'int', 'uint32_t'),
  1058. ('uint64', 'int', 'uint64_t'),
  1059. ('size', 'int', 'uint64_t'),
  1060. ('bool', 'boolean', 'bool'),
  1061. ('any', 'value', 'QObject' + POINTER_SUFFIX),
  1062. ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
  1063. self._def_builtin_type(*t)
  1064. self.the_empty_object_type = QAPISchemaObjectType(
  1065. 'q_empty', None, None, None, None, None, [], None)
  1066. self._def_definition(self.the_empty_object_type)
  1067. qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
  1068. 'qbool']
  1069. qtype_values = self._make_enum_members(
  1070. [{'name': n} for n in qtypes], None)
  1071. self._def_definition(QAPISchemaEnumType(
  1072. 'QType', None, None, None, None, qtype_values, None))
  1073. def _make_features(
  1074. self,
  1075. features: Optional[List[Dict[str, Any]]],
  1076. info: Optional[QAPISourceInfo],
  1077. ) -> List[QAPISchemaFeature]:
  1078. if features is None:
  1079. return []
  1080. for f in features:
  1081. feat = QAPISchemaFeature(f['name'], info)
  1082. if feat.name not in self._feature_dict:
  1083. self._feature_dict[feat.name] = feat
  1084. return [QAPISchemaFeature(f['name'], info,
  1085. QAPISchemaIfCond(f.get('if')))
  1086. for f in features]
  1087. def _make_enum_member(
  1088. self,
  1089. name: str,
  1090. ifcond: Optional[Union[str, Dict[str, Any]]],
  1091. features: Optional[List[Dict[str, Any]]],
  1092. info: Optional[QAPISourceInfo],
  1093. ) -> QAPISchemaEnumMember:
  1094. return QAPISchemaEnumMember(name, info,
  1095. QAPISchemaIfCond(ifcond),
  1096. self._make_features(features, info))
  1097. def _make_enum_members(
  1098. self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo]
  1099. ) -> List[QAPISchemaEnumMember]:
  1100. return [self._make_enum_member(v['name'], v.get('if'),
  1101. v.get('features'), info)
  1102. for v in values]
  1103. def _make_array_type(
  1104. self, element_type: str, info: Optional[QAPISourceInfo]
  1105. ) -> str:
  1106. name = element_type + 'List' # reserved by check_defn_name_str()
  1107. if not self.lookup_type(name):
  1108. self._def_definition(QAPISchemaArrayType(
  1109. name, info, element_type))
  1110. return name
  1111. def _make_implicit_object_type(
  1112. self,
  1113. name: str,
  1114. info: QAPISourceInfo,
  1115. ifcond: QAPISchemaIfCond,
  1116. role: str,
  1117. members: List[QAPISchemaObjectTypeMember],
  1118. ) -> Optional[str]:
  1119. if not members:
  1120. return None
  1121. # See also QAPISchemaObjectTypeMember.describe()
  1122. name = 'q_obj_%s-%s' % (name, role)
  1123. typ = self.lookup_entity(name)
  1124. if typ:
  1125. assert isinstance(typ, QAPISchemaObjectType)
  1126. # The implicit object type has multiple users. This can
  1127. # only be a duplicate definition, which will be flagged
  1128. # later.
  1129. else:
  1130. self._def_definition(QAPISchemaObjectType(
  1131. name, info, None, ifcond, None, None, members, None))
  1132. return name
  1133. def _def_enum_type(self, expr: QAPIExpression) -> None:
  1134. name = expr['enum']
  1135. data = expr['data']
  1136. prefix = expr.get('prefix')
  1137. ifcond = QAPISchemaIfCond(expr.get('if'))
  1138. info = expr.info
  1139. features = self._make_features(expr.get('features'), info)
  1140. self._def_definition(QAPISchemaEnumType(
  1141. name, info, expr.doc, ifcond, features,
  1142. self._make_enum_members(data, info), prefix))
  1143. def _make_member(
  1144. self,
  1145. name: str,
  1146. typ: Union[List[str], str],
  1147. ifcond: QAPISchemaIfCond,
  1148. features: Optional[List[Dict[str, Any]]],
  1149. info: QAPISourceInfo,
  1150. ) -> QAPISchemaObjectTypeMember:
  1151. optional = False
  1152. if name.startswith('*'):
  1153. name = name[1:]
  1154. optional = True
  1155. if isinstance(typ, list):
  1156. assert len(typ) == 1
  1157. typ = self._make_array_type(typ[0], info)
  1158. return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
  1159. self._make_features(features, info))
  1160. def _make_members(
  1161. self,
  1162. data: Dict[str, Any],
  1163. info: QAPISourceInfo,
  1164. ) -> List[QAPISchemaObjectTypeMember]:
  1165. return [self._make_member(key, value['type'],
  1166. QAPISchemaIfCond(value.get('if')),
  1167. value.get('features'), info)
  1168. for (key, value) in data.items()]
  1169. def _def_struct_type(self, expr: QAPIExpression) -> None:
  1170. name = expr['struct']
  1171. base = expr.get('base')
  1172. data = expr['data']
  1173. info = expr.info
  1174. ifcond = QAPISchemaIfCond(expr.get('if'))
  1175. features = self._make_features(expr.get('features'), info)
  1176. self._def_definition(QAPISchemaObjectType(
  1177. name, info, expr.doc, ifcond, features, base,
  1178. self._make_members(data, info),
  1179. None))
  1180. def _make_variant(
  1181. self,
  1182. case: str,
  1183. typ: str,
  1184. ifcond: QAPISchemaIfCond,
  1185. info: QAPISourceInfo,
  1186. ) -> QAPISchemaVariant:
  1187. if isinstance(typ, list):
  1188. assert len(typ) == 1
  1189. typ = self._make_array_type(typ[0], info)
  1190. return QAPISchemaVariant(case, info, typ, ifcond)
  1191. def _def_union_type(self, expr: QAPIExpression) -> None:
  1192. name = expr['union']
  1193. base = expr['base']
  1194. tag_name = expr['discriminator']
  1195. data = expr['data']
  1196. assert isinstance(data, dict)
  1197. info = expr.info
  1198. ifcond = QAPISchemaIfCond(expr.get('if'))
  1199. features = self._make_features(expr.get('features'), info)
  1200. if isinstance(base, dict):
  1201. base = self._make_implicit_object_type(
  1202. name, info, ifcond,
  1203. 'base', self._make_members(base, info))
  1204. variants = [
  1205. self._make_variant(key, value['type'],
  1206. QAPISchemaIfCond(value.get('if')),
  1207. info)
  1208. for (key, value) in data.items()]
  1209. members: List[QAPISchemaObjectTypeMember] = []
  1210. self._def_definition(
  1211. QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
  1212. base, members,
  1213. QAPISchemaBranches(
  1214. info, variants, tag_name)))
  1215. def _def_alternate_type(self, expr: QAPIExpression) -> None:
  1216. name = expr['alternate']
  1217. data = expr['data']
  1218. assert isinstance(data, dict)
  1219. ifcond = QAPISchemaIfCond(expr.get('if'))
  1220. info = expr.info
  1221. features = self._make_features(expr.get('features'), info)
  1222. variants = [
  1223. self._make_variant(key, value['type'],
  1224. QAPISchemaIfCond(value.get('if')),
  1225. info)
  1226. for (key, value) in data.items()]
  1227. tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
  1228. self._def_definition(
  1229. QAPISchemaAlternateType(
  1230. name, info, expr.doc, ifcond, features,
  1231. QAPISchemaAlternatives(info, variants, tag_member)))
  1232. def _def_command(self, expr: QAPIExpression) -> None:
  1233. name = expr['command']
  1234. data = expr.get('data')
  1235. rets = expr.get('returns')
  1236. gen = expr.get('gen', True)
  1237. success_response = expr.get('success-response', True)
  1238. boxed = expr.get('boxed', False)
  1239. allow_oob = expr.get('allow-oob', False)
  1240. allow_preconfig = expr.get('allow-preconfig', False)
  1241. coroutine = expr.get('coroutine', False)
  1242. ifcond = QAPISchemaIfCond(expr.get('if'))
  1243. info = expr.info
  1244. features = self._make_features(expr.get('features'), info)
  1245. if isinstance(data, OrderedDict):
  1246. data = self._make_implicit_object_type(
  1247. name, info, ifcond,
  1248. 'arg', self._make_members(data, info))
  1249. if isinstance(rets, list):
  1250. assert len(rets) == 1
  1251. rets = self._make_array_type(rets[0], info)
  1252. self._def_definition(
  1253. QAPISchemaCommand(name, info, expr.doc, ifcond, features, data,
  1254. rets, gen, success_response, boxed, allow_oob,
  1255. allow_preconfig, coroutine))
  1256. def _def_event(self, expr: QAPIExpression) -> None:
  1257. name = expr['event']
  1258. data = expr.get('data')
  1259. boxed = expr.get('boxed', False)
  1260. ifcond = QAPISchemaIfCond(expr.get('if'))
  1261. info = expr.info
  1262. features = self._make_features(expr.get('features'), info)
  1263. if isinstance(data, OrderedDict):
  1264. data = self._make_implicit_object_type(
  1265. name, info, ifcond,
  1266. 'arg', self._make_members(data, info))
  1267. self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond,
  1268. features, data, boxed))
  1269. def _def_exprs(self, exprs: List[QAPIExpression]) -> None:
  1270. for expr in exprs:
  1271. if 'enum' in expr:
  1272. self._def_enum_type(expr)
  1273. elif 'struct' in expr:
  1274. self._def_struct_type(expr)
  1275. elif 'union' in expr:
  1276. self._def_union_type(expr)
  1277. elif 'alternate' in expr:
  1278. self._def_alternate_type(expr)
  1279. elif 'command' in expr:
  1280. self._def_command(expr)
  1281. elif 'event' in expr:
  1282. self._def_event(expr)
  1283. elif 'include' in expr:
  1284. self._def_include(expr)
  1285. else:
  1286. assert False
  1287. def check(self) -> None:
  1288. for ent in self._entity_list:
  1289. ent.check(self)
  1290. ent.connect_doc()
  1291. for ent in self._entity_list:
  1292. ent.set_module(self)
  1293. for doc in self.docs:
  1294. doc.check()
  1295. features = list(self._feature_dict.values())
  1296. if len(features) > 64:
  1297. raise QAPISemError(
  1298. features[64].info,
  1299. "Maximum of 64 schema features is permitted")
  1300. def visit(self, visitor: QAPISchemaVisitor) -> None:
  1301. visitor.visit_begin(self)
  1302. for mod in self._module_dict.values():
  1303. mod.visit(visitor)
  1304. visitor.visit_end()