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: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <1442401589-24189-27-git-send-email-armbru@redhat.com>
This commit is contained in:
Markus Armbruster 2015-09-16 13:06:29 +02:00
parent 39a1815816
commit 1a9a507b2e
3 changed files with 56 additions and 33 deletions

View File

@ -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...]

View File

@ -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.

View File

@ -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)
return typ.name
# 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)