schema.py 41 KB

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