Commit 1a9a507b authored by Markus Armbruster's avatar Markus Armbruster
Browse files

qapi-introspect: Hide type names



To eliminate the temptation for clients to look up types by name
(which are not ABI), replace all type names by meaningless strings.

Reduces output of query-schema by 13 out of 85KiB.

As a debugging aid, provide option -u to suppress the hiding.

Signed-off-by: default avatarMarkus Armbruster <armbru@redhat.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
Message-Id: <1442401589-24189-27-git-send-email-armbru@redhat.com>
parent 39a18158
Loading
Loading
Loading
Loading
+17 −19
Original line number Diff line number Diff line
@@ -530,13 +530,16 @@ additional variant members depending on the value of meta-type.
Each SchemaInfo object describes a wire ABI entity of a certain
meta-type: a command, event or one of several kinds of type.

SchemaInfo for entities defined in the QAPI schema have the same name
as in the schema.  This is the case for all commands and events, and
most types.
SchemaInfo for commands and events have the same name as in the QAPI
schema.

Command and event names are part of the wire ABI, but type names are
not.  Therefore, looking up a type by its name in the QAPI schema is
wrong.  Look up the command or event, then follow references by name.
not.  Therefore, the SchemaInfo for types have auto-generated
meaningless names.  For readability, the examples in this section use
meaningful type names instead.

To examine a type, start with a command or event using it, then follow
references by name.

QAPI schema definitions not reachable that way are omitted.

@@ -567,8 +570,7 @@ object type without members. The event may not have a data member on
the wire then.

Each command or event defined with dictionary-valued 'data' in the
QAPI schema implicitly defines an object type called ":obj-NAME-arg",
where NAME is the command or event's name.
QAPI schema implicitly defines an object type.

Example: the SchemaInfo for EVENT_C from section Events

@@ -623,12 +625,9 @@ Note that base types are "flattened": its members are included in the

A simple union implicitly defines an enumeration type for its implicit
discriminator (called "type" on the wire, see section Union types).
Such a type's name is made by appending "Kind" to the simple union's
name.

A simple union implicitly defines an object type for each of its
variants.  The type's name is ":obj-NAME-wrapper", where NAME is the
name of the name of the variant's type.
variants.

Example: the SchemaInfo for simple union BlockdevOptions from section
Union types
@@ -659,8 +658,7 @@ Example: the SchemaInfo for BlockRef from section Alternate types

The SchemaInfo for an array type has meta-type "array", and variant
member "element-type", which names the array's element type.  Array
types are implicitly defined.  An array type's name is made by
appending "List" to its element type's name.
types are implicitly defined.

Example: the SchemaInfo for ['str']

@@ -1067,13 +1065,13 @@ Example:
[Uninteresting stuff omitted...]

    const char example_qmp_schema_json[] = "["
        "{\"arg-type\": \":empty\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
        "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
        "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
        "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
        "{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
        "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
        "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}, "
        "{\"members\": [], \"meta-type\": \"object\", \"name\": \":empty\"}, "
        "{\"members\": [{\"name\": \"arg1\", \"type\": \"UserDefOne\"}], \"meta-type\": \"object\", \"name\": \":obj-my-command-arg\"}, "
        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"UserDefOne\"}, "
        "{\"arg-type\": \":obj-my-command-arg\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"UserDefOne\"}]";
        "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
    $ cat qapi-generated/example-qmp-introspect.h
[Uninteresting stuff omitted...]

+4 −8
Original line number Diff line number Diff line
@@ -75,17 +75,13 @@
# @SchemaInfo
#
# @name: the entity's name, inherited from @base.
#         Entities defined in the QAPI schema have the name defined in
#         the schema.  Implicitly defined entities have generated
#         names.  See docs/qapi-code-gen.txt section "Client JSON
#         Protocol introspection" for details.
#        Commands and events have the name defined in the QAPI schema.
#        Unlike command and event names, type names are not part of
#        the wire ABI.  Consequently, type names are meaningless
#        strings here.
#
# All references to other SchemaInfo are by name.
#
# Command and event names are part of the wire ABI, but type names are
# not.  Therefore, looking up a type by "well-known" name is wrong.
# Look up the command or event, then follow the references.
#
# @meta-type: the entity's meta type, inherited from @base.
#
# Additional members depend on the value of @meta-type.
+35 −6
Original line number Diff line number Diff line
@@ -40,32 +40,37 @@ def to_c_string(string):


class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
    def __init__(self):
    def __init__(self, unmask):
        self._unmask = unmask
        self.defn = None
        self.decl = None
        self._schema = None
        self._jsons = None
        self._used_types = None
        self._name_map = None

    def visit_begin(self, schema):
        self._schema = schema
        self._jsons = []
        self._used_types = []
        self._name_map = {}
        return QAPISchemaType   # don't visit types for now

    def visit_end(self):
        # visit the types that are actually used
        jsons = self._jsons
        self._jsons = []
        for typ in self._used_types:
            typ.visit(self)
        self._jsons.sort()
        # generate C
        # TODO can generate awfully long lines
        jsons.extend(self._jsons)
        name = prefix + 'qmp_schema_json'
        self.decl = mcgen('''
extern const char %(c_name)s[];
''',
                          c_name=c_name(name))
        lines = to_json(self._jsons).split('\n')
        lines = to_json(jsons).split('\n')
        c_string = '\n    '.join([to_c_string(line) for line in lines])
        self.defn = mcgen('''
const char %(c_name)s[] = %(c_string)s;
@@ -75,6 +80,14 @@ const char %(c_name)s[] = %(c_string)s;
        self._schema = None
        self._jsons = None
        self._used_types = None
        self._name_map = None

    def _name(self, name):
        if self._unmask:
            return name
        if name not in self._name_map:
            self._name_map[name] = '%d' % len(self._name_map)
        return self._name_map[name]

    def _use_type(self, typ):
        # Map the various integer types to plain int
@@ -86,9 +99,16 @@ const char %(c_name)s[] = %(c_string)s;
        # Add type to work queue if new
        if typ not in self._used_types:
            self._used_types.append(typ)
        # Clients should examine commands and events, not types.  Hide
        # type names to reduce the temptation.  Also saves a few
        # characters.
        if isinstance(typ, QAPISchemaBuiltinType):
            return typ.name
        return self._name(typ.name)

    def _gen_json(self, name, mtype, obj):
        if mtype != 'command' and mtype != 'event' and mtype != 'builtin':
            name = self._name(name)
        obj['name'] = name
        obj['meta-type'] = mtype
        self._jsons.append(obj)
@@ -140,7 +160,16 @@ const char %(c_name)s[] = %(c_string)s;
        arg_type = arg_type or self._schema.the_empty_object_type
        self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})

(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
# Debugging aid: unmask QAPI schema's type names
# We normally mask them, because they're not QMP wire ABI
opt_unmask = False

(input_file, output_dir, do_c, do_h, prefix, opts) = \
    parse_command_line("u", ["unmask-non-abi-names"])

for o, a in opts:
    if o in ("-u", "--unmask-non-abi-names"):
        opt_unmask = True

c_comment = '''
/*
@@ -176,7 +205,7 @@ fdef.write(mcgen('''
                 prefix=prefix))

schema = QAPISchema(input_file)
gen = QAPISchemaGenIntrospectVisitor()
gen = QAPISchemaGenIntrospectVisitor(opt_unmask)
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)