Hi
On Thu, Aug 6, 2026 at 6:06 PM Markus Armbruster <[email protected]> wrote:
>
> Marc-André Lureau <[email protected]> writes:
>
> > New QAPISchemaGenTypeInfoVisitor produces per-module
> > qapi-type-infos-*.h/c files. Each file declares QAPITypeInfo constants
> > pairing the QAPI type name with its masked introspection name.
> >
> > Signed-off-by: Marc-André Lureau <[email protected]>
> > ---
> > docs/devel/qapi-code-gen.rst | 90 +++++++++++++++++++++
> > meson.build | 1 +
> > scripts/qapi/type_infos.py | 182
> > +++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 273 insertions(+)
> >
> > diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst
> > index 3a632b4a648..bb2e8d9c675 100644
> > --- a/docs/devel/qapi-code-gen.rst
> > +++ b/docs/devel/qapi-code-gen.rst
> > @@ -2103,3 +2103,93 @@ Example::
> > }));
> >
> > [Uninteresting stuff omitted...]
> > +
> > +
> > +Code generated for type information
> > +-----------------------------------
> > +
> > +The following files are created:
> > +
> > + ``$(prefix)qapi-type-infos.c``
> > + A ``QAPITypeInfo`` instance for each schema-defined type, providing
> > + a mapping between the C type name and the schema name used by
> > + introspection, along with optional enum lookup table and list-element
> > + pointers.
> > +
> > + ``$(prefix)qapi-type-infos.h``
> > + Declarations for the above type info instances
> > +
> > +Each ``QAPITypeInfo`` struct has the following fields:
> > +
> > +``name``
> > + The C identifier of the type (e.g. ``"UserDefOne"``).
>
> Isn't this the name used in the QAPI schema source?
correct
>
> > +
> > +``schema_name``
> > + The masked name used in ``query-qmp-schema`` output, or ``NULL``
> > + for built-in types whose schema name equals their C name. This
> > + allows management tools to cross-reference a QOM property's
> > + ``qapi-type`` against the introspection schema.
>
> I believe it can also be null when query-qmp-schema omits the type.
> More on that below.
Yes, that's a current limitation. Only types reachable from QMP are in
the introspection schema. I will try to address this
>
> > +
> > +``lookup``
> > + For enum types, a pointer to the corresponding ``QEnumLookup``
> > + table. ``NULL`` for non-enum types.
> > +
> > +``list``
> > + For types that have an array variant, a pointer to the list type's
> > + ``QAPITypeInfo``. ``NULL`` when no list type exists.
>
> Confusing mix of "array" and "list" in the same sentence. Suggest "the
> array type's".
>
> QAPI can't decide whether to call the thing an array (like JSON) or a
> list (like the C implementation). Unfortunate.
> docs/devel/qapi-code-gen.rst generally sticks to array, but there are a
> few lapses.
fixed
>
> > +
> > +These type info instances are used by QOM property registration
> > +functions (``object_property_add_qapi()``,
> > +``object_class_property_add_qapi_enum()``, etc.) to associate each
> > +property with its QAPI schema type.
>
> You're using "schema type" like you do above, i.e. for the masked type
> name. Correct?
I mean the type in general
>
> > The ``qom-list`` and
> > +``device-list-properties`` QMP commands then expose the ``qapi-type``
> > +field, giving management tools a formal type reference into the
> > +introspection schema.
>
> As far as I understand, we connect a property of QAPI type to the type's
> QAPITypeInfo, the commands you mentioned then use it to emit the masked
> type name, so that management applications can use that to find a
> property's type in the output of query-qmp-schema.
>
> Problem (already discussed elsewhere): only works when the type is used
> in QMP. If it isn't, we still have QAPITypeInfo, but its .schema_name
> is null. This needs to be documented as a known shortcoming.
ok
>
> Other uses of QAPITypeInfo may emerge in the future. We might also
> replace it by something that supports additional uses; see my ramblings
> in review of PATCH 01. In this series, however, connecting QOM
> properties to their masked QAPI type name is their only use. I
> understand we emit QAPITypeInfo for any QAPI type that could be used as
> QOM property type. Dead data unless it is actually used as QOM property
> type. This is acceptable; we already generate similarly dead visitors.
> But it's worth mentioning in the commit message, I think.
>
> > +
> > +Implicit types (names starting with ``q_``) are skipped.
>
> This isn't quite correct. Array types are also implicitly defined, and
> their names don't start with 'q_'.
>
> Possible fixes:
>
> * Drop the sentence (only if we believe it's not needed)
>
> * Drop the parenthesis, add "other than arrays" or something like that.
>
> * Positive instead of negative: only user-defined and built-in types.
> This could perhaps be worked into the text under "The following files
> are created". May want to add why: only these may be used as property
> types.
rephrased, thanks
>
> > +
> > +Example::
> > +
> > + $ cat qapi-generated/example-qapi-type-infos.h
> > + [Uninteresting stuff omitted...]
> > +
> > + #ifndef EXAMPLE_QAPI_TYPE_INFOS_H
> > + #define EXAMPLE_QAPI_TYPE_INFOS_H
> > +
> > + #include "qapi/qapi-builtin-type-infos.h"
> > +
> > + extern const QAPITypeInfo UserDefOne_type_info;
> > +
> > + extern const QAPITypeInfo UserDefOneList_type_info;
> > +
> > + #endif /* EXAMPLE_QAPI_TYPE_INFOS_H */
> > + $ cat qapi-generated/example-qapi-type-infos.c
> > + [Uninteresting stuff omitted...]
> > +
> > + const QAPITypeInfo UserDefOne_type_info = {
> > + .name = "UserDefOne",
> > + .schema_name = "1",
> > + .list = &UserDefOneList_type_info,
> > + };
> > +
> > + const QAPITypeInfo UserDefOneList_type_info = {
> > + .name = "UserDefOneList",
> > + .schema_name = "[1]",
> > + };
> > +
> > + [Uninteresting stuff omitted...]
> > +
> > +For a modular QAPI schema (see section `Include directives`_), code for
> > +each sub-module SUBDIR/SUBMODULE.json is actually generated into ::
> > +
> > + SUBDIR/$(prefix)qapi-type-infos-SUBMODULE.h
> > + SUBDIR/$(prefix)qapi-type-infos-SUBMODULE.c
> > +
> > +If qapi-gen.py is run with option --builtins, additional files are
> > +created:
> > +
> > + ``qapi-builtin-type-infos.h``
> > + Type info instances for built-in types
> > +
> > + ``qapi-builtin-type-infos.c``
> > + Definitions for the above type info instances
> > diff --git a/meson.build b/meson.build
> > index 19e123423b5..f135eddad12 100644
> > --- a/meson.build
> > +++ b/meson.build
> > @@ -3487,6 +3487,7 @@ qapi_gen_depends = [ meson.current_source_dir() /
> > 'scripts/qapi/__init__.py',
> > meson.current_source_dir() / 'scripts/qapi/parser.py',
> > meson.current_source_dir() / 'scripts/qapi/schema.py',
> > meson.current_source_dir() / 'scripts/qapi/source.py',
> > + meson.current_source_dir() /
> > 'scripts/qapi/type_infos.py',
> > meson.current_source_dir() / 'scripts/qapi/types.py',
> > meson.current_source_dir() / 'scripts/qapi/visit.py',
> > meson.current_source_dir() / 'scripts/qapi-gen.py'
> > diff --git a/scripts/qapi/type_infos.py b/scripts/qapi/type_infos.py
> > new file mode 100644
> > index 00000000000..dc7e2bace9e
> > --- /dev/null
> > +++ b/scripts/qapi/type_infos.py
>
> Please use '-' instead of '_' in filenames for local consistency.
It has to use _ for import, afaik
>
> > @@ -0,0 +1,182 @@
> > +"""
> > +QAPI type info generator
> > +
> > +SPDX-License-Identifier: GPL-2.0-or-later
> > +"""
> > +
> > +from typing import (
> > + Dict,
> > + List,
> > + Optional,
> > + Set,
> > +)
> > +
> > +from .common import c_name, mcgen
> > +from .gen import QAPISchemaModularCVisitor, ifcontext
> > +from .schema import (
> > + QAPISchema,
> > + QAPISchemaAlternatives,
> > + QAPISchemaBranches,
> > + QAPISchemaEnumMember,
> > + QAPISchemaFeature,
> > + QAPISchemaIfCond,
> > + QAPISchemaObjectType,
> > + QAPISchemaObjectTypeMember,
> > + QAPISchemaType,
> > + QAPISchemaVisitor,
> > +)
> > +from .source import QAPISourceInfo
> > +
> > +
> > +class _ArrayTypeCollector(QAPISchemaVisitor):
> > + def __init__(self) -> None:
> > + self.list_types: Set[str] = set()
> > +
> > + def visit_array_type(self,
> > + name: str,
> > + info: Optional[QAPISourceInfo],
> > + ifcond: QAPISchemaIfCond,
> > + element_type: QAPISchemaType) -> None:
> > + self.list_types.add(name)
> > +
> > +
> > +class QAPISchemaGenTypeInfoVisitor(QAPISchemaModularCVisitor):
> > +
> > + def __init__(self, prefix: str, name_map: Dict[str, str],
> > + list_types: Set[str]):
> > + super().__init__(
> > + prefix, 'qapi-type-infos',
> > + ' * Schema-defined QAPI type info',
> > + ' * Built-in QAPI type info', __doc__)
> > + self._name_map = name_map
> > + self._list_types = list_types
> > +
> > + def _begin_builtin_module(self) -> None:
> > + self._genc.preamble_add(mcgen('''
> > +#include "qemu/osdep.h"
> > +#include "qapi/qapi-builtin-types.h"
> > +#include "qapi/qapi-builtin-type-infos.h"
> > +'''))
> > + self._genh.preamble_add(mcgen('''
> > +#include "qapi/qapi-type-info.h"
> > +'''))
> > +
> > + def _begin_user_module(self, name: str) -> None:
> > + type_infos = self._module_basename('qapi-type-infos', name)
> > + types = self._module_basename('qapi-types', name)
> > + self._genc.preamble_add(mcgen('''
> > +#include "qemu/osdep.h"
> > +#include "%(types)s.h"
> > +#include "%(type_infos)s.h"
> > +''',
> > + types=types,
> > + type_infos=type_infos))
> > + self._genh.preamble_add(mcgen('''
> > +#include "qapi/qapi-builtin-type-infos.h"
> > +'''))
> > +
> > + def _gen_type_info(self, name: str,
> > + ifcond: Optional[QAPISchemaIfCond] = None,
> > + with_lookup: bool = False,
> > + with_list: bool = False,
> > + schema_name: Optional[str] = None) -> None:
> > + c_id = c_name(name + '_type_info')
> > + lookup = ''
> > + if with_lookup:
> > + lookup = mcgen('''
> > + .lookup = &%(c_name)s_lookup,
> > +''',
> > + c_name=c_name(name))
> > + list_ref = ''
> > + if with_list:
> > + list_ref = mcgen('''
> > + .list = &%(list_id)s,
> > +''',
> > + list_id=c_name(name + 'List_type_info'))
> > + if schema_name is None:
> > + masked = self._name_map.get(name)
> > + schema_name = '"%s"' % masked if masked is not None else 'NULL'
> > + else:
> > + schema_name = '"%s"' % schema_name
> > + with ifcontext(ifcond or QAPISchemaIfCond(),
> > + self._genh, self._genc):
> > + self._genh.add(mcgen('''
> > +
> > +extern const QAPITypeInfo %(c_id)s;
> > +''',
> > + c_id=c_id))
> > + self._genc.add(mcgen('''
> > +
> > +const QAPITypeInfo %(c_id)s = {
> > + .name = "%(name)s",
> > + .schema_name = %(schema_name)s,
> > +%(lookup)s%(list_ref)s};
>
> This substitutes values of mcgen() into mcgen(). That's not how it
> wants to be used. Please ...
>
> > +''',
> > + c_id=c_id, name=name,
> > + schema_name=schema_name,
> > + lookup=lookup,
> > + list_ref=list_ref))
>
> ... try something like
>
> self._genh.add(mcgen('''
>
> extern const QAPITypeInfo %(c_id)s;
> ''',
> c_id=c_id))
> self._genc.add(mcgen('''
>
> const QAPITypeInfo %(c_id)s = {
> .name = "%(name)s",
> .schema_name = %(schema_name)s,
> ''',
> c_id=c_id, name=name,
> schema_name=schema_name))
>
> if with_lookup:
> self._genc.add(mcgen('''
> .lookup = &%(c_name)s_lookup,
> ''',
> c_name=c_name(name))
>
> and so forth.
ok
>
> > +
> > + def _has_list(self, name: str) -> bool:
> > + return name + 'List' in self._list_types
>
> Simpler, I think:
>
> return self._schema.lookup_type(name + 'List')
>
> where self._schema is set in .visit_begin().
ok
>
> > +
> > + def visit_builtin_type(self,
> > + name: str,
> > + info: Optional[QAPISourceInfo],
> > + json_type: str) -> None:
> > + self._gen_type_info(name, with_list=self._has_list(name))
> > +
> > + def visit_enum_type(self,
> > + name: str,
> > + info: Optional[QAPISourceInfo],
> > + ifcond: QAPISchemaIfCond,
> > + features: List[QAPISchemaFeature],
> > + members: List[QAPISchemaEnumMember],
> > + prefix: Optional[str]) -> None:
> > + self._gen_type_info(name, ifcond, with_lookup=True,
> > + with_list=self._has_list(name))
> > +
> > + def visit_object_type(self,
> > + name: str,
> > + info: Optional[QAPISourceInfo],
> > + ifcond: QAPISchemaIfCond,
> > + features: List[QAPISchemaFeature],
> > + base: Optional[QAPISchemaObjectType],
> > + members: List[QAPISchemaObjectTypeMember],
> > + branches: Optional[QAPISchemaBranches]) -> None:
> > + if name.startswith('q_'):
>
> Duplicates QAPISchemaObjectType.is_implicit(). Similar code in types.py
> and visit.py. Not this patch's fault; moving on.
>
> > + return
> > + self._gen_type_info(name, ifcond,
> > + with_list=self._has_list(name))
> > +
> > + def visit_array_type(self,
> > + name: str,
> > + info: Optional[QAPISourceInfo],
> > + ifcond: QAPISchemaIfCond,
> > + element_type: QAPISchemaType) -> None:
> > + elem_schema = self._name_map.get(element_type.name,
> > + element_type.name)
> > + self._gen_type_info(name, ifcond,
> > + schema_name='[' + elem_schema + ']')
> > +
> > + def visit_alternate_type(self,
> > + name: str,
> > + info: Optional[QAPISourceInfo],
> > + ifcond: QAPISchemaIfCond,
> > + features: List[QAPISchemaFeature],
> > + alternatives: QAPISchemaAlternatives) -> None:
> > + self._gen_type_info(name, ifcond,
> > + with_list=self._has_list(name))
> > +
> > +
> > +def gen_type_infos(schema: QAPISchema,
> > + output_dir: str,
> > + prefix: str,
> > + opt_builtins: bool,
> > + name_map: Dict[str, str]) -> None:
> > + collector = _ArrayTypeCollector()
> > + schema.visit(collector)
> > + vis = QAPISchemaGenTypeInfoVisitor(prefix, name_map,
> > + collector.list_types)
> > + schema.visit(vis)
> > + vis.write(output_dir, opt_builtins)
>