2
0

schema.py 42 KB

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