All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection
@ 2015-09-03 14:29 Markus Armbruster
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 01/32] qapi: Rename class QAPISchema to QAPISchemaParser Markus Armbruster
                   ` (32 more replies)
  0 siblings, 33 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Still RFC, because we probably want to drop the "just to facilitate
review" patches, and maybe squash PATCH 31 or 32 into PATCH 30.

* PATCH 01-24 basically replace the interemediate representation.  The
  replacement isn't complete, but fully functional.  See PATCH 02 for
  rationale and future work.

  PATCH 06-09 and 16-17 are temporary scaffolding to ease review.  I
  don't intend to include them in future non-RFC revisions.

* PATCH 25-29 replace the '**' type bypass by a proper top type called
  'any'.

* PATCH 30 is the QMP introspection.

* PATCH 31-32 are minor variations to hide a bit more detail from
  clients.

I'm afraid the size of my series will strain reviewers.  I did my
level best to split things up into reviewable pieces.

Series is based on "[PATCH 00/26] qapi: Another round of fixes and
cleanups" (which actually has 33 patches now).  It also needs my
"[PATCH for-2.4 0/2] qom: Fix misuse of Error API" to pass tests.

You can fetch it from my git://repo.or.cz/qemu/armbru.git branch
qapi-introspect.

v4:
* Straightforward rebase, R-bys retained unless noted otherwise.
* PATCH 02:
  - Add a TODO on catching name collisions [Eric]
  - Unify how QAPISchemaObjectType.__init__() asserts "either None or
    isinstance" [Eric]
  - Drop bogus "not implemented" comment in simple_union_type() [Eric]
  - Use iteritems() where appropriate [Eric]
  - Drop dead conditional in _def_command() [Eric]
* PATCH 03:
  - Define json_type() always
    Just to be neat; it only gets called when it was defined before.
* PATCH 04,10-12,15,30:
  - Give visit_begin() parameter schema from the start instead of
    adding it in PATCH 30 [Eric]
* PATCH 07,16:
  - Touch up commit message's hints for reviewers [Eric]
* PATCH 10+11:
  - Reword a comment more clearly [Eric]
* PATCH 11:
  - Fix test_validate_fail_union_flat_no_discrim() [Eric]
* PATCH 18:
  - Drop dead commit message paragraph [Eric]
* PATCH 26:
  - Cover ['any'] in qapi-schema-test.json [Eric]
* PATCH 30:
  - Rework commit message
  - New docs/qapi-code-gen.txt section "Client JSON Protocol
    introspection"
  - Fix docs/qapi-code-gen.txt pastos [Eric]
  - Rearrange and document qapi/introspect.json
    The forward references are intentional.  I picked this order to
    make the file more readable.
  - Fill in proper qmp-commands.hx documentation
  - Uniform spacing in generated code
  - Put the commit message's TODO on long lines into code, too
  - Drop _gen_json()'s useless default argument [Eric]
  - Fix name of simple unions' implicit tag [Eric]
  - Adjust white-space to make pylint happier
  - Fix indentation in test-qmp-input-strict.c [Eric]
* PATCH 31:
  - Commit message tweak
  - Update docs/qapi-code-gen.txt
  - R-by dropped to make sure the doc gets reviewed.
* PATCH 32:
  - New option -u to suppress the type name hiding (debugging aid)
    Easier than generating comments.  We can still generate them
    later.
  - Update docs/qapi-code-gen.txt and qapi/introspect.json comments
  - Use %d to format integers [Eric]
  - R-by dropped to make sure the doc gets reviewed.
* This change log is again long, but compared to last time, the
  changes are rather local, and most of them are to the last three
  patches.  I think we're converging.

v3:
* Straightforward rebase, R-bys retained unless noted otherwise.  Due
  to my widespread changes, you may prefer to review from scratch
  anyway.
* Old PATCH 01-19 split off into separate non-RFC series "[PATCH
  00/26] qapi: Another round of fixes and cleanups"
* PATCH 02:
  - Commit message spelling fix [Eric]
  - Clarify patch's non-effect in commit message.
  - Assert enumeration constants are unique.
  - Assert object's base is an object without variants [Eric].
  - Drop a few superfluous check() recursions.
  - Use arg_type, ret_type instead of args, ret for commands [Eric],
    and arg_type instead of data for events.
  - Lower simple variants to flat ones as described on
    qapi-code-gen.txt.  Replace QAPISchemaObjectType's .flat by
    .simple_union_type(), for use by pre-existing code-generation
    warts.
  - _make_implicit_object_type() idempotent (previous item needs
    that).
  - Clean up how empty command argument dictionaries work [Eric].
  - Same for event.
  - No R-by to drop.
* PATCH 03:
  - Fix QAPISchemaEnumType.c_null() for empty enums [Eric].
  - R-by dropped.
* PATCH 04:
  - Use arg_type, ret_type instead of args, ret for commands [Eric],
    and arg_type instead of data for events.
* PATCH 05:
  - Use arg_type, ret_type instead of args, ret for commands [Eric],
    and arg_type instead of data for events.
  - .flat is gone, drop code to print it.
  - Expected test output changed due to revision of PATCH 02.
* PATCH 06-09,16-17: New, replacing old PATCH 25 "qapi: Make
  generators work on sorted schema expressions".
* PATCH 10:
  - Update qapi-code-gen.txt with the patch's changes.
  - Earlier patches ripple into gen_union().
  - Nicer comment on -b [Eric].
  - Objects with both members and unions aren't implemented.  Add an
    assertion [Eric].
  - No R-by to drop.
* PATCH 11:
  - Earlier patches ripple into gen_visit_union().
  - Rename guard QAPI_VISIT_BUILTIN_VISITOR_DECL to
    QAPI_VISIT_BUILTIN.
  - Nicer comment on -b [Eric].
  - Objects with both members and unions aren't implemented.  Add an
    assertion [Eric].
  - No R-by to drop.
* PATCH 12:
  - Use arg_type, ret_type instead of args, ret [Eric].
  - Zap .regy unconditionally in visit_end (squashed from PATCH 30).
* PATCH 13:
  - Commit message fixed and improved [Eric].
  - Update qapi-code-gen.txt with the patch's changes.
* PATCH 15
  - Copyright notice pasto fixed [Eric].
  - Minimize patch by delaying more renames to PATCH 19.
  - Use arg_type instead of data.
  - Free .event_names in visit_end(), moved here from PATCH 30.
* PATCH 18:
  - Moved; was right after PATCH 12 before.
  - One hunk dropped due to "[PATCH 25/26] qapi-commands: Drop useless
    initialization".
* PATCH 19:
  - Use arg_type, ret_type instead of args, ret for commands [Eric],
    and arg_type instead of data or params for events.
  - Consistently use single quotes for strings holding generated code
    [Eric].
  - Generate just 'FOO' instead of 'struct FOO' when possible [Eric].
  - Earlier patches ripple into gen_union() and gen_visit_union().
  - R-by dropped (death by a thousand cuts).
* PATCH 20:
  - Straightforward conflicts due to new "[PATCH 14/26] qapi: Document
    that input visitor semantics are prone to leaks" and "[PATCH
    15/26] qapi: Document shortcoming with union 'data' branch".
* PATCH 21:
  - Use arg_type, ret_type instead of args, ret [Eric].
  - Straightforward conflict due to "[PATCH 25/26] qapi-commands: Drop
    useless initialization".
* PATCH 22:
  - Rename in qmp.c's comment, too [Eric].
* PATCH 23:
  - Use arg_type, ret_type instead of args, ret for commands [Eric],
    and arg_type instead of data for events.
  - Consistently use single quotes for strings holding generated code
    [Eric].
* PATCH 24:
  - Update qapi-code-gen.txt with the patch's changes.
  - Use arg_type, ret_type instead of args, ret [Eric].
  - Free .visited_ret_types in visit_end(), moved here from PATCH 30.
* PATCH 26:
  - Indentation fix [Eric].
  - Some test case updates moved here from PATCH 29: move positive
    test case from args-returns-any.json to qapi-schema-test.json
    (with new qmp_guest_sync() dummy in test-qmp-commands.c), move
    negative test case to args-any.json, drop bogus test case.
  - Rename flat-union-base-star.* to flat-union-base-any.*.
  - Test QMP input visitor's visit_type_any() doesn't add extra
    members [Eric].
* PATCH 27:
  - Drop a TODO obsoleted by the patch [Eric].
* PATCH 28:
  - Old PATCH 42+43 squashed together [Eric].
* PATCH 29:
  - Documentation update on 'gen' was botched, rephrase [Eric].
  - Some test case updates move to PATCH 26.
* PATCH 30:
  - success-response isn't implementation detail, add TODO and fix
    commit message [Eric].
  - Cover new qapi-introspect.py in qapi-code-gen.txt [Eric].
  - Clarify qmp_query_schema()'s comment.
  - Fix introspect.json's comment on default values [Eric].
  - Replace SchemaInfoObjectVariant member 'members' by 'type' [Eric].
  - New SchemaInfoAlternateMember [Eric].
  - Use arg_type, ret_type instead of args, ret for commands [Eric],
    and arg_type instead of data for events.
  - Rewrite actual code generation: instead of generating a list of
    JSON strings directly, first generate a list of dicts, then format
    them as JSON.
  - Fix to use " instead of ' for JSON strings [Eric].
  - Make the generated array const [Eric].
  - Fix accidental drop of a line of code [Eric].
  - Fix file name in tests/.gitignore [Eric].
  - Revision of PATCH 02 ripples into test output diffs.
  - Move new test from test-qmp-input-visitor.c to
    test-qmp-input-strict.c to make it catch extra members.
  - R-by dropped.
* PATCH 32:
  - Update qapi-code-gen.txt with the patch's changes.
  - Rewrite actual code generation: instead of generating a list of
    JSON strings directly, first generate a list of dicts, then format
    them as JSON.
  - Use plain integers as type names [Eric].
  - Don't hide names of builtin types [Eric].
  - Put a TODO on type name comments in commit message.

Markus Armbruster (32):
  qapi: Rename class QAPISchema to QAPISchemaParser
  qapi: New QAPISchema intermediate reperesentation
  qapi: QAPISchema code generation helper methods
  qapi: New QAPISchemaVisitor
  tests/qapi-schema: Convert test harness to QAPISchemaVisitor
  qapi: Split up some typedefs to ease review
  qapi: Generate comments to simplify splitting for review
  Revert "qapi: Generate comments to simplify splitting for review"
  Revert "qapi: Split up some typedefs to ease review"
  qapi-types: Convert to QAPISchemaVisitor, fixing flat unions
  qapi-visit: Convert to QAPISchemaVisitor, fixing bugs
  qapi-commands: Convert to QAPISchemaVisitor
  qapi: De-duplicate enum code generation
  qapi-event: Eliminate global variable event_enum_value
  qapi-event: Convert to QAPISchemaVisitor, fixing data with base
  qapi: Generate comments to simplify splitting for review
  Revert "qapi: Generate comments to simplify splitting for review"
  qapi: Replace dirty is_c_ptr() by method c_null()
  qapi: Clean up after recent conversions to QAPISchemaVisitor
  qapi-visit: Rearrange code a bit
  qapi-commands: Rearrange code
  qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO()
  qapi: De-duplicate parameter list generation
  qapi-commands: De-duplicate output marshaling functions
  qapi: Improve built-in type documentation
  qapi: Introduce a first class 'any' type
  qom: Don't use 'gen': false for qom-get, qom-set, object-add
  qapi-schema: Fix up misleading specification of netdev_add
  qapi: Pseudo-type '**' is now unused, drop it
  qapi: New QMP command query-schema for QMP schema introspection
  qapi-introspect: Map all integer types to 'int'
  qapi-introspect: Hide type names

 .gitignore                                         |   1 +
 Makefile                                           |   9 +-
 Makefile.objs                                      |   4 +-
 docs/qapi-code-gen.txt                             | 349 ++++++++---
 docs/writing-qmp-commands.txt                      |   8 +-
 include/monitor/monitor.h                          |   3 -
 include/qapi/visitor-impl.h                        |   2 +
 include/qapi/visitor.h                             |   1 +
 monitor.c                                          |  17 +-
 qapi-schema.json                                   |  25 +-
 qapi/introspect.json                               | 266 +++++++++
 qapi/qapi-dealloc-visitor.c                        |   9 +
 qapi/qapi-visit-core.c                             |   6 +
 qapi/qmp-input-visitor.c                           |  11 +
 qapi/qmp-output-visitor.c                          |   9 +
 qmp-commands.hx                                    | 267 +++++----
 qmp.c                                              |  27 +-
 scripts/qapi-commands.py                           | 272 ++++-----
 scripts/qapi-event.py                              | 239 +++-----
 scripts/qapi-introspect.py                         | 210 +++++++
 scripts/qapi-types.py                              | 355 +++++-------
 scripts/qapi-visit.py                              | 350 +++++------
 scripts/qapi.py                                    | 637 +++++++++++++++++----
 tests/.gitignore                                   |   1 +
 tests/Makefile                                     |  15 +-
 tests/qapi-schema/alternate-good.out               |  16 +-
 tests/qapi-schema/args-any.err                     |   1 +
 .../{type-bypass-no-gen.exit => args-any.exit}     |   0
 tests/qapi-schema/args-any.json                    |   2 +
 .../qapi-schema/{type-bypass.err => args-any.out}  |   0
 tests/qapi-schema/args-member-array.out            |  14 +-
 tests/qapi-schema/comments.out                     |   5 +-
 tests/qapi-schema/empty.out                        |   4 +-
 tests/qapi-schema/enum-empty.out                   |   5 +-
 tests/qapi-schema/event-case.out                   |   5 +-
 tests/qapi-schema/flat-union-base-any.err          |   1 +
 ...ion-base-star.exit => flat-union-base-any.exit} |   0
 ...ion-base-star.json => flat-union-base-any.json} |   2 +-
 ...e-bypass-no-gen.out => flat-union-base-any.out} |   0
 tests/qapi-schema/flat-union-base-star.err         |   1 -
 tests/qapi-schema/flat-union-base-star.out         |   0
 tests/qapi-schema/flat-union-reverse-define.out    |  22 +-
 tests/qapi-schema/ident-with-escape.out            |   8 +-
 tests/qapi-schema/include-relpath.out              |   5 +-
 tests/qapi-schema/include-repetition.out           |   5 +-
 tests/qapi-schema/include-simple.out               |   5 +-
 tests/qapi-schema/indented-expr.out                |   8 +-
 tests/qapi-schema/qapi-schema-test.json            |  11 +-
 tests/qapi-schema/qapi-schema-test.out             | 222 +++++--
 tests/qapi-schema/returns-int.out                  |   6 +-
 tests/qapi-schema/test-qapi.py                     |  38 +-
 tests/qapi-schema/type-bypass-no-gen.err           |   1 -
 tests/qapi-schema/type-bypass-no-gen.json          |   2 -
 tests/qapi-schema/type-bypass.exit                 |   1 -
 tests/qapi-schema/type-bypass.json                 |   2 -
 tests/qapi-schema/type-bypass.out                  |   3 -
 tests/test-qmp-commands.c                          |   5 +
 tests/test-qmp-input-strict.c                      |  59 +-
 tests/test-qmp-input-visitor.c                     |  49 +-
 tests/test-qmp-output-visitor.c                    |  53 ++
 60 files changed, 2462 insertions(+), 1192 deletions(-)
 create mode 100644 qapi/introspect.json
 create mode 100644 scripts/qapi-introspect.py
 create mode 100644 tests/qapi-schema/args-any.err
 rename tests/qapi-schema/{type-bypass-no-gen.exit => args-any.exit} (100%)
 create mode 100644 tests/qapi-schema/args-any.json
 rename tests/qapi-schema/{type-bypass.err => args-any.out} (100%)
 create mode 100644 tests/qapi-schema/flat-union-base-any.err
 rename tests/qapi-schema/{flat-union-base-star.exit => flat-union-base-any.exit} (100%)
 rename tests/qapi-schema/{flat-union-base-star.json => flat-union-base-any.json} (95%)
 rename tests/qapi-schema/{type-bypass-no-gen.out => flat-union-base-any.out} (100%)
 delete mode 100644 tests/qapi-schema/flat-union-base-star.err
 delete mode 100644 tests/qapi-schema/flat-union-base-star.out
 delete mode 100644 tests/qapi-schema/type-bypass-no-gen.err
 delete mode 100644 tests/qapi-schema/type-bypass-no-gen.json
 delete mode 100644 tests/qapi-schema/type-bypass.exit
 delete mode 100644 tests/qapi-schema/type-bypass.json
 delete mode 100644 tests/qapi-schema/type-bypass.out

-- 
2.4.3

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 01/32] qapi: Rename class QAPISchema to QAPISchemaParser
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 15:55   ` Daniel P. Berrange
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation Markus Armbruster
                   ` (31 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

I want to name a new class QAPISchema.

While there, make it a new-style class.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 817d824..a0165dd 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -103,7 +103,7 @@ class QAPIExprError(Exception):
         return error_path(self.info['parent']) + \
             "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
 
-class QAPISchema:
+class QAPISchemaParser(object):
 
     def __init__(self, fp, previously_included = [], incl_info = None):
         abs_fname = os.path.abspath(fp.name)
@@ -149,8 +149,8 @@ class QAPISchema:
                 except IOError, e:
                     raise QAPIExprError(expr_info,
                                         '%s: %s' % (e.strerror, include))
-                exprs_include = QAPISchema(fobj, previously_included,
-                                           expr_info)
+                exprs_include = QAPISchemaParser(fobj, previously_included,
+                                                 expr_info)
                 self.exprs.extend(exprs_include.exprs)
             else:
                 expr_elem = {'expr': expr,
@@ -751,7 +751,7 @@ def check_exprs(exprs):
 
 def parse_schema(fname):
     try:
-        schema = QAPISchema(open(fname, "r"))
+        schema = QAPISchemaParser(open(fname, "r"))
         return check_exprs(schema.exprs)
     except (QAPISchemaError, QAPIExprError), e:
         print >>sys.stderr, e
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 01/32] qapi: Rename class QAPISchema to QAPISchemaParser Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 16:11   ` Daniel P. Berrange
                     ` (2 more replies)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 03/32] qapi: QAPISchema code generation helper methods Markus Armbruster
                   ` (30 subsequent siblings)
  32 siblings, 3 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

The QAPI code generators work with a syntax tree (nested dictionaries)
plus a few symbol tables (also dictionaries) on the side.

They have clearly outgrown these simple data structures.  There's lots
of rummaging around in dictionaries, and information is recomputed on
the fly.  For the work I'm going to do, I want more clearly defined
and more convenient interfaces.

Going forward, I also want less coupling between the back-ends and the
syntax tree, to make messing with the syntax easier.

Create a bunch of classes to represent QAPI schemata.

Have the QAPISchema initializer call the parser, then walk the syntax
tree to create the new internal representation, and finally perform
semantic analysis.

Shortcut: the semantic analysis still relies on existing check_exprs()
to do the actual semantic checking.  All this code needs to move into
the classes.  Mark as TODO.

Catching name collisions in generated code would be nice.  Mark as
TODO.

We generate array types eagerly, even though most of them aren't used.
Mark as TODO.

Nothing uses the new intermediate representation just yet, thus no
change to generated files.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-commands.py       |   2 +-
 scripts/qapi-event.py          |   2 +-
 scripts/qapi-types.py          |   2 +-
 scripts/qapi-visit.py          |   2 +-
 scripts/qapi.py                | 360 +++++++++++++++++++++++++++++++++++++++--
 tests/qapi-schema/test-qapi.py |   2 +-
 6 files changed, 356 insertions(+), 14 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 890ce5d..12bdc4c 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -272,7 +272,7 @@ for o, a in opts:
     if o in ("-m", "--middle"):
         middle_mode = True
 
-exprs = parse_schema(input_file)
+exprs = QAPISchema(input_file).get_exprs()
 commands = filter(lambda expr: expr.has_key('command'), exprs)
 commands = filter(lambda expr: not expr.has_key('gen'), commands)
 
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 7f238df..aec2d32 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -263,7 +263,7 @@ fdecl.write(mcgen('''
 ''',
                   prefix=prefix))
 
-exprs = parse_schema(input_file)
+exprs = QAPISchema(input_file).get_exprs()
 
 event_enum_name = c_name(prefix + "QAPIEvent", protect=False)
 event_enum_values = []
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index f2428f3..d162ca2 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -336,7 +336,7 @@ fdecl.write(mcgen('''
 #include <stdint.h>
 '''))
 
-exprs = parse_schema(input_file)
+exprs = QAPISchema(input_file).get_exprs()
 
 fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
 for typename in builtin_types.keys():
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 3cd662b..c493964 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -446,7 +446,7 @@ fdecl.write(mcgen('''
 ''',
                   prefix=prefix))
 
-exprs = parse_schema(input_file)
+exprs = QAPISchema(input_file).get_exprs()
 
 # to avoid header dependency hell, we always generate declarations
 # for built-in types in our header files and simply guard them
diff --git a/scripts/qapi.py b/scripts/qapi.py
index a0165dd..c42bea1 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -302,6 +302,8 @@ class QAPISchemaParser(object):
 
 #
 # Semantic analysis of schema expressions
+# TODO fold into QAPISchema
+# TODO catching name collisions in generated code would be nice
 #
 
 def find_base_fields(base):
@@ -747,15 +749,355 @@ def check_exprs(exprs):
         else:
             assert False, 'unexpected meta type'
 
-    return map(lambda expr_elem: expr_elem['expr'], exprs)
-
-def parse_schema(fname):
-    try:
-        schema = QAPISchemaParser(open(fname, "r"))
-        return check_exprs(schema.exprs)
-    except (QAPISchemaError, QAPIExprError), e:
-        print >>sys.stderr, e
-        exit(1)
+    return exprs
+
+#
+# Schema compiler frontend
+#
+
+class QAPISchemaEntity(object):
+    def __init__(self, name, info):
+        assert isinstance(name, str)
+        self.name = name
+        self.info = info
+    def check(self, schema):
+        pass
+
+class QAPISchemaType(QAPISchemaEntity):
+    pass
+
+class QAPISchemaBuiltinType(QAPISchemaType):
+    def __init__(self, name):
+        QAPISchemaType.__init__(self, name, None)
+
+class QAPISchemaEnumType(QAPISchemaType):
+    def __init__(self, name, info, values):
+        QAPISchemaType.__init__(self, name, info)
+        for v in values:
+            assert isinstance(v, str)
+        self.values = values
+    def check(self, schema):
+        assert len(set(self.values)) == len(self.values)
+
+class QAPISchemaArrayType(QAPISchemaType):
+    def __init__(self, name, info, element_type):
+        QAPISchemaType.__init__(self, name, info)
+        assert isinstance(element_type, str)
+        self.element_type_name = element_type
+        self.element_type = None
+    def check(self, schema):
+        self.element_type = schema.lookup_type(self.element_type_name)
+        assert self.element_type
+
+class QAPISchemaObjectType(QAPISchemaType):
+    def __init__(self, name, info, base, local_members, variants):
+        QAPISchemaType.__init__(self, name, info)
+        assert base == None or isinstance(base, str)
+        for m in local_members:
+            assert isinstance(m, QAPISchemaObjectTypeMember)
+        assert variants == None \
+            or isinstance(variants, QAPISchemaObjectTypeVariants)
+        self.base_name = base
+        self.base = None
+        self.local_members = local_members
+        self.variants = variants
+        self.members = None
+    def check(self, schema):
+        assert self.members != False    # not running in cycles
+        if self.members:
+            return
+        self.members = False            # mark as being checked
+        if self.base_name:
+            self.base = schema.lookup_type(self.base_name)
+            assert isinstance(self.base, QAPISchemaObjectType)
+            assert not self.base.variants # not implemented
+            self.base.check(schema)
+            members = list(self.base.members)
+        else:
+            members = []
+        seen = {}
+        for m in members:
+            seen[m.name] = m
+        for m in self.local_members:
+            m.check(schema, members, seen)
+        if self.variants:
+            self.variants.check(schema, members, seen)
+        self.members = members
+
+class QAPISchemaObjectTypeMember(object):
+    def __init__(self, name, typ, optional):
+        assert isinstance(name, str)
+        assert isinstance(typ, str)
+        assert isinstance(optional, bool)
+        self.name = name
+        self.type_name = typ
+        self.type = None
+        self.optional = optional
+    def check(self, schema, all_members, seen):
+        assert self.name not in seen
+        self.type = schema.lookup_type(self.type_name)
+        assert self.type
+        all_members.append(self)
+        seen[self.name] = self
+
+class QAPISchemaObjectTypeVariants(object):
+    def __init__(self, tag_name, tag_enum, variants):
+        assert tag_name == None or isinstance(tag_name, str)
+        assert tag_enum == None or isinstance(tag_enum, str)
+        for v in variants:
+            assert isinstance(v, QAPISchemaObjectTypeVariant)
+        self.tag_name = tag_name
+        if tag_name:
+            assert not tag_enum
+            self.tag_member = None
+        else:
+            self.tag_member = QAPISchemaObjectTypeMember('kind', tag_enum,
+                                                         False)
+        self.variants = variants
+    def check(self, schema, members, seen):
+        if self.tag_name:
+            self.tag_member = seen[self.tag_name]
+        else:
+            self.tag_member.check(schema, members, seen)
+        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
+        for v in self.variants:
+            vseen = dict(seen)
+            v.check(schema, self.tag_member.type, vseen)
+
+class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
+    def __init__(self, name, typ):
+        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
+    def check(self, schema, tag_type, seen):
+        QAPISchemaObjectTypeMember.check(self, schema, [], seen)
+        assert self.name in tag_type.values
+    # TODO try to get rid of .simple_union_type()
+    def simple_union_type(self):
+        if isinstance(self.type, QAPISchemaObjectType) and not self.type.info:
+            assert len(self.type.members) == 1
+            assert not self.type.variants
+            return self.type.members[0].type
+        return None
+
+class QAPISchemaAlternateType(QAPISchemaType):
+    def __init__(self, name, info, variants):
+        QAPISchemaType.__init__(self, name, info)
+        assert isinstance(variants, QAPISchemaObjectTypeVariants)
+        assert not variants.tag_name
+        self.variants = variants
+    def check(self, schema):
+        self.variants.check(schema, [], {})
+
+class QAPISchemaCommand(QAPISchemaEntity):
+    def __init__(self, name, info, arg_type, ret_type, gen, success_response):
+        QAPISchemaEntity.__init__(self, name, info)
+        assert not arg_type or isinstance(arg_type, str)
+        assert not ret_type or isinstance(ret_type, str)
+        self.arg_type_name = arg_type
+        self.arg_type = None
+        self.ret_type_name = ret_type
+        self.ret_type = None
+        self.gen = gen
+        self.success_response = success_response
+    def check(self, schema):
+        if self.arg_type_name:
+            self.arg_type = schema.lookup_type(self.arg_type_name)
+            assert isinstance(self.arg_type, QAPISchemaObjectType)
+            assert not self.arg_type.variants # not implemented
+        if self.ret_type_name:
+            self.ret_type = schema.lookup_type(self.ret_type_name)
+            assert isinstance(self.ret_type, QAPISchemaType)
+
+class QAPISchemaEvent(QAPISchemaEntity):
+    def __init__(self, name, info, arg_type):
+        QAPISchemaEntity.__init__(self, name, info)
+        assert not arg_type or isinstance(arg_type, str)
+        self.arg_type_name = arg_type
+        self.arg_type = None
+    def check(self, schema):
+        if self.arg_type_name:
+            self.arg_type = schema.lookup_type(self.arg_type_name)
+            assert isinstance(self.arg_type, QAPISchemaObjectType)
+            assert not self.arg_type.variants # not implemented
+
+class QAPISchema(object):
+    def __init__(self, fname):
+        try:
+            self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
+        except (QAPISchemaError, QAPIExprError), err:
+            print >>sys.stderr, err
+            exit(1)
+        self.entity_dict = {}
+        self._def_predefineds()
+        self._def_exprs()
+        self.check()
+
+    def get_exprs(self):
+        return [expr_elem['expr'] for expr_elem in self.exprs]
+
+    def _def_entity(self, ent):
+        assert ent.name not in self.entity_dict
+        self.entity_dict[ent.name] = ent
+
+    def lookup_entity(self, name, typ=None):
+        ent = self.entity_dict.get(name)
+        if typ and not isinstance(ent, typ):
+            return None
+        return ent
+
+    def lookup_type(self, name):
+        return self.lookup_entity(name, QAPISchemaType)
+
+    def _def_builtin_type(self, name):
+        self._def_entity(QAPISchemaBuiltinType(name))
+        if name != '**':
+            self._make_array_type(name) # TODO really needed?
+
+    def _def_predefineds(self):
+        for t in ['str', 'number', 'int', 'int8', 'int16', 'int32', 'int64',
+                  'uint8', 'uint16', 'uint32', 'uint64', 'size', 'bool', '**']:
+            self._def_builtin_type(t)
+
+    def _make_implicit_enum_type(self, name, values):
+        name = name + 'Kind'
+        self._def_entity(QAPISchemaEnumType(name, None, values))
+        return name
+
+    def _make_array_type(self, element_type):
+        name = element_type + 'List'
+        if not self.lookup_type(name):
+            self._def_entity(QAPISchemaArrayType(name, None, element_type))
+        return name
+
+    def _make_implicit_object_type(self, name, role, members):
+        if not members:
+            return None
+        name = ':obj-%s-%s' % (name, role)
+        if not self.lookup_entity(name, QAPISchemaObjectType):
+            self._def_entity(QAPISchemaObjectType(name, None, None,
+                                                  members, None))
+        return name
+
+    def _def_enum_type(self, expr, info):
+        name = expr['enum']
+        data = expr['data']
+        self._def_entity(QAPISchemaEnumType(name, info, data))
+        self._make_array_type(name) # TODO really needed?
+
+    def _make_member(self, name, typ):
+        optional = False
+        if name.startswith('*'):
+            name = name[1:]
+            optional = True
+        if isinstance(typ, list):
+            assert len(typ) == 1
+            typ = self._make_array_type(typ[0])
+        return QAPISchemaObjectTypeMember(name, typ, optional)
+
+    def _make_members(self, data):
+        return [self._make_member(key, value) 
+                for (key, value) in data.iteritems()]
+
+    def _def_struct_type(self, expr, info):
+        name = expr['struct']
+        base = expr.get('base')
+        data = expr['data']
+        self._def_entity(QAPISchemaObjectType(name, info, base,
+                                              self._make_members(data),
+                                              None))
+        self._make_array_type(name) # TODO really needed?
+
+    def _make_variant(self, case, typ):
+        return QAPISchemaObjectTypeVariant(case, typ)
+
+    def _make_simple_variant(self, case, typ):
+        if isinstance(typ, list):
+            assert len(typ) == 1
+            typ = self._make_array_type(typ[0])
+        typ = self._make_implicit_object_type(typ, 'wrapper',
+                                              [self._make_member('data', typ)])
+        return QAPISchemaObjectTypeVariant(case, typ)
+
+    def _make_tag_enum(self, type_name, variants):
+        return self._make_implicit_enum_type(type_name,
+                                             [v.name for v in variants])
+
+    def _def_union_type(self, expr, info):
+        name = expr['union']
+        data = expr['data']
+        base = expr.get('base')
+        tag_name = expr.get('discriminator')
+        tag_enum = None
+        if tag_name:
+            variants = [self._make_variant(key, value)
+                        for (key, value) in data.iteritems()]
+        else:
+            variants = [self._make_simple_variant(key, value)
+                        for (key, value) in data.iteritems()]
+            tag_enum = self._make_tag_enum(name, variants)
+        self._def_entity(QAPISchemaObjectType(name, info, base,
+                                    self._make_members(OrderedDict()),
+                                    QAPISchemaObjectTypeVariants(tag_name,
+                                                                 tag_enum,
+                                                                 variants)))
+        self._make_array_type(name) # TODO really needed?
+
+    def _def_alternate_type(self, expr, info):
+        name = expr['alternate']
+        data = expr['data']
+        variants = [self._make_variant(key, value)
+                    for (key, value) in data.iteritems()]
+        tag_enum = self._make_tag_enum(name, variants)
+        self._def_entity(QAPISchemaAlternateType(name, info,
+                                    QAPISchemaObjectTypeVariants(None,
+                                                                 tag_enum,
+                                                                 variants)))
+        self._make_array_type(name) # TODO really needed?
+
+    def _def_command(self, expr, info):
+        name = expr['command']
+        data = expr.get('data')
+        rets = expr.get('returns')
+        gen = expr.get('gen', True)
+        success_response = expr.get('success-response', True)
+        if isinstance(data, OrderedDict):
+            data = self._make_implicit_object_type(name, 'arg',
+                                                   self._make_members(data))
+        if isinstance(rets, list):
+            assert len(rets) == 1
+            rets = self._make_array_type(rets[0])
+        self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
+                                           success_response))
+
+    def _def_event(self, expr, info):
+        name = expr['event']
+        data = expr.get('data')
+        if isinstance(data, OrderedDict):
+            data = self._make_implicit_object_type(name, 'arg',
+                                                   self._make_members(data))
+        self._def_entity(QAPISchemaEvent(name, info, data))
+
+    def _def_exprs(self):
+        for expr_elem in self.exprs:
+            expr = expr_elem['expr']
+            info = expr_elem['info']
+            if 'enum' in expr:
+                self._def_enum_type(expr, info)
+            elif 'struct' in expr:
+                self._def_struct_type(expr, info)
+            elif 'union' in expr:
+                self._def_union_type(expr, info)
+            elif 'alternate' in expr:
+                self._def_alternate_type(expr, info)
+            elif 'command' in expr:
+                self._def_command(expr, info)
+            elif 'event' in expr:
+                self._def_event(expr, info)
+            else:
+                assert False
+
+    def check(self):
+        for ent in self.entity_dict.values():
+            ent.check(self)
 
 #
 # Code generation helpers
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 634ef2d..461c713 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -16,7 +16,7 @@ import os
 import sys
 
 try:
-    exprs = parse_schema(sys.argv[1])
+    exprs = QAPISchema(sys.argv[1]).get_exprs()
 except SystemExit:
     raise
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 03/32] qapi: QAPISchema code generation helper methods
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 01/32] qapi: Rename class QAPISchema to QAPISchemaParser Markus Armbruster
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 16:13   ` Daniel P. Berrange
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 04/32] qapi: New QAPISchemaVisitor Markus Armbruster
                   ` (29 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

New methods c_name(), c_type(), c_null(), json_type(),
alternate_qtype().

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 69 insertions(+), 7 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index c42bea1..cf1436d 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -760,15 +760,47 @@ class QAPISchemaEntity(object):
         assert isinstance(name, str)
         self.name = name
         self.info = info
+    def c_name(self):
+        return c_name(self.name)
     def check(self, schema):
         pass
 
 class QAPISchemaType(QAPISchemaEntity):
-    pass
+    def c_type(self, is_param=False):
+        return c_name(self.name) + pointer_suffix
+    def c_null(self):
+        return 'NULL'
+    def json_type(self):
+        pass
+    def alternate_qtype(self):
+        json2qtype = {
+            'string':  'QTYPE_QSTRING',
+            'number':  'QTYPE_QFLOAT',
+            'int':     'QTYPE_QINT',
+            'boolean': 'QTYPE_QBOOL',
+            'object':  'QTYPE_QDICT'
+        }
+        return json2qtype.get(self.json_type())
 
 class QAPISchemaBuiltinType(QAPISchemaType):
-    def __init__(self, name):
+    def __init__(self, name, json_type, c_type, c_null):
         QAPISchemaType.__init__(self, name, None)
+        assert not c_type or isinstance(c_type, str)
+        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
+                             'value')
+        self.json_type_name = json_type
+        self.c_type_name = c_type
+        self.c_null_val = c_null
+    def c_name(self):
+        return self.name
+    def c_type(self, is_param=False):
+        if is_param and self.name == 'str':
+            return 'const ' + self.c_type_name
+        return self.c_type_name
+    def c_null(self):
+        return self.c_null_val
+    def json_type(self):
+        return self.json_type_name
 
 class QAPISchemaEnumType(QAPISchemaType):
     def __init__(self, name, info, values):
@@ -778,6 +810,12 @@ class QAPISchemaEnumType(QAPISchemaType):
         self.values = values
     def check(self, schema):
         assert len(set(self.values)) == len(self.values)
+    def c_type(self, is_param=False):
+        return c_name(self.name)
+    def c_null(self):
+        return c_enum_const(self.name, (self.values + ['MAX'])[0])
+    def json_type(self):
+        return 'string'
 
 class QAPISchemaArrayType(QAPISchemaType):
     def __init__(self, name, info, element_type):
@@ -788,6 +826,8 @@ class QAPISchemaArrayType(QAPISchemaType):
     def check(self, schema):
         self.element_type = schema.lookup_type(self.element_type_name)
         assert self.element_type
+    def json_type(self):
+        return 'array'
 
 class QAPISchemaObjectType(QAPISchemaType):
     def __init__(self, name, info, base, local_members, variants):
@@ -823,6 +863,14 @@ class QAPISchemaObjectType(QAPISchemaType):
         if self.variants:
             self.variants.check(schema, members, seen)
         self.members = members
+    def c_name(self):
+        assert self.info
+        return QAPISchemaType.c_name(self)
+    def c_type(self, is_param=False):
+        assert self.info
+        return QAPISchemaType.c_type(self)
+    def json_type(self):
+        return 'object'
 
 class QAPISchemaObjectTypeMember(object):
     def __init__(self, name, typ, optional):
@@ -886,6 +934,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
         self.variants = variants
     def check(self, schema):
         self.variants.check(schema, [], {})
+    def json_type(self):
+        return 'value'
 
 class QAPISchemaCommand(QAPISchemaEntity):
     def __init__(self, name, info, arg_type, ret_type, gen, success_response):
@@ -947,15 +997,27 @@ class QAPISchema(object):
     def lookup_type(self, name):
         return self.lookup_entity(name, QAPISchemaType)
 
-    def _def_builtin_type(self, name):
-        self._def_entity(QAPISchemaBuiltinType(name))
+    def _def_builtin_type(self, name, json_type, c_type, c_null):
+        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type, c_null))
         if name != '**':
             self._make_array_type(name) # TODO really needed?
 
     def _def_predefineds(self):
-        for t in ['str', 'number', 'int', 'int8', 'int16', 'int32', 'int64',
-                  'uint8', 'uint16', 'uint32', 'uint64', 'size', 'bool', '**']:
-            self._def_builtin_type(t)
+        for t in [('str',    'string',  'char' + pointer_suffix, 'NULL'),
+                  ('number', 'number',  'double',   '0'),
+                  ('int',    'int',     'int64_t',  '0'),
+                  ('int8',   'int',     'int8_t',   '0'),
+                  ('int16',  'int',     'int16_t',  '0'),
+                  ('int32',  'int',     'int32_t',  '0'),
+                  ('int64',  'int',     'int64_t',  '0'),
+                  ('uint8',  'int',     'uint8_t',  '0'),
+                  ('uint16', 'int',     'uint16_t', '0'),
+                  ('uint32', 'int',     'uint32_t', '0'),
+                  ('uint64', 'int',     'uint64_t', '0'),
+                  ('size',   'int',     'uint64_t', '0'),
+                  ('bool',   'boolean', 'bool',     'false'),
+                  ('**',     'value',   None,       None)]:
+            self._def_builtin_type(*t)
 
     def _make_implicit_enum_type(self, name, values):
         name = name + 'Kind'
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 04/32] qapi: New QAPISchemaVisitor
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (2 preceding siblings ...)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 03/32] qapi: QAPISchema code generation helper methods Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 16:16   ` Daniel P. Berrange
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor Markus Armbruster
                   ` (28 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

The visitor will help keeping the code generation code simple and
reasonably separated from QAPISchema details.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index cf1436d..3e7c154 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -764,6 +764,29 @@ class QAPISchemaEntity(object):
         return c_name(self.name)
     def check(self, schema):
         pass
+    def visit(self, visitor):
+        pass
+
+class QAPISchemaVisitor(object):
+    def visit_begin(self, schema):
+        pass
+    def visit_end(self):
+        pass
+    def visit_builtin_type(self, name, info, json_type):
+        pass
+    def visit_enum_type(self, name, info, values):
+        pass
+    def visit_array_type(self, name, info, element_type):
+        pass
+    def visit_object_type(self, name, info, base, members, variants):
+        pass
+    def visit_alternate_type(self, name, info, variants):
+        pass
+    def visit_command(self, name, info, arg_type, ret_type,
+                      gen, success_response):
+        pass
+    def visit_event(self, name, info, arg_type):
+        pass
 
 class QAPISchemaType(QAPISchemaEntity):
     def c_type(self, is_param=False):
@@ -801,6 +824,8 @@ class QAPISchemaBuiltinType(QAPISchemaType):
         return self.c_null_val
     def json_type(self):
         return self.json_type_name
+    def visit(self, visitor):
+        visitor.visit_builtin_type(self.name, self.info, self.json_type())
 
 class QAPISchemaEnumType(QAPISchemaType):
     def __init__(self, name, info, values):
@@ -816,6 +841,8 @@ class QAPISchemaEnumType(QAPISchemaType):
         return c_enum_const(self.name, (self.values + ['MAX'])[0])
     def json_type(self):
         return 'string'
+    def visit(self, visitor):
+        visitor.visit_enum_type(self.name, self.info, self.values)
 
 class QAPISchemaArrayType(QAPISchemaType):
     def __init__(self, name, info, element_type):
@@ -828,6 +855,8 @@ class QAPISchemaArrayType(QAPISchemaType):
         assert self.element_type
     def json_type(self):
         return 'array'
+    def visit(self, visitor):
+        visitor.visit_array_type(self.name, self.info, self.element_type)
 
 class QAPISchemaObjectType(QAPISchemaType):
     def __init__(self, name, info, base, local_members, variants):
@@ -871,6 +900,9 @@ class QAPISchemaObjectType(QAPISchemaType):
         return QAPISchemaType.c_type(self)
     def json_type(self):
         return 'object'
+    def visit(self, visitor):
+        visitor.visit_object_type(self.name, self.info,
+                                  self.base, self.local_members, self.variants)
 
 class QAPISchemaObjectTypeMember(object):
     def __init__(self, name, typ, optional):
@@ -936,6 +968,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
         self.variants.check(schema, [], {})
     def json_type(self):
         return 'value'
+    def visit(self, visitor):
+        visitor.visit_alternate_type(self.name, self.info, self.variants)
 
 class QAPISchemaCommand(QAPISchemaEntity):
     def __init__(self, name, info, arg_type, ret_type, gen, success_response):
@@ -956,6 +990,10 @@ class QAPISchemaCommand(QAPISchemaEntity):
         if self.ret_type_name:
             self.ret_type = schema.lookup_type(self.ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
+    def visit(self, visitor):
+        visitor.visit_command(self.name, self.info,
+                              self.arg_type, self.ret_type,
+                              self.gen, self.success_response)
 
 class QAPISchemaEvent(QAPISchemaEntity):
     def __init__(self, name, info, arg_type):
@@ -968,6 +1006,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
             self.arg_type = schema.lookup_type(self.arg_type_name)
             assert isinstance(self.arg_type, QAPISchemaObjectType)
             assert not self.arg_type.variants # not implemented
+    def visit(self, visitor):
+        visitor.visit_event(self.name, self.info, self.arg_type)
 
 class QAPISchema(object):
     def __init__(self, fname):
@@ -1161,6 +1201,12 @@ class QAPISchema(object):
         for ent in self.entity_dict.values():
             ent.check(self)
 
+    def visit(self, visitor):
+        visitor.visit_begin(self)
+        for name in sorted(self.entity_dict.keys()):
+            self.entity_dict[name].visit(visitor)
+        visitor.visit_end()
+
 #
 # Code generation helpers
 #
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (3 preceding siblings ...)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 04/32] qapi: New QAPISchemaVisitor Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 16:18   ` Daniel P. Berrange
                     ` (2 more replies)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 06/32] qapi: Split up some typedefs to ease review Markus Armbruster
                   ` (27 subsequent siblings)
  32 siblings, 3 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

The old code prints the result of parsing (list of expression
dictionaries), and partial results of semantic analysis (list of enum
dictionaries, list of struct dictionaries).

The new code prints a trace of a schema visit, i.e. what the back-ends
are going to use.  Built-in and array types are omitted, because
they're boring.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 tests/qapi-schema/alternate-good.out            |  15 +-
 tests/qapi-schema/args-member-array.out         |  13 +-
 tests/qapi-schema/comments.out                  |   4 +-
 tests/qapi-schema/empty.out                     |   3 -
 tests/qapi-schema/enum-empty.out                |   4 +-
 tests/qapi-schema/event-case.out                |   4 +-
 tests/qapi-schema/flat-union-reverse-define.out |  21 ++-
 tests/qapi-schema/ident-with-escape.out         |   7 +-
 tests/qapi-schema/include-relpath.out           |   4 +-
 tests/qapi-schema/include-repetition.out        |   4 +-
 tests/qapi-schema/include-simple.out            |   4 +-
 tests/qapi-schema/indented-expr.out             |   7 +-
 tests/qapi-schema/qapi-schema-test.out          | 214 ++++++++++++++++++------
 tests/qapi-schema/returns-int.out               |   5 +-
 tests/qapi-schema/test-qapi.py                  |  38 ++++-
 tests/qapi-schema/type-bypass.out               |   7 +-
 16 files changed, 239 insertions(+), 115 deletions(-)

diff --git a/tests/qapi-schema/alternate-good.out b/tests/qapi-schema/alternate-good.out
index 99848ee..3d765ff 100644
--- a/tests/qapi-schema/alternate-good.out
+++ b/tests/qapi-schema/alternate-good.out
@@ -1,6 +1,9 @@
-[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]),
- OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
- OrderedDict([('alternate', 'Alt'), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])]
-[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
- {'enum_name': 'AltKind', 'enum_values': None}]
-[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])]
+alternate Alt
+    case value: int
+    case string: Enum
+    case struct: Data
+enum AltKind ['value', 'string', 'struct']
+object Data
+    member number: int optional=True
+    member name: str optional=True
+enum Enum ['hello', 'world']
diff --git a/tests/qapi-schema/args-member-array.out b/tests/qapi-schema/args-member-array.out
index c39fa25..b67384c 100644
--- a/tests/qapi-schema/args-member-array.out
+++ b/tests/qapi-schema/args-member-array.out
@@ -1,5 +1,8 @@
-[OrderedDict([('enum', 'abc'), ('data', ['a', 'b', 'c'])]),
- OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))]),
- OrderedDict([('command', 'okay'), ('data', OrderedDict([('member1', ['int']), ('member2', ['def'])]))])]
-[{'enum_name': 'abc', 'enum_values': ['a', 'b', 'c']}]
-[OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))])]
+object :obj-okay-arg
+    member member1: intList optional=False
+    member member2: defList optional=False
+enum abc ['a', 'b', 'c']
+object def
+    member array: abcList optional=False
+command okay :obj-okay-arg -> None
+   gen=True success_response=True
diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out
index 4ce3dcf..6161b90 100644
--- a/tests/qapi-schema/comments.out
+++ b/tests/qapi-schema/comments.out
@@ -1,3 +1 @@
-[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
-[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
-[]
+enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out
index b7f89a4..e69de29 100644
--- a/tests/qapi-schema/empty.out
+++ b/tests/qapi-schema/empty.out
@@ -1,3 +0,0 @@
-[]
-[]
-[]
diff --git a/tests/qapi-schema/enum-empty.out b/tests/qapi-schema/enum-empty.out
index 3b75c16..e09b00f 100644
--- a/tests/qapi-schema/enum-empty.out
+++ b/tests/qapi-schema/enum-empty.out
@@ -1,3 +1 @@
-[OrderedDict([('enum', 'MyEnum'), ('data', [])])]
-[{'enum_name': 'MyEnum', 'enum_values': []}]
-[]
+enum MyEnum []
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index 3764bc7..b5ae4c2 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1,3 +1 @@
-[OrderedDict([('event', 'oops')])]
-[]
-[]
+event oops None
diff --git a/tests/qapi-schema/flat-union-reverse-define.out b/tests/qapi-schema/flat-union-reverse-define.out
index 1ed7b8a..477fb31 100644
--- a/tests/qapi-schema/flat-union-reverse-define.out
+++ b/tests/qapi-schema/flat-union-reverse-define.out
@@ -1,9 +1,12 @@
-[OrderedDict([('union', 'TestUnion'), ('base', 'TestBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'TestTypeA'), ('value2', 'TestTypeB')]))]),
- OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
- OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
- OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
- OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
-[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
-[OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
- OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
- OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
+object TestBase
+    member enum1: TestEnum optional=False
+enum TestEnum ['value1', 'value2']
+object TestTypeA
+    member string: str optional=False
+object TestTypeB
+    member integer: int optional=False
+object TestUnion
+    base TestBase
+    tag enum1
+    case value1: TestTypeA
+    case value2: TestTypeB
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index 4028430..9577d1b 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -1,3 +1,4 @@
-[OrderedDict([('command', 'fooA'), ('data', OrderedDict([('bar1', 'str')]))])]
-[]
-[]
+object :obj-fooA-arg
+    member bar1: str optional=False
+command fooA :obj-fooA-arg -> None
+   gen=True success_response=True
diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out
index 4ce3dcf..6161b90 100644
--- a/tests/qapi-schema/include-relpath.out
+++ b/tests/qapi-schema/include-relpath.out
@@ -1,3 +1 @@
-[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
-[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
-[]
+enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out
index 4ce3dcf..6161b90 100644
--- a/tests/qapi-schema/include-repetition.out
+++ b/tests/qapi-schema/include-repetition.out
@@ -1,3 +1 @@
-[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
-[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
-[]
+enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out
index 4ce3dcf..6161b90 100644
--- a/tests/qapi-schema/include-simple.out
+++ b/tests/qapi-schema/include-simple.out
@@ -1,3 +1 @@
-[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
-[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
-[]
+enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index b5ce915..c5af55a 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,3 +1,4 @@
-[OrderedDict([('command', 'eins')]), OrderedDict([('command', 'zwei')])]
-[]
-[]
+command eins None -> None
+   gen=True success_response=True
+command zwei None -> None
+   gen=True success_response=True
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index b0b7187..b30bccc 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,55 +1,159 @@
-[OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]),
- OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
- OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
- OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]),
- OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]),
- OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]),
- OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
- OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('intb', 'int')]))]),
- OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefA'), ('value2', 'UserDefB'), ('value3', 'UserDefB')]))]),
- OrderedDict([('struct', 'UserDefUnionBase'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
- OrderedDict([('union', 'UserDefFlatUnion2'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefC'), ('value2', 'UserDefB'), ('value3', 'UserDefA')]))]),
- OrderedDict([('alternate', 'UserDefAlternate'), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]),
- OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
- OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str']), ('sizes', ['size'])]))]),
- OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]),
- OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]),
- OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]),
- OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]),
- OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
- OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]),
- OrderedDict([('event', 'EVENT_A')]),
- OrderedDict([('event', 'EVENT_B'), ('data', OrderedDict())]),
- OrderedDict([('event', 'EVENT_C'), ('data', OrderedDict([('*a', 'int'), ('*b', 'UserDefOne'), ('c', 'str')]))]),
- OrderedDict([('event', 'EVENT_D'), ('data', OrderedDict([('a', 'EventStructOne'), ('b', 'str'), ('*c', 'str'), ('*enum3', 'EnumOne')]))]),
- OrderedDict([('enum', '__org.qemu_x-Enum'), ('data', ['__org.qemu_x-value'])]),
- OrderedDict([('struct', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member1', '__org.qemu_x-Enum')]))]),
- OrderedDict([('struct', '__org.qemu_x-Struct'), ('base', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member2', 'str')]))]),
- OrderedDict([('union', '__org.qemu_x-Union1'), ('data', OrderedDict([('__org.qemu_x-branch', 'str')]))]),
- OrderedDict([('struct', '__org.qemu_x-Struct2'), ('data', OrderedDict([('array', ['__org.qemu_x-Union1'])]))]),
- OrderedDict([('union', '__org.qemu_x-Union2'), ('base', '__org.qemu_x-Base'), ('discriminator', '__org.qemu_x-member1'), ('data', OrderedDict([('__org.qemu_x-value', '__org.qemu_x-Struct2')]))]),
- OrderedDict([('alternate', '__org.qemu_x-Alt'), ('data', OrderedDict([('__org.qemu_x-branch', 'str'), ('b', '__org.qemu_x-Base')]))]),
- OrderedDict([('event', '__ORG.QEMU_X-EVENT'), ('data', '__org.qemu_x-Struct')]),
- OrderedDict([('command', '__org.qemu_x-command'), ('data', OrderedDict([('a', ['__org.qemu_x-Enum']), ('b', ['__org.qemu_x-Struct']), ('c', '__org.qemu_x-Union2'), ('d', '__org.qemu_x-Alt')])), ('returns', '__org.qemu_x-Union1')])]
-[{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']},
- {'enum_name': '__org.qemu_x-Enum', 'enum_values': ['__org.qemu_x-value']},
- {'enum_name': 'UserDefAlternateKind', 'enum_values': None},
- {'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None},
- {'enum_name': '__org.qemu_x-Union1Kind', 'enum_values': None},
- {'enum_name': '__org.qemu_x-AltKind', 'enum_values': None}]
-[OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
- OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
- OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]),
- OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]),
- OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]),
- OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
- OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('intb', 'int')]))]),
- OrderedDict([('struct', 'UserDefUnionBase'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
- OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
- OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
- OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]),
- OrderedDict([('struct', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member1', '__org.qemu_x-Enum')]))]),
- OrderedDict([('struct', '__org.qemu_x-Struct'), ('base', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member2', 'str')]))]),
- OrderedDict([('struct', '__org.qemu_x-Struct2'), ('data', OrderedDict([('array', ['__org.qemu_x-Union1'])]))])]
+object :obj-EVENT_C-arg
+    member a: int optional=True
+    member b: UserDefOne optional=True
+    member c: str optional=False
+object :obj-EVENT_D-arg
+    member a: EventStructOne optional=False
+    member b: str optional=False
+    member c: str optional=True
+    member enum3: EnumOne optional=True
+object :obj-__org.qemu_x-command-arg
+    member a: __org.qemu_x-EnumList optional=False
+    member b: __org.qemu_x-StructList optional=False
+    member c: __org.qemu_x-Union2 optional=False
+    member d: __org.qemu_x-Alt optional=False
+object :obj-boolList-wrapper
+    member data: boolList optional=False
+object :obj-int16List-wrapper
+    member data: int16List optional=False
+object :obj-int32List-wrapper
+    member data: int32List optional=False
+object :obj-int64List-wrapper
+    member data: int64List optional=False
+object :obj-int8List-wrapper
+    member data: int8List optional=False
+object :obj-intList-wrapper
+    member data: intList optional=False
+object :obj-numberList-wrapper
+    member data: numberList optional=False
+object :obj-sizeList-wrapper
+    member data: sizeList optional=False
+object :obj-str-wrapper
+    member data: str optional=False
+object :obj-strList-wrapper
+    member data: strList optional=False
+object :obj-uint16List-wrapper
+    member data: uint16List optional=False
+object :obj-uint32List-wrapper
+    member data: uint32List optional=False
+object :obj-uint64List-wrapper
+    member data: uint64List optional=False
+object :obj-uint8List-wrapper
+    member data: uint8List optional=False
+object :obj-user_def_cmd1-arg
+    member ud1a: UserDefOne optional=False
+object :obj-user_def_cmd2-arg
+    member ud1a: UserDefOne optional=False
+    member ud1b: UserDefOne optional=True
+object :obj-user_def_cmd3-arg
+    member a: int optional=False
+    member b: int optional=True
+event EVENT_A None
+event EVENT_B None
+event EVENT_C :obj-EVENT_C-arg
+event EVENT_D :obj-EVENT_D-arg
+enum EnumOne ['value1', 'value2', 'value3']
+object EventStructOne
+    member struct1: UserDefOne optional=False
+    member string: str optional=False
+    member enum2: EnumOne optional=True
+object NestedEnumsOne
+    member enum1: EnumOne optional=False
+    member enum2: EnumOne optional=True
+    member enum3: EnumOne optional=False
+    member enum4: EnumOne optional=True
+object UserDefA
+    member boolean: bool optional=False
+alternate UserDefAlternate
+    case uda: UserDefA
+    case s: str
+    case i: int
+enum UserDefAlternateKind ['uda', 's', 'i']
+object UserDefB
+    member intb: int optional=False
+object UserDefC
+    member string1: str optional=False
+    member string2: str optional=False
+object UserDefFlatUnion
+    base UserDefUnionBase
+    tag enum1
+    case value1: UserDefA
+    case value2: UserDefB
+    case value3: UserDefB
+object UserDefFlatUnion2
+    base UserDefUnionBase
+    tag enum1
+    case value1: UserDefC
+    case value2: UserDefB
+    case value3: UserDefA
+object UserDefNativeListUnion
+    case integer: :obj-intList-wrapper
+    case s8: :obj-int8List-wrapper
+    case s16: :obj-int16List-wrapper
+    case s32: :obj-int32List-wrapper
+    case s64: :obj-int64List-wrapper
+    case u8: :obj-uint8List-wrapper
+    case u16: :obj-uint16List-wrapper
+    case u32: :obj-uint32List-wrapper
+    case u64: :obj-uint64List-wrapper
+    case number: :obj-numberList-wrapper
+    case boolean: :obj-boolList-wrapper
+    case string: :obj-strList-wrapper
+    case sizes: :obj-sizeList-wrapper
+enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes']
+object UserDefOne
+    base UserDefZero
+    member string: str optional=False
+    member enum1: EnumOne optional=True
+object UserDefOptions
+    member i64: intList optional=True
+    member u64: uint64List optional=True
+    member u16: uint16List optional=True
+    member i64x: int optional=True
+    member u64x: uint64 optional=True
+object UserDefTwo
+    member string0: str optional=False
+    member dict1: UserDefTwoDict optional=False
+object UserDefTwoDict
+    member string1: str optional=False
+    member dict2: UserDefTwoDictDict optional=False
+    member dict3: UserDefTwoDictDict optional=True
+object UserDefTwoDictDict
+    member userdef: UserDefOne optional=False
+    member string: str optional=False
+object UserDefUnionBase
+    base UserDefZero
+    member string: str optional=False
+    member enum1: EnumOne optional=False
+object UserDefZero
+    member integer: int optional=False
+event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
+alternate __org.qemu_x-Alt
+    case __org.qemu_x-branch: str
+    case b: __org.qemu_x-Base
+enum __org.qemu_x-AltKind ['__org.qemu_x-branch', 'b']
+object __org.qemu_x-Base
+    member __org.qemu_x-member1: __org.qemu_x-Enum optional=False
+enum __org.qemu_x-Enum ['__org.qemu_x-value']
+object __org.qemu_x-Struct
+    base __org.qemu_x-Base
+    member __org.qemu_x-member2: str optional=False
+object __org.qemu_x-Struct2
+    member array: __org.qemu_x-Union1List optional=False
+object __org.qemu_x-Union1
+    case __org.qemu_x-branch: :obj-str-wrapper
+enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
+object __org.qemu_x-Union2
+    base __org.qemu_x-Base
+    tag __org.qemu_x-member1
+    case __org.qemu_x-value: __org.qemu_x-Struct2
+command __org.qemu_x-command :obj-__org.qemu_x-command-arg -> __org.qemu_x-Union1
+   gen=True success_response=True
+command user_def_cmd None -> None
+   gen=True success_response=True
+command user_def_cmd1 :obj-user_def_cmd1-arg -> None
+   gen=True success_response=True
+command user_def_cmd2 :obj-user_def_cmd2-arg -> UserDefTwo
+   gen=True success_response=True
+command user_def_cmd3 :obj-user_def_cmd3-arg -> int
+   gen=True success_response=True
diff --git a/tests/qapi-schema/returns-int.out b/tests/qapi-schema/returns-int.out
index 70b3ac5..1ac3e1e 100644
--- a/tests/qapi-schema/returns-int.out
+++ b/tests/qapi-schema/returns-int.out
@@ -1,3 +1,2 @@
-[OrderedDict([('command', 'guest-get-time'), ('returns', 'int')])]
-[]
-[]
+command guest-get-time None -> int
+   gen=True success_response=True
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 461c713..471f8e1 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -15,11 +15,35 @@ from pprint import pprint
 import os
 import sys
 
-try:
-    exprs = QAPISchema(sys.argv[1]).get_exprs()
-except SystemExit:
-    raise
+class QAPISchemaTestVisitor(QAPISchemaVisitor):
+    def visit_enum_type(self, name, info, values):
+        print 'enum %s %s' % (name, values)
+    def visit_object_type(self, name, info, base, members, variants):
+        print 'object %s' % name
+        if base:
+            print '    base %s' % base.name
+        for m in members:
+            print '    member %s: %s optional=%s' % \
+                (m.name, m.type.name, m.optional)
+        self._print_variants(variants)
+    def visit_alternate_type(self, name, info, variants):
+        print 'alternate %s' % name
+        self._print_variants(variants)
+    def visit_command(self, name, info, arg_type, ret_type,
+                      gen, success_response):
+        print 'command %s %s -> %s' % \
+            (name, arg_type and arg_type.name, ret_type and ret_type.name)
+        print '   gen=%s success_response=%s' % (gen, success_response)
+    def visit_event(self, name, info, arg_type):
+        print 'event %s %s' % (name, arg_type and arg_type.name)
 
-pprint(exprs)
-pprint(enum_types)
-pprint(struct_types)
+    @staticmethod
+    def _print_variants(variants):
+        if variants:
+            if variants.tag_name:
+                print '    tag %s' % variants.tag_name
+            for v in variants.variants:
+                print '    case %s: %s' % (v.name, v.type.name)
+
+schema = QAPISchema(sys.argv[1])
+schema.visit(QAPISchemaTestVisitor())
diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out
index eaf20f8..0070d4b 100644
--- a/tests/qapi-schema/type-bypass.out
+++ b/tests/qapi-schema/type-bypass.out
@@ -1,3 +1,4 @@
-[OrderedDict([('command', 'unsafe'), ('data', OrderedDict([('arg', '**')])), ('returns', '**'), ('gen', False)])]
-[]
-[]
+object :obj-unsafe-arg
+    member arg: ** optional=False
+command unsafe :obj-unsafe-arg -> **
+   gen=False success_response=True
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 06/32] qapi: Split up some typedefs to ease review
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (4 preceding siblings ...)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 16:19   ` Daniel P. Berrange
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 07/32] qapi: Generate comments to simplify splitting for review Markus Armbruster
                   ` (26 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

One of the next patches will among other things generate a separate
typedef for some struct types, i.e.

    typedef struct FOO FOO;

    struct FOO {
    ...
    };

instead of

    typedef struct FOO {
    ...
    } FOO;

To make the generated files easier to diff, anticipate the change.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-types.py | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index d162ca2..124a788 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -15,13 +15,15 @@ from qapi import *
 def generate_fwd_builtin(name):
     return mcgen('''
 
-typedef struct %(name)sList {
+typedef struct %(name)sList %(name)sList;
+
+struct %(name)sList {
     union {
         %(type)s value;
         uint64_t padding;
     };
     struct %(name)sList *next;
-} %(name)sList;
+};
 ''',
                  type=c_type(name),
                  name=name)
@@ -31,26 +33,30 @@ def generate_fwd_struct(name):
 
 typedef struct %(name)s %(name)s;
 
-typedef struct %(name)sList {
+typedef struct %(name)sList %(name)sList;
+
+struct %(name)sList {
     union {
         %(name)s *value;
         uint64_t padding;
     };
     struct %(name)sList *next;
-} %(name)sList;
+};
 ''',
                  name=c_name(name))
 
 def generate_fwd_enum_struct(name):
     return mcgen('''
 
-typedef struct %(name)sList {
+typedef struct %(name)sList %(name)sList;
+
+struct %(name)sList {
     union {
         %(name)s value;
         uint64_t padding;
     };
     struct %(name)sList *next;
-} %(name)sList;
+};
 ''',
                  name=c_name(name))
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 07/32] qapi: Generate comments to simplify splitting for review
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (5 preceding siblings ...)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 06/32] qapi: Split up some typedefs to ease review Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 16:21   ` Daniel P. Berrange
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 08/32] Revert "qapi: Generate comments to simplify splitting for review" Markus Armbruster
                   ` (25 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

The effect of the next few patches on generated files is hard to
review, because stuff gets generated in different order, which renders
diffs of the generated files useless.

To get reviewable diffs, we need to split the generated files into
suitable parts: put every top-level declaration in a file named like
the thing declared, so we can diff top-level declarations regardless
of their order in the generated files.

Since I don't feel like parsing C, simply generate a // comment
identifying the declaration right before each top-level declaration.
This lets us split with a simple shell loop:

    mkdir -p o
    for i in q*-{commands,marshal,event,types,visit}.[ch]
    do
        csplit -n 4 -s "$i" '/^\/\//' '{*}'
        for j in xx*
        do
            read h t <$j
            [ "$h" = "//" ] || t=""
            mv $j o/"$i-${j#xx}-${t/ /-}"
        done
    done

Splits each file F into F-NUMB-ID, where NUMB counts up from 0001, and
ID comes from the // comment.

To check the split's sane, we can run

    for i in q*-{commands,marshal,event,types,visit}.[ch]
    do cat o/$i-* | diff $i -
    done

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-commands.py |  7 +++++++
 scripts/qapi-event.py    | 12 ++++++++++++
 scripts/qapi-types.py    | 18 ++++++++++++++++++
 scripts/qapi-visit.py    | 12 ++++++++++++
 scripts/qapi.py          |  5 +++++
 5 files changed, 54 insertions(+)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 12bdc4c..c3afb64 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -24,6 +24,7 @@ def generate_command_decl(name, args, ret_type):
             arglist += "bool has_%s, " % c_name(argname)
         arglist += "%s %s, " % (argtype, c_name(argname))
     return mcgen('''
+// decl qmp_%(name)s
 %(ret_type)s qmp_%(name)s(%(args)sError **errp);
 ''',
                  ret_type=c_type(ret_type), name=c_name(name),
@@ -160,6 +161,7 @@ def gen_marshal_output(name, ret_type):
         return ""
 
     ret = mcgen('''
+// def qmp_marshal_output_%(c_name)s
 static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *local_err = NULL;
@@ -198,10 +200,12 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
     hdr = gen_marshal_input_decl(name, middle_mode)
 
     ret = mcgen('''
+// def qmp_marshal_input_%(name)s
 %(header)s
 {
     Error *local_err = NULL;
 ''',
+                name=name,
                 header=hdr)
 
     if ret_type:
@@ -252,6 +256,7 @@ qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
                      opts=options)
     pop_indent()
     ret = mcgen('''
+// def qmp_init_marshal
 static void qmp_init_marshal(void)
 {
 ''')
@@ -259,6 +264,7 @@ static void qmp_init_marshal(void)
     ret += mcgen('''
 }
 
+// decl qapi_init
 qapi_init(qmp_init_marshal);
 ''')
     return ret
@@ -347,6 +353,7 @@ for cmd in commands:
         fdef.write(ret)
 
     if middle_mode:
+        fdecl.write('// decl gen_marshal_input_%s\n' % cmd['command'])
         fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], middle_mode))
 
     ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index aec2d32..e511437 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -9,6 +9,7 @@
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
+import re;
 from ordereddict import OrderedDict
 from qapi import *
 
@@ -33,22 +34,30 @@ def _generate_event_api_name(event_name, params):
 # Following are the core functions that generate C APIs to emit event.
 
 def generate_event_declaration(api_name):
+    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
+    name = match.group(1)
     return mcgen('''
 
+// decl %(name)s
 %(api_name)s;
 ''',
+                 name=name,
                  api_name = api_name)
 
 def generate_event_implement(api_name, event_name, params):
+    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
+    name = match.group(1)
     # step 1: declare any variables
     ret = mcgen("""
 
+// def %(name)s
 %(api_name)s
 {
     QDict *qmp;
     Error *local_err = NULL;
     QMPEventFuncEmit emit;
 """,
+                name=name,
                 api_name = api_name)
 
     if params:
@@ -162,11 +171,13 @@ def generate_event_implement(api_name, event_name, params):
 def generate_event_enum_decl(event_enum_name, event_enum_values):
     lookup_decl = mcgen('''
 
+// decl %(event_enum_name)s_lookup
 extern const char *%(event_enum_name)s_lookup[];
 ''',
                         event_enum_name = event_enum_name)
 
     enum_decl = mcgen('''
+// typedef %(event_enum_name)s
 typedef enum %(event_enum_name)s {
 ''',
                       event_enum_name = event_enum_name)
@@ -194,6 +205,7 @@ typedef enum %(event_enum_name)s {
 def generate_event_enum_lookup(event_enum_name, event_enum_strings):
     ret = mcgen('''
 
+// def %(event_enum_name)s_lookup
 const char *%(event_enum_name)s_lookup[] = {
 ''',
                 event_enum_name = event_enum_name)
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 124a788..7fabcaa 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -15,8 +15,10 @@ from qapi import *
 def generate_fwd_builtin(name):
     return mcgen('''
 
+// typedef %(name)sList
 typedef struct %(name)sList %(name)sList;
 
+// struct %(name)sList
 struct %(name)sList {
     union {
         %(type)s value;
@@ -31,10 +33,13 @@ struct %(name)sList {
 def generate_fwd_struct(name):
     return mcgen('''
 
+// typedef %(name)s
 typedef struct %(name)s %(name)s;
 
+// typedef %(name)sList
 typedef struct %(name)sList %(name)sList;
 
+// struct %(name)sList
 struct %(name)sList {
     union {
         %(name)s *value;
@@ -48,8 +53,10 @@ struct %(name)sList {
 def generate_fwd_enum_struct(name):
     return mcgen('''
 
+// typedef %(name)sList
 typedef struct %(name)sList %(name)sList;
 
+// struct %(name)sList
 struct %(name)sList {
     union {
         %(name)s value;
@@ -84,6 +91,7 @@ def generate_struct(expr):
 
     ret = mcgen('''
 
+// struct %(name)s
 struct %(name)s {
 ''',
           name=c_name(structname))
@@ -110,6 +118,7 @@ struct %(name)s {
 def generate_enum_lookup(name, values):
     ret = mcgen('''
 
+// def %(name)s_lookup
 const char *const %(name)s_lookup[] = {
 ''',
                 name=c_name(name))
@@ -132,12 +141,14 @@ def generate_enum(name, values):
     name = c_name(name)
     lookup_decl = mcgen('''
 
+// decl %(name)s_lookup
 extern const char *const %(name)s_lookup[];
 ''',
                 name=name)
 
     enum_decl = mcgen('''
 
+// typedef %(name)s
 typedef enum %(name)s {
 ''',
                 name=name)
@@ -169,6 +180,7 @@ def generate_alternate_qtypes(expr):
 
     ret = mcgen('''
 
+// def %(name)s_qtypes
 const int %(name)s_qtypes[QTYPE_MAX] = {
 ''',
                 name=c_name(name))
@@ -205,6 +217,7 @@ def generate_union(expr, meta):
 
     ret = mcgen('''
 
+// struct %(name)s
 struct %(name)s {
 ''',
                 name=name)
@@ -252,6 +265,8 @@ struct %(name)s {
 ''')
     if meta == 'alternate':
         ret += mcgen('''
+
+// decl %(name)s_qtypes
 extern const int %(name)s_qtypes[];
 ''',
             name=name)
@@ -261,6 +276,8 @@ extern const int %(name)s_qtypes[];
 
 def generate_type_cleanup_decl(name):
     ret = mcgen('''
+
+// decl qapi_free_%(name)s
 void qapi_free_%(name)s(%(c_type)s obj);
 ''',
                 c_type=c_type(name), name=c_name(name))
@@ -269,6 +286,7 @@ void qapi_free_%(name)s(%(c_type)s obj);
 def generate_type_cleanup(name):
     ret = mcgen('''
 
+// def qapi_free_%(name)s
 void qapi_free_%(name)s(%(c_type)s obj)
 {
     QapiDeallocVisitor *md;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index c493964..f0fef55 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -28,12 +28,14 @@ def generate_visit_implicit_struct(type):
         # Need a forward declaration
         ret += mcgen('''
 
+// def visit_type_%(c_type)s_fields
 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
 ''',
                      c_type=type_name(type))
 
     ret += mcgen('''
 
+// def visit_type_implicit_%(c_type)s
 static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -59,6 +61,7 @@ def generate_visit_struct_fields(name, members, base = None):
 
     ret += mcgen('''
 
+// def visit_type_%(name)s_fields
 static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -146,6 +149,7 @@ def generate_visit_struct(expr):
 
     ret += mcgen('''
 
+// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
 ''',
@@ -161,6 +165,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
 def generate_visit_list(name):
     return mcgen('''
 
+// def visit_type_%(name)sList
 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -190,6 +195,7 @@ out:
 def generate_visit_enum(name):
     return mcgen('''
 
+// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
 {
     visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
@@ -200,6 +206,7 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
 def generate_visit_alternate(name, members):
     ret = mcgen('''
 
+// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -283,6 +290,7 @@ def generate_visit_union(expr):
 
     ret += mcgen('''
 
+// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -365,11 +373,13 @@ def generate_declaration(name, builtin_type=False):
         name = c_name(name)
         ret += mcgen('''
 
+// decl visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
 ''',
                      name=name)
 
     ret += mcgen('''
+// decl visit_type_%(name)sList
 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
 ''',
                  name=name)
@@ -378,6 +388,7 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E
 
 def generate_enum_declaration(name):
     ret = mcgen('''
+// decl visit_type_%(name)sList
 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
 ''',
                 name=c_name(name))
@@ -387,6 +398,7 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E
 def generate_decl_enum(name):
     return mcgen('''
 
+// decl visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
 ''',
                  name=c_name(name))
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 3e7c154..2b639a5 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1410,6 +1410,7 @@ def guardname(filename):
 def guardstart(name):
     return mcgen('''
 
+// guardstart %(name)s
 #ifndef %(name)s
 #define %(name)s
 
@@ -1419,6 +1420,7 @@ def guardstart(name):
 def guardend(name):
     return mcgen('''
 
+// guardend %(name)s
 #endif /* %(name)s */
 
 ''',
@@ -1503,12 +1505,14 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
     fdecl = maybe_open(do_h, h_file, 'w')
 
     fdef.write(mcgen('''
+// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 ''',
                      comment = c_comment))
 
     fdecl.write(mcgen('''
+// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 #ifndef %(guard)s
@@ -1521,6 +1525,7 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
 
 def close_output(fdef, fdecl):
     fdecl.write('''
+// epilogue
 #endif
 ''')
     fdecl.close()
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 08/32] Revert "qapi: Generate comments to simplify splitting for review"
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (6 preceding siblings ...)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 07/32] qapi: Generate comments to simplify splitting for review Markus Armbruster
@ 2015-09-03 14:29 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 09/32] Revert "qapi: Split up some typedefs to ease review" Markus Armbruster
                   ` (24 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

This reverts commit 22ff4d4ac6cd553f328e400de6913dc879581c4c.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-commands.py |  7 -------
 scripts/qapi-event.py    | 12 ------------
 scripts/qapi-types.py    | 18 ------------------
 scripts/qapi-visit.py    | 12 ------------
 scripts/qapi.py          |  5 -----
 5 files changed, 54 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index c3afb64..12bdc4c 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -24,7 +24,6 @@ def generate_command_decl(name, args, ret_type):
             arglist += "bool has_%s, " % c_name(argname)
         arglist += "%s %s, " % (argtype, c_name(argname))
     return mcgen('''
-// decl qmp_%(name)s
 %(ret_type)s qmp_%(name)s(%(args)sError **errp);
 ''',
                  ret_type=c_type(ret_type), name=c_name(name),
@@ -161,7 +160,6 @@ def gen_marshal_output(name, ret_type):
         return ""
 
     ret = mcgen('''
-// def qmp_marshal_output_%(c_name)s
 static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *local_err = NULL;
@@ -200,12 +198,10 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
     hdr = gen_marshal_input_decl(name, middle_mode)
 
     ret = mcgen('''
-// def qmp_marshal_input_%(name)s
 %(header)s
 {
     Error *local_err = NULL;
 ''',
-                name=name,
                 header=hdr)
 
     if ret_type:
@@ -256,7 +252,6 @@ qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
                      opts=options)
     pop_indent()
     ret = mcgen('''
-// def qmp_init_marshal
 static void qmp_init_marshal(void)
 {
 ''')
@@ -264,7 +259,6 @@ static void qmp_init_marshal(void)
     ret += mcgen('''
 }
 
-// decl qapi_init
 qapi_init(qmp_init_marshal);
 ''')
     return ret
@@ -353,7 +347,6 @@ for cmd in commands:
         fdef.write(ret)
 
     if middle_mode:
-        fdecl.write('// decl gen_marshal_input_%s\n' % cmd['command'])
         fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], middle_mode))
 
     ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index e511437..aec2d32 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -9,7 +9,6 @@
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
-import re;
 from ordereddict import OrderedDict
 from qapi import *
 
@@ -34,30 +33,22 @@ def _generate_event_api_name(event_name, params):
 # Following are the core functions that generate C APIs to emit event.
 
 def generate_event_declaration(api_name):
-    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
-    name = match.group(1)
     return mcgen('''
 
-// decl %(name)s
 %(api_name)s;
 ''',
-                 name=name,
                  api_name = api_name)
 
 def generate_event_implement(api_name, event_name, params):
-    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
-    name = match.group(1)
     # step 1: declare any variables
     ret = mcgen("""
 
-// def %(name)s
 %(api_name)s
 {
     QDict *qmp;
     Error *local_err = NULL;
     QMPEventFuncEmit emit;
 """,
-                name=name,
                 api_name = api_name)
 
     if params:
@@ -171,13 +162,11 @@ def generate_event_implement(api_name, event_name, params):
 def generate_event_enum_decl(event_enum_name, event_enum_values):
     lookup_decl = mcgen('''
 
-// decl %(event_enum_name)s_lookup
 extern const char *%(event_enum_name)s_lookup[];
 ''',
                         event_enum_name = event_enum_name)
 
     enum_decl = mcgen('''
-// typedef %(event_enum_name)s
 typedef enum %(event_enum_name)s {
 ''',
                       event_enum_name = event_enum_name)
@@ -205,7 +194,6 @@ typedef enum %(event_enum_name)s {
 def generate_event_enum_lookup(event_enum_name, event_enum_strings):
     ret = mcgen('''
 
-// def %(event_enum_name)s_lookup
 const char *%(event_enum_name)s_lookup[] = {
 ''',
                 event_enum_name = event_enum_name)
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 7fabcaa..124a788 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -15,10 +15,8 @@ from qapi import *
 def generate_fwd_builtin(name):
     return mcgen('''
 
-// typedef %(name)sList
 typedef struct %(name)sList %(name)sList;
 
-// struct %(name)sList
 struct %(name)sList {
     union {
         %(type)s value;
@@ -33,13 +31,10 @@ struct %(name)sList {
 def generate_fwd_struct(name):
     return mcgen('''
 
-// typedef %(name)s
 typedef struct %(name)s %(name)s;
 
-// typedef %(name)sList
 typedef struct %(name)sList %(name)sList;
 
-// struct %(name)sList
 struct %(name)sList {
     union {
         %(name)s *value;
@@ -53,10 +48,8 @@ struct %(name)sList {
 def generate_fwd_enum_struct(name):
     return mcgen('''
 
-// typedef %(name)sList
 typedef struct %(name)sList %(name)sList;
 
-// struct %(name)sList
 struct %(name)sList {
     union {
         %(name)s value;
@@ -91,7 +84,6 @@ def generate_struct(expr):
 
     ret = mcgen('''
 
-// struct %(name)s
 struct %(name)s {
 ''',
           name=c_name(structname))
@@ -118,7 +110,6 @@ struct %(name)s {
 def generate_enum_lookup(name, values):
     ret = mcgen('''
 
-// def %(name)s_lookup
 const char *const %(name)s_lookup[] = {
 ''',
                 name=c_name(name))
@@ -141,14 +132,12 @@ def generate_enum(name, values):
     name = c_name(name)
     lookup_decl = mcgen('''
 
-// decl %(name)s_lookup
 extern const char *const %(name)s_lookup[];
 ''',
                 name=name)
 
     enum_decl = mcgen('''
 
-// typedef %(name)s
 typedef enum %(name)s {
 ''',
                 name=name)
@@ -180,7 +169,6 @@ def generate_alternate_qtypes(expr):
 
     ret = mcgen('''
 
-// def %(name)s_qtypes
 const int %(name)s_qtypes[QTYPE_MAX] = {
 ''',
                 name=c_name(name))
@@ -217,7 +205,6 @@ def generate_union(expr, meta):
 
     ret = mcgen('''
 
-// struct %(name)s
 struct %(name)s {
 ''',
                 name=name)
@@ -265,8 +252,6 @@ struct %(name)s {
 ''')
     if meta == 'alternate':
         ret += mcgen('''
-
-// decl %(name)s_qtypes
 extern const int %(name)s_qtypes[];
 ''',
             name=name)
@@ -276,8 +261,6 @@ extern const int %(name)s_qtypes[];
 
 def generate_type_cleanup_decl(name):
     ret = mcgen('''
-
-// decl qapi_free_%(name)s
 void qapi_free_%(name)s(%(c_type)s obj);
 ''',
                 c_type=c_type(name), name=c_name(name))
@@ -286,7 +269,6 @@ void qapi_free_%(name)s(%(c_type)s obj);
 def generate_type_cleanup(name):
     ret = mcgen('''
 
-// def qapi_free_%(name)s
 void qapi_free_%(name)s(%(c_type)s obj)
 {
     QapiDeallocVisitor *md;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index f0fef55..c493964 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -28,14 +28,12 @@ def generate_visit_implicit_struct(type):
         # Need a forward declaration
         ret += mcgen('''
 
-// def visit_type_%(c_type)s_fields
 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
 ''',
                      c_type=type_name(type))
 
     ret += mcgen('''
 
-// def visit_type_implicit_%(c_type)s
 static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -61,7 +59,6 @@ def generate_visit_struct_fields(name, members, base = None):
 
     ret += mcgen('''
 
-// def visit_type_%(name)s_fields
 static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -149,7 +146,6 @@ def generate_visit_struct(expr):
 
     ret += mcgen('''
 
-// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
 ''',
@@ -165,7 +161,6 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
 def generate_visit_list(name):
     return mcgen('''
 
-// def visit_type_%(name)sList
 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -195,7 +190,6 @@ out:
 def generate_visit_enum(name):
     return mcgen('''
 
-// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
 {
     visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
@@ -206,7 +200,6 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
 def generate_visit_alternate(name, members):
     ret = mcgen('''
 
-// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -290,7 +283,6 @@ def generate_visit_union(expr):
 
     ret += mcgen('''
 
-// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -373,13 +365,11 @@ def generate_declaration(name, builtin_type=False):
         name = c_name(name)
         ret += mcgen('''
 
-// decl visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
 ''',
                      name=name)
 
     ret += mcgen('''
-// decl visit_type_%(name)sList
 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
 ''',
                  name=name)
@@ -388,7 +378,6 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E
 
 def generate_enum_declaration(name):
     ret = mcgen('''
-// decl visit_type_%(name)sList
 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
 ''',
                 name=c_name(name))
@@ -398,7 +387,6 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E
 def generate_decl_enum(name):
     return mcgen('''
 
-// decl visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
 ''',
                  name=c_name(name))
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 2b639a5..3e7c154 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1410,7 +1410,6 @@ def guardname(filename):
 def guardstart(name):
     return mcgen('''
 
-// guardstart %(name)s
 #ifndef %(name)s
 #define %(name)s
 
@@ -1420,7 +1419,6 @@ def guardstart(name):
 def guardend(name):
     return mcgen('''
 
-// guardend %(name)s
 #endif /* %(name)s */
 
 ''',
@@ -1505,14 +1503,12 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
     fdecl = maybe_open(do_h, h_file, 'w')
 
     fdef.write(mcgen('''
-// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 ''',
                      comment = c_comment))
 
     fdecl.write(mcgen('''
-// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 #ifndef %(guard)s
@@ -1525,7 +1521,6 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
 
 def close_output(fdef, fdecl):
     fdecl.write('''
-// epilogue
 #endif
 ''')
     fdecl.close()
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 09/32] Revert "qapi: Split up some typedefs to ease review"
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (7 preceding siblings ...)
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 08/32] Revert "qapi: Generate comments to simplify splitting for review" Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 10/32] qapi-types: Convert to QAPISchemaVisitor, fixing flat unions Markus Armbruster
                   ` (23 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

This reverts commit 6542034f356ebc6f2818c1c6b8c9a143f817bb2a.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-types.py | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 124a788..d162ca2 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -15,15 +15,13 @@ from qapi import *
 def generate_fwd_builtin(name):
     return mcgen('''
 
-typedef struct %(name)sList %(name)sList;
-
-struct %(name)sList {
+typedef struct %(name)sList {
     union {
         %(type)s value;
         uint64_t padding;
     };
     struct %(name)sList *next;
-};
+} %(name)sList;
 ''',
                  type=c_type(name),
                  name=name)
@@ -33,30 +31,26 @@ def generate_fwd_struct(name):
 
 typedef struct %(name)s %(name)s;
 
-typedef struct %(name)sList %(name)sList;
-
-struct %(name)sList {
+typedef struct %(name)sList {
     union {
         %(name)s *value;
         uint64_t padding;
     };
     struct %(name)sList *next;
-};
+} %(name)sList;
 ''',
                  name=c_name(name))
 
 def generate_fwd_enum_struct(name):
     return mcgen('''
 
-typedef struct %(name)sList %(name)sList;
-
-struct %(name)sList {
+typedef struct %(name)sList {
     union {
         %(name)s value;
         uint64_t padding;
     };
     struct %(name)sList *next;
-};
+} %(name)sList;
 ''',
                  name=c_name(name))
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 10/32] qapi-types: Convert to QAPISchemaVisitor, fixing flat unions
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (8 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 09/32] Revert "qapi: Split up some typedefs to ease review" Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-04  3:11   ` Eric Blake
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 11/32] qapi-visit: Convert to QAPISchemaVisitor, fixing bugs Markus Armbruster
                   ` (22 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Fixes flat unions to get the base's base members.  Test case is from
commit 2fc0043, in qapi-schema-test.json:

    { 'union': 'UserDefFlatUnion',
      'base': 'UserDefUnionBase',
      'discriminator': 'enum1',
      'data': { 'value1' : 'UserDefA',
                'value2' : 'UserDefB',
                'value3' : 'UserDefB' } }

    { 'struct': 'UserDefUnionBase',
      'base': 'UserDefZero',
      'data': { 'string': 'str', 'enum1': 'EnumOne' } }

    { 'struct': 'UserDefZero',
      'data': { 'integer': 'int' } }

Patch's effect on UserDefFlatUnion:

     struct UserDefFlatUnion {
         /* Members inherited from UserDefUnionBase: */
    +    int64_t integer;
         char *string;
         EnumOne enum1;
         /* Own members: */
         union { /* union tag is @enum1 */
             void *data;
             UserDefA *value1;
             UserDefB *value2;
             UserDefB *value3;
         };
     };

Flat union visitors remain broken.  They'll be fixed next.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt                  |  51 +++---
 scripts/qapi-types.py                   | 273 ++++++++++++++------------------
 tests/qapi-schema/qapi-schema-test.json |   4 +-
 3 files changed, 144 insertions(+), 184 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index ff16df2..710defc 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -537,22 +537,6 @@ Example:
     $ cat qapi-generated/example-qapi-types.c
 [Uninteresting stuff omitted...]
 
-    void qapi_free_UserDefOneList(UserDefOneList *obj)
-    {
-        QapiDeallocVisitor *md;
-        Visitor *v;
-
-        if (!obj) {
-            return;
-        }
-
-        md = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(md);
-        visit_type_UserDefOneList(v, &obj, NULL, NULL);
-        qapi_dealloc_visitor_cleanup(md);
-    }
-
-
     void qapi_free_UserDefOne(UserDefOne *obj)
     {
         QapiDeallocVisitor *md;
@@ -567,6 +551,21 @@ Example:
         visit_type_UserDefOne(v, &obj, NULL, NULL);
         qapi_dealloc_visitor_cleanup(md);
     }
+
+    void qapi_free_UserDefOneList(UserDefOneList *obj)
+    {
+        QapiDeallocVisitor *md;
+        Visitor *v;
+
+        if (!obj) {
+            return;
+        }
+
+        md = qapi_dealloc_visitor_new();
+        v = qapi_dealloc_get_visitor(md);
+        visit_type_UserDefOneList(v, &obj, NULL, NULL);
+        qapi_dealloc_visitor_cleanup(md);
+    }
     $ cat qapi-generated/example-qapi-types.h
 [Uninteresting stuff omitted...]
 
@@ -577,24 +576,24 @@ Example:
 
     typedef struct UserDefOne UserDefOne;
 
-    typedef struct UserDefOneList {
+    typedef struct UserDefOneList UserDefOneList;
+
+    struct UserDefOne {
+        int64_t integer;
+        char *string;
+    };
+
+    void qapi_free_UserDefOne(UserDefOne *obj);
+
+    struct UserDefOneList {
         union {
             UserDefOne *value;
             uint64_t padding;
         };
         struct UserDefOneList *next;
-    } UserDefOneList;
-
-
-[Functions on built-in types omitted...]
-
-    struct UserDefOne {
-        int64_t integer;
-        char *string;
     };
 
     void qapi_free_UserDefOneList(UserDefOneList *obj);
-    void qapi_free_UserDefOne(UserDefOne *obj);
 
     #endif
 
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index d162ca2..85dbd74 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -2,88 +2,67 @@
 # QAPI types generator
 #
 # Copyright IBM, Corp. 2011
+# Copyright (c) 2013-2015 Red Hat Inc.
 #
 # Authors:
 #  Anthony Liguori <aliguori@us.ibm.com>
+#  Markus Armbruster <armbru@redhat.com>
 #
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
-from ordereddict import OrderedDict
 from qapi import *
 
-def generate_fwd_builtin(name):
-    return mcgen('''
-
-typedef struct %(name)sList {
-    union {
-        %(type)s value;
-        uint64_t padding;
-    };
-    struct %(name)sList *next;
-} %(name)sList;
-''',
-                 type=c_type(name),
-                 name=name)
-
-def generate_fwd_struct(name):
+def gen_fwd_object_or_array(name):
     return mcgen('''
 
 typedef struct %(name)s %(name)s;
-
-typedef struct %(name)sList {
-    union {
-        %(name)s *value;
-        uint64_t padding;
-    };
-    struct %(name)sList *next;
-} %(name)sList;
 ''',
                  name=c_name(name))
 
-def generate_fwd_enum_struct(name):
+def gen_array(name, element_type):
     return mcgen('''
 
-typedef struct %(name)sList {
+struct %(name)s {
     union {
-        %(name)s value;
+        %(c_type)s value;
         uint64_t padding;
     };
-    struct %(name)sList *next;
-} %(name)sList;
+    struct %(name)s *next;
+};
 ''',
-                 name=c_name(name))
+                 name=c_name(name), c_type=element_type.c_type())
 
-def generate_struct_fields(members):
+def gen_struct_field(name, typ, optional):
     ret = ''
 
-    for argname, argentry, optional in parse_args(members):
-        if optional:
-            ret += mcgen('''
+    if optional:
+        ret += mcgen('''
     bool has_%(c_name)s;
 ''',
-                         c_name=c_name(argname))
-        ret += mcgen('''
+                     c_name=c_name(name))
+    ret += mcgen('''
     %(c_type)s %(c_name)s;
 ''',
-                     c_type=c_type(argentry), c_name=c_name(argname))
-
+                 c_type=typ.c_type(), c_name=c_name(name))
     return ret
 
-def generate_struct(expr):
+def generate_struct_fields(members):
+    ret = ''
 
-    structname = expr.get('struct', "")
-    members = expr['data']
-    base = expr.get('base')
+    for memb in members:
+        ret += gen_struct_field(memb.name, memb.type, memb.optional)
+    return ret
 
+def gen_struct(name, base, members):
     ret = mcgen('''
 
 struct %(name)s {
 ''',
-          name=c_name(structname))
+                name=c_name(name))
 
     if base:
-        ret += generate_struct_fields({'base': base})
+        ret += gen_struct_field('base', base, False)
 
     ret += generate_struct_fields(members)
 
@@ -156,46 +135,38 @@ typedef enum %(name)s {
 
     return enum_decl + lookup_decl
 
-def generate_alternate_qtypes(expr):
+def gen_alternate_qtypes_decl(name):
+    return mcgen('''
 
-    name = expr['alternate']
-    members = expr['data']
+extern const int %(c_name)s_qtypes[];
+''',
+                 c_name=c_name(name))
 
+def gen_alternate_qtypes(name, variants):
     ret = mcgen('''
 
 const int %(name)s_qtypes[QTYPE_MAX] = {
 ''',
                 name=c_name(name))
 
-    for key in members:
-        qtype = find_alternate_member_qtype(members[key])
-        assert qtype, "Invalid alternate member"
+    for var in variants.variants:
+        qtype = var.type.alternate_qtype()
+        assert qtype
 
         ret += mcgen('''
     [%(qtype)s] = %(enum_const)s,
 ''',
                      qtype = qtype,
-                     enum_const = c_enum_const(name + 'Kind', key))
+                     enum_const=c_enum_const(variants.tag_member.type.name,
+                                             var.name))
 
     ret += mcgen('''
 };
 ''')
     return ret
 
-
-def generate_union(expr, meta):
-
-    name = c_name(expr[meta])
-    typeinfo = expr['data']
-
-    base = expr.get('base')
-    discriminator = expr.get('discriminator')
-
-    enum_define = discriminator_find_enum_define(expr)
-    if enum_define:
-        discriminator_type_name = enum_define['enum_name']
-    else:
-        discriminator_type_name = '%sKind' % (name)
+def gen_union(name, base, variants):
+    name = c_name(name)
 
     ret = mcgen('''
 
@@ -206,18 +177,16 @@ struct %(name)s {
         ret += mcgen('''
     /* Members inherited from %(c_name)s: */
 ''',
-                     c_name=c_name(base))
-        base_fields = find_struct(base)['data']
-        ret += generate_struct_fields(base_fields)
+                     c_name=c_name(base.name))
+        ret += generate_struct_fields(base.members)
         ret += mcgen('''
     /* Own members: */
 ''')
     else:
-        assert not discriminator
         ret += mcgen('''
     %(discriminator_type_name)s kind;
 ''',
-                     discriminator_type_name=c_name(discriminator_type_name))
+                     discriminator_type_name=c_name(variants.tag_member.type.name))
 
     # FIXME: What purpose does data serve, besides preventing a union that
     # has a branch named 'data'? We use it in qapi-visit.py to decide
@@ -231,39 +200,36 @@ struct %(name)s {
     union { /* union tag is @%(c_name)s */
         void *data;
 ''',
-                 c_name=c_name(discriminator or 'kind'))
+                 c_name=c_name(variants.tag_member.name))
 
-    for key in typeinfo:
+    for var in variants.variants:
+        # TODO ugly special case for simple union
+        typ = var.simple_union_type() or var.type
         ret += mcgen('''
         %(c_type)s %(c_name)s;
 ''',
-                     c_type=c_type(typeinfo[key]),
-                     c_name=c_name(key))
+                     c_type=typ.c_type(),
+                     c_name=c_name(var.name))
 
     ret += mcgen('''
     };
 };
 ''')
-    if meta == 'alternate':
-        ret += mcgen('''
-extern const int %(name)s_qtypes[];
-''',
-            name=name)
-
 
     return ret
 
 def generate_type_cleanup_decl(name):
     ret = mcgen('''
-void qapi_free_%(name)s(%(c_type)s obj);
+
+void qapi_free_%(name)s(%(name)s *obj);
 ''',
-                c_type=c_type(name), name=c_name(name))
+                name=c_name(name))
     return ret
 
 def generate_type_cleanup(name):
     ret = mcgen('''
 
-void qapi_free_%(name)s(%(c_type)s obj)
+void qapi_free_%(name)s(%(name)s *obj)
 {
     QapiDeallocVisitor *md;
     Visitor *v;
@@ -278,9 +244,71 @@ void qapi_free_%(name)s(%(c_type)s obj)
     qapi_dealloc_visitor_cleanup(md);
 }
 ''',
-                c_type=c_type(name), name=c_name(name))
+                name=c_name(name))
     return ret
 
+class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
+    def __init__(self):
+        self.decl = None
+        self.defn = None
+        self.fwdecl = None
+        self.fwdefn = None
+        self.btin = None
+    def visit_begin(self, schema):
+        self.decl = ''
+        self.defn = ''
+        self.fwdecl = ''
+        self.fwdefn = ''
+        self.btin = guardstart('QAPI_TYPES_BUILTIN')
+    def visit_end(self):
+        self.decl = self.fwdecl + self.decl
+        self.fwdecl = None
+        self.defn = self.fwdefn + self.defn
+        self.fwdefn = None
+        # To avoid header dependency hell, we always generate
+        # declarations for built-in types in our header files and
+        # simply guard them.  See also do_builtins (command line
+        # option -b).
+        self.btin += guardend('QAPI_TYPES_BUILTIN')
+        self.decl = self.btin + self.decl
+        self.btin = None
+    def _gen_type_cleanup(self, name):
+        self.decl += generate_type_cleanup_decl(name)
+        self.defn += generate_type_cleanup(name)
+    def visit_enum_type(self, name, info, values):
+        self.fwdecl += generate_enum(name, values)
+        self.fwdefn += generate_enum_lookup(name, values)
+    def visit_array_type(self, name, info, element_type):
+        if isinstance(element_type, QAPISchemaBuiltinType):
+            self.btin += gen_fwd_object_or_array(name)
+            self.btin += gen_array(name, element_type)
+            self.btin += generate_type_cleanup_decl(name)
+            if do_builtins:
+                self.defn += generate_type_cleanup(name)
+        else:
+            self.fwdecl += gen_fwd_object_or_array(name)
+            self.decl += gen_array(name, element_type)
+            self._gen_type_cleanup(name)
+    def visit_object_type(self, name, info, base, members, variants):
+        if info:
+            self.fwdecl += gen_fwd_object_or_array(name)
+            if variants:
+                assert not members # not implemented
+                self.decl += gen_union(name, base, variants)
+            else:
+                self.decl += gen_struct(name, base, members)
+            self._gen_type_cleanup(name)
+    def visit_alternate_type(self, name, info, variants):
+        self.fwdecl += gen_fwd_object_or_array(name)
+        self.fwdefn += gen_alternate_qtypes(name, variants)
+        self.decl += gen_union(name, None, variants)
+        self.decl += gen_alternate_qtypes_decl(name)
+        self._gen_type_cleanup(name)
+
+# If you link code generated from multiple schemata, you want only one
+# instance of the code for built-in types.  Generate it only when
+# do_builtins, enabled by command line option -b.  See also
+# QAPISchemaGenTypeVisitor.visit_end().
 do_builtins = False
 
 (input_file, output_dir, do_c, do_h, prefix, opts) = \
@@ -336,77 +364,10 @@ fdecl.write(mcgen('''
 #include <stdint.h>
 '''))
 
-exprs = QAPISchema(input_file).get_exprs()
-
-fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
-for typename in builtin_types.keys():
-    fdecl.write(generate_fwd_builtin(typename))
-fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
-
-for expr in exprs:
-    ret = ""
-    if expr.has_key('struct'):
-        ret += generate_fwd_struct(expr['struct'])
-    elif expr.has_key('enum'):
-        ret += generate_enum(expr['enum'], expr['data'])
-        ret += generate_fwd_enum_struct(expr['enum'])
-        fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
-    elif expr.has_key('union'):
-        ret += generate_fwd_struct(expr['union'])
-        enum_define = discriminator_find_enum_define(expr)
-        if not enum_define:
-            ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
-            fdef.write(generate_enum_lookup('%sKind' % expr['union'],
-                                            expr['data'].keys()))
-    elif expr.has_key('alternate'):
-        ret += generate_fwd_struct(expr['alternate'])
-        ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
-        fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
-                                        expr['data'].keys()))
-        fdef.write(generate_alternate_qtypes(expr))
-    else:
-        continue
-    fdecl.write(ret)
-
-# to avoid header dependency hell, we always generate declarations
-# for built-in types in our header files and simply guard them
-fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
-for typename in builtin_types.keys():
-    fdecl.write(generate_type_cleanup_decl(typename + "List"))
-fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
-
-# ...this doesn't work for cases where we link in multiple objects that
-# have the functions defined, so we use -b option to provide control
-# over these cases
-if do_builtins:
-    for typename in builtin_types.keys():
-        fdef.write(generate_type_cleanup(typename + "List"))
-
-for expr in exprs:
-    ret = ""
-    if expr.has_key('struct'):
-        ret += generate_struct(expr) + "\n"
-        ret += generate_type_cleanup_decl(expr['struct'] + "List")
-        fdef.write(generate_type_cleanup(expr['struct'] + "List"))
-        ret += generate_type_cleanup_decl(expr['struct'])
-        fdef.write(generate_type_cleanup(expr['struct']))
-    elif expr.has_key('union'):
-        ret += generate_union(expr, 'union') + "\n"
-        ret += generate_type_cleanup_decl(expr['union'] + "List")
-        fdef.write(generate_type_cleanup(expr['union'] + "List"))
-        ret += generate_type_cleanup_decl(expr['union'])
-        fdef.write(generate_type_cleanup(expr['union']))
-    elif expr.has_key('alternate'):
-        ret += generate_union(expr, 'alternate') + "\n"
-        ret += generate_type_cleanup_decl(expr['alternate'] + "List")
-        fdef.write(generate_type_cleanup(expr['alternate'] + "List"))
-        ret += generate_type_cleanup_decl(expr['alternate'])
-        fdef.write(generate_type_cleanup(expr['alternate']))
-    elif expr.has_key('enum'):
-        ret += "\n" + generate_type_cleanup_decl(expr['enum'] + "List")
-        fdef.write(generate_type_cleanup(expr['enum'] + "List"))
-    else:
-        continue
-    fdecl.write(ret)
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenTypeVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
 
 close_output(fdef, fdecl)
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index a9e5aab..257b4d4 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -39,8 +39,8 @@
   'data': { 'value1' : 'UserDefA',
             'value2' : 'UserDefB',
             'value3' : 'UserDefB' } }
-# FIXME generated struct UserDefFlatUnion has members for direct base
-# UserDefUnionBase, but lacks members for indirect base UserDefZero
+# FIXME generated visit_type_UserDefFlatUnion_fields() fails to visit
+# members of indirect base UserDefZero
 
 { 'struct': 'UserDefUnionBase',
   'base': 'UserDefZero',
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 11/32] qapi-visit: Convert to QAPISchemaVisitor, fixing bugs
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (9 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 10/32] qapi-types: Convert to QAPISchemaVisitor, fixing flat unions Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-04  3:14   ` Eric Blake
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 12/32] qapi-commands: Convert to QAPISchemaVisitor Markus Armbruster
                   ` (21 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Fixes flat unions to visit the base's base members (the previous
commit merely added them to the struct).  Same test case.

Patch's effect on visit_type_UserDefFlatUnion():

     static void visit_type_UserDefFlatUnion_fields(Visitor *m, UserDefFlatUnion **obj, Error **errp)
     {
         Error *err = NULL;

    +    visit_type_int(m, &(*obj)->integer, "integer", &err);
    +    if (err) {
    +        goto out;
    +    }
         visit_type_str(m, &(*obj)->string, "string", &err);
         if (err) {
             goto out;

Test cases updated for the bug fix.

Fixes alternates to generate a visitor for their implicit enumeration
type.  None of them are currently used, obviously.  Example:
block-core.json's BlockdevRef now generates
visit_type_BlockdevRefKind().

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-visit.py                   | 260 +++++++++++++-------------------
 tests/qapi-schema/qapi-schema-test.json |   3 -
 tests/test-qmp-input-strict.c           |   4 +-
 tests/test-qmp-input-visitor.c          |   4 +-
 4 files changed, 107 insertions(+), 164 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index c493964..a6ee17f 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -12,7 +12,6 @@
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
-from ordereddict import OrderedDict
 from qapi import *
 import re
 
@@ -24,13 +23,13 @@ def generate_visit_implicit_struct(type):
         return ''
     implicit_structs_seen.add(type)
     ret = ''
-    if type not in struct_fields_seen:
+    if type.name not in struct_fields_seen:
         # Need a forward declaration
         ret += mcgen('''
 
 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
 ''',
-                     c_type=type_name(type))
+                     c_type=type.c_name())
 
     ret += mcgen('''
 
@@ -46,7 +45,7 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
     error_propagate(errp, err);
 }
 ''',
-                 c_type=type_name(type))
+                 c_type=type.c_name())
     return ret
 
 def generate_visit_struct_fields(name, members, base = None):
@@ -74,24 +73,24 @@ if (err) {
     goto out;
 }
 ''',
-                     type=type_name(base), c_name=c_name('base'))
+                     type=base.c_name(), c_name=c_name('base'))
 
-    for argname, argentry, optional in parse_args(members):
-        if optional:
+    for memb in members:
+        if memb.optional:
             ret += mcgen('''
 visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
 if (!err && (*obj)->has_%(c_name)s) {
 ''',
-                         c_name=c_name(argname), name=argname)
+                         c_name=c_name(memb.name), name=memb.name)
             push_indent()
 
         ret += mcgen('''
 visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
 ''',
-                     type=type_name(argentry), c_name=c_name(argname),
-                     name=argname)
+                     type=memb.type.c_name(), c_name=c_name(memb.name),
+                     name=memb.name)
 
-        if optional:
+        if memb.optional:
             pop_indent()
             ret += mcgen('''
 }
@@ -136,12 +135,7 @@ def generate_visit_struct_body(name):
 
     return ret
 
-def generate_visit_struct(expr):
-
-    name = expr['struct']
-    members = expr['data']
-    base = expr.get('base')
-
+def gen_visit_struct(name, base, members):
     ret = generate_visit_struct_fields(name, members, base)
 
     ret += mcgen('''
@@ -158,10 +152,10 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
 ''')
     return ret
 
-def generate_visit_list(name):
+def gen_visit_list(name, element_type):
     return mcgen('''
 
-void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
+void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
     GenericList *i, **prev;
@@ -174,8 +168,8 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E
     for (prev = (GenericList **)obj;
          !err && (i = visit_next_list(m, prev, &err)) != NULL;
          prev = &i) {
-        %(name)sList *native_i = (%(name)sList *)i;
-        visit_type_%(name)s(m, &native_i->value, NULL, &err);
+        %(name)s *native_i = (%(name)s *)i;
+        visit_type_%(c_elt_type)s(m, &native_i->value, NULL, &err);
     }
 
     error_propagate(errp, err);
@@ -185,7 +179,8 @@ out:
     error_propagate(errp, err);
 }
 ''',
-                name=type_name(name))
+                 name=c_name(name),
+                 c_elt_type=element_type.c_name())
 
 def generate_visit_enum(name):
     return mcgen('''
@@ -197,7 +192,7 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
 ''',
                  c_name=c_name(name), name=name)
 
-def generate_visit_alternate(name, members):
+def gen_visit_alternate(name, variants):
     ret = mcgen('''
 
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
@@ -216,25 +211,17 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
 ''',
                 name=c_name(name))
 
-    # For alternate, always use the default enum type automatically generated
-    # as name + 'Kind'
-    disc_type = c_name(name) + 'Kind'
-
-    for key in members:
-        assert (members[key] in builtin_types.keys()
-            or find_struct(members[key])
-            or find_union(members[key])
-            or find_enum(members[key])), "Invalid alternate member"
-
-        enum_full_value = c_enum_const(disc_type, key)
+    for var in variants.variants:
+        enum_full_value = c_enum_const(variants.tag_member.type.name,
+                                       var.name)
         ret += mcgen('''
     case %(enum_full_value)s:
         visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
         break;
 ''',
                 enum_full_value = enum_full_value,
-                c_type = type_name(members[key]),
-                c_name = c_name(key))
+                c_type=var.type.c_name(),
+                c_name=c_name(var.name))
 
     ret += mcgen('''
     default:
@@ -251,35 +238,17 @@ out:
 
     return ret
 
-
-def generate_visit_union(expr):
-
-    name = expr['union']
-    members = expr['data']
-
-    base = expr.get('base')
-    discriminator = expr.get('discriminator')
-
-    enum_define = discriminator_find_enum_define(expr)
-    if enum_define:
-        # Use the enum type as discriminator
-        ret = ""
-        disc_type = c_name(enum_define['enum_name'])
-    else:
-        # There will always be a discriminator in the C switch code, by default
-        # it is an enum type generated silently
-        ret = generate_visit_enum(name + 'Kind')
-        disc_type = c_name(name) + 'Kind'
+def gen_visit_union(name, base, variants):
+    ret = ''
 
     if base:
-        assert discriminator
-        base_fields = find_struct(base)['data'].copy()
-        del base_fields[discriminator]
-        ret += generate_visit_struct_fields(name, base_fields)
+        members = [m for m in base.members if m != variants.tag_member]
+        ret += generate_visit_struct_fields(name, members)
 
-    if discriminator:
-        for key in members:
-            ret += generate_visit_implicit_struct(members[key])
+    for var in variants.variants:
+        # TODO ugly special case for simple union
+        if not var.simple_union_type():
+            ret += generate_visit_implicit_struct(var.type)
 
     ret += mcgen('''
 
@@ -304,41 +273,41 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
 ''',
                      name=c_name(name))
 
-    if not discriminator:
-        tag = 'kind'
-        disc_key = "type"
-    else:
-        tag = discriminator
-        disc_key = discriminator
+    disc_key = variants.tag_member.name
+    if not variants.tag_name:
+        # we pointlessly use a different key for simple unions
+        disc_key = 'type'
     ret += mcgen('''
-        visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
+        visit_type_%(disc_type)s(m, &(*obj)->%(c_name)s, "%(disc_key)s", &err);
         if (err) {
             goto out_obj;
         }
         if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
             goto out_obj;
         }
-        switch ((*obj)->%(c_tag)s) {
+        switch ((*obj)->%(c_name)s) {
 ''',
-                 disc_type = disc_type,
-                 c_tag=c_name(tag),
+                 disc_type=variants.tag_member.type.c_name(),
+                 c_name=c_name(variants.tag_member.name),
                  disc_key = disc_key)
 
-    for key in members:
-        if not discriminator:
+    for var in variants.variants:
+        # TODO ugly special case for simple union
+        simple_union_type = var.simple_union_type()
+        if simple_union_type:
             fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
         else:
             fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
 
-        enum_full_value = c_enum_const(disc_type, key)
+        enum_full_value = c_enum_const(variants.tag_member.type.name, var.name)
         ret += mcgen('''
         case %(enum_full_value)s:
             ''' + fmt + '''
             break;
 ''',
                 enum_full_value = enum_full_value,
-                c_type=type_name(members[key]),
-                c_name=c_name(key))
+                c_type=(simple_union_type or var.type).c_name(),
+                c_name=c_name(var.name))
 
     ret += mcgen('''
         default:
@@ -359,38 +328,61 @@ out:
 
     return ret
 
-def generate_declaration(name, builtin_type=False):
-    ret = ""
-    if not builtin_type:
-        name = c_name(name)
-        ret += mcgen('''
-
-void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
-''',
-                     name=name)
-
-    ret += mcgen('''
-void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
-''',
-                 name=name)
-
-    return ret
-
-def generate_enum_declaration(name):
-    ret = mcgen('''
-void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
-''',
-                name=c_name(name))
-
-    return ret
-
-def generate_decl_enum(name):
+def gen_visit_decl(name, scalar=False):
+    c_type = c_name(name) + ' *'
+    if not scalar:
+        c_type += '*'
     return mcgen('''
-
-void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
+void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp);
 ''',
-                 name=c_name(name))
+                 c_name=c_name(name), c_type=c_type)
 
+class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
+    def __init__(self):
+        self.decl = None
+        self.defn = None
+        self.btin = None
+    def visit_begin(self, schema):
+        self.decl = ''
+        self.defn = ''
+        self.btin = guardstart('QAPI_VISIT_BUILTIN')
+    def visit_end(self):
+        # To avoid header dependency hell, we always generate
+        # declarations for built-in types in our header files and
+        # simply guard them.  See also do_builtins (command line
+        # option -b).
+        self.btin += guardend('QAPI_VISIT_BUILTIN')
+        self.decl = self.btin + self.decl
+        self.btin = None
+    def visit_enum_type(self, name, info, values):
+        self.decl += gen_visit_decl(name, scalar=True)
+        self.defn += generate_visit_enum(name)
+    def visit_array_type(self, name, info, element_type):
+        decl = gen_visit_decl(name)
+        defn = gen_visit_list(name, element_type)
+        if isinstance(element_type, QAPISchemaBuiltinType):
+            self.btin += decl
+            if do_builtins:
+                self.defn += defn
+        else:
+            self.decl += decl
+            self.defn += defn
+    def visit_object_type(self, name, info, base, members, variants):
+        if info:
+            self.decl += gen_visit_decl(name)
+            if variants:
+                assert not members # not implemented
+                self.defn += gen_visit_union(name, base, variants)
+            else:
+                self.defn += gen_visit_struct(name, base, members)
+    def visit_alternate_type(self, name, info, variants):
+        self.decl += gen_visit_decl(name)
+        self.defn += gen_visit_alternate(name, variants)
+
+# If you link code generated from multiple schemata, you want only one
+# instance of the code for built-in types.  Generate it only when
+# do_builtins, enabled by command line option -b.  See also
+# QAPISchemaGenVisitVisitor.visit_end().
 do_builtins = False
 
 (input_file, output_dir, do_c, do_h, prefix, opts) = \
@@ -446,56 +438,10 @@ fdecl.write(mcgen('''
 ''',
                   prefix=prefix))
 
-exprs = QAPISchema(input_file).get_exprs()
-
-# to avoid header dependency hell, we always generate declarations
-# for built-in types in our header files and simply guard them
-fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
-for typename in builtin_types.keys():
-    fdecl.write(generate_declaration(typename, builtin_type=True))
-fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
-
-# ...this doesn't work for cases where we link in multiple objects that
-# have the functions defined, so we use -b option to provide control
-# over these cases
-if do_builtins:
-    for typename in builtin_types.keys():
-        fdef.write(generate_visit_list(typename))
-
-for expr in exprs:
-    if expr.has_key('struct'):
-        ret = generate_visit_struct(expr)
-        ret += generate_visit_list(expr['struct'])
-        fdef.write(ret)
-
-        ret = generate_declaration(expr['struct'])
-        fdecl.write(ret)
-    elif expr.has_key('union'):
-        ret = generate_visit_union(expr)
-        ret += generate_visit_list(expr['union'])
-        fdef.write(ret)
-
-        enum_define = discriminator_find_enum_define(expr)
-        ret = ""
-        if not enum_define:
-            ret = generate_decl_enum('%sKind' % expr['union'])
-        ret += generate_declaration(expr['union'])
-        fdecl.write(ret)
-    elif expr.has_key('alternate'):
-        ret = generate_visit_alternate(expr['alternate'], expr['data'])
-        ret += generate_visit_list(expr['alternate'])
-        fdef.write(ret)
-
-        ret = generate_decl_enum('%sKind' % expr['alternate'])
-        ret += generate_declaration(expr['alternate'])
-        fdecl.write(ret)
-    elif expr.has_key('enum'):
-        ret = generate_visit_list(expr['enum'])
-        ret += generate_visit_enum(expr['enum'])
-        fdef.write(ret)
-
-        ret = generate_decl_enum(expr['enum'])
-        ret += generate_enum_declaration(expr['enum'])
-        fdecl.write(ret)
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenVisitVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
 
 close_output(fdef, fdecl)
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 257b4d4..b182174 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -39,8 +39,6 @@
   'data': { 'value1' : 'UserDefA',
             'value2' : 'UserDefB',
             'value3' : 'UserDefB' } }
-# FIXME generated visit_type_UserDefFlatUnion_fields() fails to visit
-# members of indirect base UserDefZero
 
 { 'struct': 'UserDefUnionBase',
   'base': 'UserDefZero',
@@ -57,7 +55,6 @@
 
 { 'alternate': 'UserDefAlternate',
   'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } }
-# FIXME only a declaration of visit_type_UserDefAlternateKind() generated
 
 { 'struct': 'UserDefC',
   'data': { 'string1': 'str', 'string2': 'str' } }
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 68f855b..a2ae786 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -167,9 +167,9 @@ static void test_validate_union_flat(TestInputVisitorData *data,
 
     v = validate_test_init(data,
                            "{ 'enum1': 'value1', "
+                           "'integer': 41, "
                            "'string': 'str', "
                            "'boolean': true }");
-    /* TODO when generator bug is fixed, add 'integer': 41 */
 
     visit_type_UserDefFlatUnion(v, &tmp, NULL, &err);
     g_assert(!err);
@@ -272,7 +272,7 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
     Visitor *v;
 
     /* test situation where discriminator field ('enum1' here) is missing */
-    v = validate_test_init(data, "{ 'string': 'c', 'string1': 'd', 'string2': 'e' }");
+    v = validate_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }");
 
     visit_type_UserDefFlatUnion2(v, &tmp, NULL, &err);
     g_assert(err);
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index a5cfefa..508c11a 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -307,15 +307,15 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
 
     v = visitor_input_test_init(data,
                                 "{ 'enum1': 'value1', "
+                                "'integer': 41, "
                                 "'string': 'str', "
                                 "'boolean': true }");
-    /* TODO when generator bug is fixed, add 'integer': 41 */
 
     visit_type_UserDefFlatUnion(v, &tmp, NULL, &err);
     g_assert(err == NULL);
     g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1);
     g_assert_cmpstr(tmp->string, ==, "str");
-    /* TODO g_assert_cmpint(tmp->integer, ==, 41); */
+    g_assert_cmpint(tmp->integer, ==, 41);
     g_assert_cmpint(tmp->value1->boolean, ==, true);
     qapi_free_UserDefFlatUnion(tmp);
 }
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 12/32] qapi-commands: Convert to QAPISchemaVisitor
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (10 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 11/32] qapi-visit: Convert to QAPISchemaVisitor, fixing bugs Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 13/32] qapi: De-duplicate enum code generation Markus Armbruster
                   ` (20 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Output unchanged apart from reordering and white-space.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-commands.py | 154 ++++++++++++++++++++++++++---------------------
 scripts/qapi.py          |   2 +-
 2 files changed, 86 insertions(+), 70 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 12bdc4c..e95fbb8 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -12,21 +12,22 @@
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
-from ordereddict import OrderedDict
 from qapi import *
 import re
 
 def generate_command_decl(name, args, ret_type):
     arglist=""
-    for argname, argtype, optional in parse_args(args):
-        argtype = c_type(argtype, is_param=True)
-        if optional:
-            arglist += "bool has_%s, " % c_name(argname)
-        arglist += "%s %s, " % (argtype, c_name(argname))
+    if args:
+        for memb in args.members:
+            argtype = memb.type.c_type(is_param=True)
+            if memb.optional:
+                arglist += "bool has_%s, " % c_name(memb.name)
+            arglist += "%s %s, " % (argtype, c_name(memb.name))
     return mcgen('''
 %(ret_type)s qmp_%(name)s(%(args)sError **errp);
 ''',
-                 ret_type=c_type(ret_type), name=c_name(name),
+                 ret_type=(ret_type and ret_type.c_type()) or 'void',
+                 name=c_name(name),
                  args=arglist)
 
 def gen_err_check(err):
@@ -45,10 +46,11 @@ def gen_sync_call(name, args, ret_type):
     retval=""
     if ret_type:
         retval = "retval = "
-    for argname, argtype, optional in parse_args(args):
-        if optional:
-            arglist += "has_%s, " % c_name(argname)
-        arglist += "%s, " % (c_name(argname))
+    if args:
+        for memb in args.members:
+            if memb.optional:
+                arglist += "has_%s, " % c_name(memb.name)
+            arglist += "%s, " % c_name(memb.name)
     push_indent()
     ret = mcgen('''
 %(retval)sqmp_%(name)s(%(args)s&local_err);
@@ -68,7 +70,7 @@ def gen_visitor_input_containers_decl(args):
     ret = ""
 
     push_indent()
-    if len(args) > 0:
+    if args:
         ret += mcgen('''
 QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
 QapiDeallocVisitor *md;
@@ -81,22 +83,26 @@ Visitor *v;
 def gen_visitor_input_vars_decl(args):
     ret = ""
     push_indent()
-    for argname, argtype, optional in parse_args(args):
-        if optional:
-            ret += mcgen('''
+
+    if args:
+        for memb in args.members:
+            if memb.optional:
+                ret += mcgen('''
 bool has_%(argname)s = false;
 ''',
-                         argname=c_name(argname))
-        if is_c_ptr(argtype):
-            ret += mcgen('''
+                             argname=c_name(memb.name))
+            if is_c_ptr(memb.type.c_type()):
+                ret += mcgen('''
 %(argtype)s %(argname)s = NULL;
 ''',
-                         argname=c_name(argname), argtype=c_type(argtype))
-        else:
-            ret += mcgen('''
+                             argname=c_name(memb.name),
+                             argtype=memb.type.c_type())
+            else:
+                ret += mcgen('''
 %(argtype)s %(argname)s = {0};
 ''',
-                         argname=c_name(argname), argtype=c_type(argtype))
+                             argname=c_name(memb.name),
+                             argtype=memb.type.c_type())
 
     pop_indent()
     return ret
@@ -106,7 +112,7 @@ def gen_visitor_input_block(args, dealloc=False):
     errparg = '&local_err'
     errarg = 'local_err'
 
-    if len(args) == 0:
+    if not args:
         return ret
 
     push_indent()
@@ -124,25 +130,26 @@ v = qapi_dealloc_get_visitor(md);
 v = qmp_input_get_visitor(mi);
 ''')
 
-    for argname, argtype, optional in parse_args(args):
-        if optional:
+    for memb in args.members:
+        if memb.optional:
             ret += mcgen('''
 visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
 ''',
-                         c_name=c_name(argname), name=argname, errp=errparg)
+                         c_name=c_name(memb.name), name=memb.name,
+                         errp=errparg)
             ret += gen_err_check(errarg)
             ret += mcgen('''
 if (has_%(c_name)s) {
 ''',
-                         c_name=c_name(argname))
+                         c_name=c_name(memb.name))
             push_indent()
         ret += mcgen('''
 visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
 ''',
-                     c_name=c_name(argname), name=argname, argtype=argtype,
-                     visitor=type_name(argtype), errp=errparg)
+                     c_name=c_name(memb.name), name=memb.name,
+                     visitor=memb.type.c_name(), errp=errparg)
         ret += gen_err_check(errarg)
-        if optional:
+        if memb.optional:
             pop_indent()
             ret += mcgen('''
 }
@@ -160,6 +167,7 @@ def gen_marshal_output(name, ret_type):
         return ""
 
     ret = mcgen('''
+
 static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *local_err = NULL;
@@ -183,8 +191,8 @@ out:
     qapi_dealloc_visitor_cleanup(md);
 }
 ''',
-                c_ret_type=c_type(ret_type), c_name=c_name(name),
-                visitor=type_name(ret_type))
+                c_ret_type=ret_type.c_type(), c_name=c_name(name),
+                visitor=ret_type.c_name())
 
     return ret
 
@@ -198,6 +206,7 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
     hdr = gen_marshal_input_decl(name, middle_mode)
 
     ret = mcgen('''
+
 %(header)s
 {
     Error *local_err = NULL;
@@ -208,9 +217,9 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
         ret += mcgen('''
     %(c_type)s retval;
 ''',
-                     c_type=c_type(ret_type))
+                     c_type=ret_type.c_type())
 
-    if len(args) > 0:
+    if args:
         ret += gen_visitor_input_containers_decl(args)
         ret += gen_visitor_input_vars_decl(args) + '\n'
         ret += gen_visitor_input_block(args) + '\n'
@@ -237,21 +246,23 @@ out:
 ''')
     return ret
 
-def gen_registry(commands):
-    registry=""
+def gen_register_command(name, success_response):
     push_indent()
-    for cmd in commands:
-        options = 'QCO_NO_OPTIONS'
-        if not cmd.get('success-response', True):
-            options = 'QCO_NO_SUCCESS_RESP'
+    options = 'QCO_NO_OPTIONS'
+    if not success_response:
+        options = 'QCO_NO_SUCCESS_RESP'
 
-        registry += mcgen('''
+    ret = mcgen('''
 qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
 ''',
-                     name=cmd['command'], c_name=c_name(cmd['command']),
+                     name=name, c_name=c_name(name),
                      opts=options)
     pop_indent()
+    return ret
+
+def gen_registry(registry):
     ret = mcgen('''
+
 static void qmp_init_marshal(void)
 {
 ''')
@@ -263,6 +274,32 @@ qapi_init(qmp_init_marshal);
 ''')
     return ret
 
+class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
+    def __init__(self):
+        self.decl = None
+        self.defn = None
+        self.regy = None
+    def visit_begin(self, schema):
+        self.decl = ''
+        self.defn = ''
+        self.regy = ''
+    def visit_end(self):
+        if not middle_mode:
+            self.defn += gen_registry(self.regy)
+        self.regy = None
+    def visit_command(self, name, info, arg_type, ret_type,
+                      gen, success_response):
+        if not gen:
+            return
+        self.decl += generate_command_decl(name, arg_type, ret_type)
+        if ret_type:
+            self.defn += gen_marshal_output(name, ret_type)
+        if middle_mode:
+            self.decl += gen_marshal_input_decl(name, middle_mode) + ';\n'
+        self.defn += gen_marshal_input(name, arg_type, ret_type, middle_mode)
+        if not middle_mode:
+            self.regy += gen_register_command(name, success_response)
+
 middle_mode = False
 
 (input_file, output_dir, do_c, do_h, prefix, opts) = \
@@ -272,10 +309,6 @@ for o, a in opts:
     if o in ("-m", "--middle"):
         middle_mode = True
 
-exprs = QAPISchema(input_file).get_exprs()
-commands = filter(lambda expr: expr.has_key('command'), exprs)
-commands = filter(lambda expr: not expr.has_key('gen'), commands)
-
 c_comment = '''
 /*
  * schema-defined QMP->QAPI command dispatch
@@ -331,29 +364,12 @@ fdecl.write(mcgen('''
 #include "qapi/error.h"
 
 ''',
-                 prefix=prefix))
+                  prefix=prefix))
 
-for cmd in commands:
-    arglist = []
-    ret_type = None
-    if cmd.has_key('data'):
-        arglist = cmd['data']
-    if cmd.has_key('returns'):
-        ret_type = cmd['returns']
-    ret = generate_command_decl(cmd['command'], arglist, ret_type)
-    fdecl.write(ret)
-    if ret_type:
-        ret = gen_marshal_output(cmd['command'], ret_type) + "\n"
-        fdef.write(ret)
-
-    if middle_mode:
-        fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], middle_mode))
-
-    ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
-    fdef.write(ret)
-
-if not middle_mode:
-    ret = gen_registry(commands)
-    fdef.write(ret)
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenCommandVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
 
 close_output(fdef, fdecl)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 3e7c154..923c3ac 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1370,7 +1370,7 @@ def c_type(value, is_param=False):
         return c_name(value) + pointer_suffix
 
 def is_c_ptr(value):
-    return c_type(value).endswith(pointer_suffix)
+    return value.endswith(pointer_suffix)
 
 def genindent(count):
     ret = ""
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 13/32] qapi: De-duplicate enum code generation
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (11 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 12/32] qapi-commands: Convert to QAPISchemaVisitor Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 14/32] qapi-event: Eliminate global variable event_enum_value Markus Armbruster
                   ` (19 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Duplicated in commit 21cd70d.  Yes, we can't import qapi-types, but
that's no excuse.  Move the helpers from qapi-types.py to qapi.py, and
replace the duplicates in qapi-event.py.

The generated event enumeration type's lookup table becomes
const-correct (see commit 2e4450f), and uses explicit indexes instead
of relying on order (see commit 912ae9c).

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt |  9 ++++---
 scripts/qapi-event.py  | 67 +++-----------------------------------------------
 scripts/qapi-types.py  | 55 -----------------------------------------
 scripts/qapi.py        | 55 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 64 insertions(+), 122 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 710defc..7c500b6 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -821,9 +821,9 @@ Example:
         QDECREF(qmp);
     }
 
-    const char *example_QAPIEvent_lookup[] = {
-        "MY_EVENT",
-        NULL,
+    const char *const example_QAPIEvent_lookup[] = {
+        [EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
+        [EXAMPLE_QAPI_EVENT_MAX] = NULL,
     };
     $ cat qapi-generated/example-qapi-event.h
 [Uninteresting stuff omitted...]
@@ -838,10 +838,11 @@ Example:
 
     void qapi_event_send_my_event(Error **errp);
 
-    extern const char *example_QAPIEvent_lookup[];
     typedef enum example_QAPIEvent {
         EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
         EXAMPLE_QAPI_EVENT_MAX = 1,
     } example_QAPIEvent;
 
+    extern const char *const example_QAPIEvent_lookup[];
+
     #endif
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index aec2d32..aed45d6 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -153,63 +153,6 @@ def generate_event_implement(api_name, event_name, params):
 
     return ret
 
-
-# Following are the functions that generate an enum type for all defined
-# events, similar to qapi-types.py. Here we already have enum name and
-# values which were generated before and recorded in event_enum_*. It also
-# works around the issue that "import qapi-types" can't work.
-
-def generate_event_enum_decl(event_enum_name, event_enum_values):
-    lookup_decl = mcgen('''
-
-extern const char *%(event_enum_name)s_lookup[];
-''',
-                        event_enum_name = event_enum_name)
-
-    enum_decl = mcgen('''
-typedef enum %(event_enum_name)s {
-''',
-                      event_enum_name = event_enum_name)
-
-    # append automatically generated _MAX value
-    enum_max_value = c_enum_const(event_enum_name, "MAX")
-    enum_values = event_enum_values + [ enum_max_value ]
-
-    i = 0
-    for value in enum_values:
-        enum_decl += mcgen('''
-    %(value)s = %(i)d,
-''',
-                     value = value,
-                     i = i)
-        i += 1
-
-    enum_decl += mcgen('''
-} %(event_enum_name)s;
-''',
-                       event_enum_name = event_enum_name)
-
-    return lookup_decl + enum_decl
-
-def generate_event_enum_lookup(event_enum_name, event_enum_strings):
-    ret = mcgen('''
-
-const char *%(event_enum_name)s_lookup[] = {
-''',
-                event_enum_name = event_enum_name)
-
-    for string in event_enum_strings:
-        ret += mcgen('''
-    "%(string)s",
-''',
-                     string = string)
-
-    ret += mcgen('''
-    NULL,
-};
-''')
-    return ret
-
 (input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
 
 c_comment = '''
@@ -266,8 +209,7 @@ fdecl.write(mcgen('''
 exprs = QAPISchema(input_file).get_exprs()
 
 event_enum_name = c_name(prefix + "QAPIEvent", protect=False)
-event_enum_values = []
-event_enum_strings = []
+event_names = []
 
 for expr in exprs:
     if expr.has_key('event'):
@@ -286,12 +228,11 @@ for expr in exprs:
         fdef.write(ret)
 
         # Record it, and generate enum later
-        event_enum_values.append(event_enum_value)
-        event_enum_strings.append(event_name)
+        event_names.append(event_name)
 
-ret = generate_event_enum_decl(event_enum_name, event_enum_values)
+ret = generate_enum(event_enum_name, event_names)
 fdecl.write(ret)
-ret = generate_event_enum_lookup(event_enum_name, event_enum_strings)
+ret = generate_enum_lookup(event_enum_name, event_names)
 fdef.write(ret)
 
 close_output(fdef, fdecl)
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 85dbd74..5ab3b3b 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -80,61 +80,6 @@ struct %(name)s {
 
     return ret
 
-def generate_enum_lookup(name, values):
-    ret = mcgen('''
-
-const char *const %(name)s_lookup[] = {
-''',
-                name=c_name(name))
-    for value in values:
-        index = c_enum_const(name, value)
-        ret += mcgen('''
-    [%(index)s] = "%(value)s",
-''',
-                     index = index, value = value)
-
-    max_index = c_enum_const(name, 'MAX')
-    ret += mcgen('''
-    [%(max_index)s] = NULL,
-};
-''',
-        max_index=max_index)
-    return ret
-
-def generate_enum(name, values):
-    name = c_name(name)
-    lookup_decl = mcgen('''
-
-extern const char *const %(name)s_lookup[];
-''',
-                name=name)
-
-    enum_decl = mcgen('''
-
-typedef enum %(name)s {
-''',
-                name=name)
-
-    # append automatically generated _MAX value
-    enum_values = values + [ 'MAX' ]
-
-    i = 0
-    for value in enum_values:
-        enum_full_value = c_enum_const(name, value)
-        enum_decl += mcgen('''
-    %(enum_full_value)s = %(i)d,
-''',
-                     enum_full_value = enum_full_value,
-                     i=i)
-        i += 1
-
-    enum_decl += mcgen('''
-} %(name)s;
-''',
-                 name=name)
-
-    return enum_decl + lookup_decl
-
 def gen_alternate_qtypes_decl(name):
     return mcgen('''
 
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 923c3ac..641ea9f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1424,6 +1424,61 @@ def guardend(name):
 ''',
                  name=guardname(name))
 
+def generate_enum_lookup(name, values):
+    ret = mcgen('''
+
+const char *const %(name)s_lookup[] = {
+''',
+                name=c_name(name))
+    for value in values:
+        index = c_enum_const(name, value)
+        ret += mcgen('''
+    [%(index)s] = "%(value)s",
+''',
+                     index = index, value = value)
+
+    max_index = c_enum_const(name, 'MAX')
+    ret += mcgen('''
+    [%(max_index)s] = NULL,
+};
+''',
+        max_index=max_index)
+    return ret
+
+def generate_enum(name, values):
+    name = c_name(name)
+    lookup_decl = mcgen('''
+
+extern const char *const %(name)s_lookup[];
+''',
+                name=name)
+
+    enum_decl = mcgen('''
+
+typedef enum %(name)s {
+''',
+                name=name)
+
+    # append automatically generated _MAX value
+    enum_values = values + [ 'MAX' ]
+
+    i = 0
+    for value in enum_values:
+        enum_full_value = c_enum_const(name, value)
+        enum_decl += mcgen('''
+    %(enum_full_value)s = %(i)d,
+''',
+                     enum_full_value = enum_full_value,
+                     i=i)
+        i += 1
+
+    enum_decl += mcgen('''
+} %(name)s;
+''',
+                 name=name)
+
+    return enum_decl + lookup_decl
+
 #
 # Common command line parsing
 #
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 14/32] qapi-event: Eliminate global variable event_enum_value
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (12 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 13/32] qapi: De-duplicate enum code generation Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 15/32] qapi-event: Convert to QAPISchemaVisitor, fixing data with base Markus Armbruster
                   ` (18 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-event.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index aed45d6..537da17 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -137,7 +137,7 @@ def generate_event_implement(api_name, event_name, params):
     emit(%(event_enum_value)s, qmp, &local_err);
 
 """,
-                 event_enum_value = event_enum_value)
+                 event_enum_value = c_enum_const(event_enum_name, event_name))
 
     # step 5: clean up
     if params:
@@ -223,7 +223,6 @@ for expr in exprs:
         fdecl.write(ret)
 
         # We need an enum value per event
-        event_enum_value = c_enum_const(event_enum_name, event_name)
         ret = generate_event_implement(api_name, event_name, params)
         fdef.write(ret)
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 15/32] qapi-event: Convert to QAPISchemaVisitor, fixing data with base
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (13 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 14/32] qapi-event: Eliminate global variable event_enum_value Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 16/32] qapi: Generate comments to simplify splitting for review Markus Armbruster
                   ` (17 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Fixes events whose data is struct with base to include the struct's
base members.  Test case is qapi-schema-test.json's event
__org.qemu_x-command:

    { 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' }

    { 'struct': '__org.qemu_x-Struct', 'base': '__org.qemu_x-Base',
      'data': { '__org.qemu_x-member2': 'str' } }

    { 'struct': '__org.qemu_x-Base',
      'data': { '__org.qemu_x-member1': '__org.qemu_x-Enum' } }

Patch's effect on generated qapi_event_send___org_qemu_x_event():

    -void qapi_event_send___org_qemu_x_event(const char *__org_qemu_x_member2,
    +void qapi_event_send___org_qemu_x_event(__org_qemu_x_Enum __org_qemu_x_member1,
    +                                        const char *__org_qemu_x_member2,
                                             Error **errp)
     {
         QDict *qmp;
    @@ -224,6 +225,10 @@ void qapi_event_send___org_qemu_x_event(
             goto clean;
         }

    +    visit_type___org_qemu_x_Enum(v, &__org_qemu_x_member1, "__org.qemu_x-member1", &local_err);
    +    if (local_err) {
    +        goto clean;
    +    }
         visit_type_str(v, (char **)&__org_qemu_x_member2, "__org.qemu_x-member2", &local_err);
         if (local_err) {
             goto clean;

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-event.py                   | 84 ++++++++++++++++-----------------
 tests/qapi-schema/qapi-schema-test.json |  3 --
 2 files changed, 42 insertions(+), 45 deletions(-)

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 537da17..7525bab 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -2,14 +2,15 @@
 # QAPI event generator
 #
 # Copyright (c) 2014 Wenchao Xia
+# Copyright (c) 2015 Red Hat Inc.
 #
 # Authors:
 #  Wenchao Xia <wenchaoqemu@gmail.com>
+#  Markus Armbruster <armbru@redhat.com>
 #
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
-from ordereddict import OrderedDict
 from qapi import *
 
 def _generate_event_api_name(event_name, params):
@@ -17,13 +18,13 @@ def _generate_event_api_name(event_name, params):
     l = len(api_name)
 
     if params:
-        for argname, argentry, optional in parse_args(params):
-            if optional:
-                api_name += "bool has_%s,\n" % c_name(argname)
+        for m in params.members:
+            if m.optional:
+                api_name += "bool has_%s,\n" % c_name(m.name)
                 api_name += "".ljust(l)
 
-            api_name += "%s %s,\n" % (c_type(argentry, is_param=True),
-                                      c_name(argname))
+            api_name += "%s %s,\n" % (m.type.c_type(is_param=True),
+                                      c_name(m.name))
             api_name += "".ljust(l)
 
     api_name += "Error **errp)"
@@ -51,7 +52,7 @@ def generate_event_implement(api_name, event_name, params):
 """,
                 api_name = api_name)
 
-    if params:
+    if params and params.members:
         ret += mcgen("""
     QmpOutputVisitor *qov;
     Visitor *v;
@@ -72,7 +73,7 @@ def generate_event_implement(api_name, event_name, params):
                  event_name = event_name)
 
     # step 3: visit the params if params != None
-    if params:
+    if params and params.members:
         ret += mcgen("""
     qov = qmp_output_visitor_new();
     g_assert(qov);
@@ -89,15 +90,15 @@ def generate_event_implement(api_name, event_name, params):
 """,
                 event_name = event_name)
 
-        for argname, argentry, optional in parse_args(params):
-            if optional:
+        for memb in params.members:
+            if memb.optional:
                 ret += mcgen("""
     if (has_%(var)s) {
 """,
-                             var = c_name(argname))
+                             var=c_name(memb.name))
                 push_indent()
 
-            if argentry == "str":
+            if memb.type.name == "str":
                 var_type = "(char **)"
             else:
                 var_type = ""
@@ -109,11 +110,11 @@ def generate_event_implement(api_name, event_name, params):
     }
 """,
                          var_type = var_type,
-                         var = c_name(argname),
-                         type = type_name(argentry),
-                         name = argname)
+                         var=c_name(memb.name),
+                         type=memb.type.c_name(),
+                         name=memb.name)
 
-            if optional:
+            if memb.optional:
                 pop_indent()
                 ret += mcgen("""
     }
@@ -140,7 +141,7 @@ def generate_event_implement(api_name, event_name, params):
                  event_enum_value = c_enum_const(event_enum_name, event_name))
 
     # step 5: clean up
-    if params:
+    if params and params.members:
         ret += mcgen("""
  clean:
     qmp_output_visitor_cleanup(qov);
@@ -153,6 +154,25 @@ def generate_event_implement(api_name, event_name, params):
 
     return ret
 
+class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
+    def __init__(self):
+        self.decl = None
+        self.defn = None
+        self.event_names = None
+    def visit_begin(self, schema):
+        self.decl = ''
+        self.defn = ''
+        self.event_names = []
+    def visit_end(self):
+        self.decl += generate_enum(event_enum_name, self.event_names)
+        self.defn += generate_enum_lookup(event_enum_name, self.event_names)
+        self.event_names = None
+    def visit_event(self, name, info, arg_type):
+        api_name = _generate_event_api_name(name, arg_type)
+        self.decl += generate_event_declaration(api_name)
+        self.defn += generate_event_implement(api_name, name, arg_type)
+        self.event_names.append(name)
+
 (input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
 
 c_comment = '''
@@ -206,32 +226,12 @@ fdecl.write(mcgen('''
 ''',
                   prefix=prefix))
 
-exprs = QAPISchema(input_file).get_exprs()
-
 event_enum_name = c_name(prefix + "QAPIEvent", protect=False)
-event_names = []
 
-for expr in exprs:
-    if expr.has_key('event'):
-        event_name = expr['event']
-        params = expr.get('data')
-        if params and len(params) == 0:
-            params = None
-
-        api_name = _generate_event_api_name(event_name, params)
-        ret = generate_event_declaration(api_name)
-        fdecl.write(ret)
-
-        # We need an enum value per event
-        ret = generate_event_implement(api_name, event_name, params)
-        fdef.write(ret)
-
-        # Record it, and generate enum later
-        event_names.append(event_name)
-
-ret = generate_enum(event_enum_name, event_names)
-fdecl.write(ret)
-ret = generate_enum_lookup(event_enum_name, event_names)
-fdef.write(ret)
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenEventVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
 
 close_output(fdef, fdecl)
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index b182174..90b4740 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -126,9 +126,6 @@
 { 'alternate': '__org.qemu_x-Alt',
   'data': { '__org.qemu_x-branch': 'str', 'b': '__org.qemu_x-Base' } }
 { 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' }
-# FIXME generated qapi_event_send___org_qemu_x_event() has only a
-# parameter for data's member __org_qemu_x_member2, none for its base
-# __org.qemu_x-Base's member __org_qemu_x_member1
 { 'command': '__org.qemu_x-command',
   'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
             'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 16/32] qapi: Generate comments to simplify splitting for review
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (14 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 15/32] qapi-event: Convert to QAPISchemaVisitor, fixing data with base Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 17/32] Revert "qapi: Generate comments to simplify splitting for review" Markus Armbruster
                   ` (16 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

The effect of the previous few patches on generated files is hard to
review, because stuff gets generated in different order, which renders
diffs of the generated files useless.

To get reviewable diffs, we need to split the generated files into
suitable parts: put every top-level declaration in a file named like
the thing declared, so we can diff top-level declarations regardless
of their order in the generated files.

Since I don't feel like parsing C, simply generate a // comment
identifying the declaration right before each top-level declaration.
This lets us split with a simple shell loop:

    mkdir -p n
    for i in q*-{commands,marshal,event,types,visit}.[ch]
    do
        csplit -n 4 -s "$i" '/^\/\//' '{*}'
        for j in xx*
        do
            read h t <$j
            [ "$h" = "//" ] || t=""
            mv $j n/"$i-${j#xx}-${t/ /-}"
        done
    done

Splits each file F into F-NUMB-ID, where NUMB counts up from 0001, and
ID comes from the // comment.

To check the split's sane, we can run

    for i in q*-{commands,marshal,event,types,visit}.[ch]
    do cat n/$i-* | diff $i -
    done

We got a commit similar to this one right before the patches we want
to check.  If you have that commit's split files in directory o, and
this commit's split files in directory n, you can diff them with this
loop:

    for i in `(ls o; ls n) | sed -n 's/-[0-9][0-9][0-9][0-9]-/-*-/p' | sort | uniq`
    do
        diff -uBp o/$i n/$i
    done

Complete annotated output:

* QAPIEvent_lookup[] and enum QAPIEvent

  Innocent changes due to "De-duplicate enum code generation".

    --- o/qapi-event.c-0037-def-QAPIEvent_lookup	2015-08-04 12:47:16.479980075 +0200
    +++ n/qapi-event.c-0037-def-QAPIEvent_lookup	2015-08-04 13:02:46.336626925 +0200
    @@ -1,39 +1,39 @@
     // def QAPIEvent_lookup
    -const char *QAPIEvent_lookup[] = {
    -    "BLOCK_IMAGE_CORRUPTED",
    -    "BLOCK_IO_ERROR",
    -    "BLOCK_JOB_COMPLETED",
    -    "BLOCK_JOB_CANCELLED",
    -    "BLOCK_JOB_ERROR",
    -    "BLOCK_JOB_READY",
    -    "BLOCK_WRITE_THRESHOLD",
    -    "DEVICE_TRAY_MOVED",
    -    "SHUTDOWN",
    -    "POWERDOWN",
    -    "RESET",
    -    "STOP",
    -    "RESUME",
    -    "SUSPEND",
    -    "SUSPEND_DISK",
    -    "WAKEUP",
    -    "RTC_CHANGE",
    -    "WATCHDOG",
    -    "DEVICE_DELETED",
    -    "NIC_RX_FILTER_CHANGED",
    -    "VNC_CONNECTED",
    -    "VNC_INITIALIZED",
    -    "VNC_DISCONNECTED",
    -    "SPICE_CONNECTED",
    -    "SPICE_INITIALIZED",
    -    "SPICE_DISCONNECTED",
    -    "SPICE_MIGRATE_COMPLETED",
    -    "MIGRATION",
    -    "ACPI_DEVICE_OST",
    -    "BALLOON_CHANGE",
    -    "GUEST_PANICKED",
    -    "QUORUM_FAILURE",
    -    "QUORUM_REPORT_BAD",
    -    "VSERPORT_CHANGE",
    -    "MEM_UNPLUG_ERROR",
    -    NULL,
    +const char *const QAPIEvent_lookup[] = {
    +    [QAPI_EVENT_ACPI_DEVICE_OST] = "ACPI_DEVICE_OST",
    +    [QAPI_EVENT_BALLOON_CHANGE] = "BALLOON_CHANGE",
    +    [QAPI_EVENT_BLOCK_IMAGE_CORRUPTED] = "BLOCK_IMAGE_CORRUPTED",
    +    [QAPI_EVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR",
    +    [QAPI_EVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED",
    +    [QAPI_EVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED",
    +    [QAPI_EVENT_BLOCK_JOB_ERROR] = "BLOCK_JOB_ERROR",
    +    [QAPI_EVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY",
    +    [QAPI_EVENT_BLOCK_WRITE_THRESHOLD] = "BLOCK_WRITE_THRESHOLD",
    +    [QAPI_EVENT_DEVICE_DELETED] = "DEVICE_DELETED",
    +    [QAPI_EVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
    +    [QAPI_EVENT_GUEST_PANICKED] = "GUEST_PANICKED",
    +    [QAPI_EVENT_MEM_UNPLUG_ERROR] = "MEM_UNPLUG_ERROR",
    +    [QAPI_EVENT_MIGRATION] = "MIGRATION",
    +    [QAPI_EVENT_NIC_RX_FILTER_CHANGED] = "NIC_RX_FILTER_CHANGED",
    +    [QAPI_EVENT_POWERDOWN] = "POWERDOWN",
    +    [QAPI_EVENT_QUORUM_FAILURE] = "QUORUM_FAILURE",
    +    [QAPI_EVENT_QUORUM_REPORT_BAD] = "QUORUM_REPORT_BAD",
    +    [QAPI_EVENT_RESET] = "RESET",
    +    [QAPI_EVENT_RESUME] = "RESUME",
    +    [QAPI_EVENT_RTC_CHANGE] = "RTC_CHANGE",
    +    [QAPI_EVENT_SHUTDOWN] = "SHUTDOWN",
    +    [QAPI_EVENT_SPICE_CONNECTED] = "SPICE_CONNECTED",
    +    [QAPI_EVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED",
    +    [QAPI_EVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED",
    +    [QAPI_EVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED",
    +    [QAPI_EVENT_STOP] = "STOP",
    +    [QAPI_EVENT_SUSPEND] = "SUSPEND",
    +    [QAPI_EVENT_SUSPEND_DISK] = "SUSPEND_DISK",
    +    [QAPI_EVENT_VNC_CONNECTED] = "VNC_CONNECTED",
    +    [QAPI_EVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED",
    +    [QAPI_EVENT_VNC_INITIALIZED] = "VNC_INITIALIZED",
    +    [QAPI_EVENT_VSERPORT_CHANGE] = "VSERPORT_CHANGE",
    +    [QAPI_EVENT_WAKEUP] = "WAKEUP",
    +    [QAPI_EVENT_WATCHDOG] = "WATCHDOG",
    +    [QAPI_EVENT_MAX] = NULL,
     };
    --- o/qapi-event.h-0037-decl-QAPIEvent_lookup	2015-08-04 12:47:16.480980103 +0200
    +++ n/qapi-event.h-0038-decl-QAPIEvent_lookup	2015-08-04 13:02:46.387628338 +0200
    @@ -1,2 +1,3 @@
     // decl QAPIEvent_lookup
    -extern const char *QAPIEvent_lookup[];
    +extern const char *const QAPIEvent_lookup[];
    +
    --- o/qapi-event.h-0038-typedef-QAPIEvent	2015-08-04 12:47:16.480980103 +0200
    +++ n/qapi-event.h-0037-typedef-QAPIEvent	2015-08-04 13:02:46.387628338 +0200
    @@ -1,40 +1,40 @@
     // typedef QAPIEvent
     typedef enum QAPIEvent {
    -    QAPI_EVENT_BLOCK_IMAGE_CORRUPTED = 0,
    -    QAPI_EVENT_BLOCK_IO_ERROR = 1,
    -    QAPI_EVENT_BLOCK_JOB_COMPLETED = 2,
    -    QAPI_EVENT_BLOCK_JOB_CANCELLED = 3,
    -    QAPI_EVENT_BLOCK_JOB_ERROR = 4,
    -    QAPI_EVENT_BLOCK_JOB_READY = 5,
    -    QAPI_EVENT_BLOCK_WRITE_THRESHOLD = 6,
    -    QAPI_EVENT_DEVICE_TRAY_MOVED = 7,
    -    QAPI_EVENT_SHUTDOWN = 8,
    -    QAPI_EVENT_POWERDOWN = 9,
    -    QAPI_EVENT_RESET = 10,
    -    QAPI_EVENT_STOP = 11,
    -    QAPI_EVENT_RESUME = 12,
    -    QAPI_EVENT_SUSPEND = 13,
    -    QAPI_EVENT_SUSPEND_DISK = 14,
    -    QAPI_EVENT_WAKEUP = 15,
    -    QAPI_EVENT_RTC_CHANGE = 16,
    -    QAPI_EVENT_WATCHDOG = 17,
    -    QAPI_EVENT_DEVICE_DELETED = 18,
    -    QAPI_EVENT_NIC_RX_FILTER_CHANGED = 19,
    -    QAPI_EVENT_VNC_CONNECTED = 20,
    -    QAPI_EVENT_VNC_INITIALIZED = 21,
    -    QAPI_EVENT_VNC_DISCONNECTED = 22,
    -    QAPI_EVENT_SPICE_CONNECTED = 23,
    +    QAPI_EVENT_ACPI_DEVICE_OST = 0,
    +    QAPI_EVENT_BALLOON_CHANGE = 1,
    +    QAPI_EVENT_BLOCK_IMAGE_CORRUPTED = 2,
    +    QAPI_EVENT_BLOCK_IO_ERROR = 3,
    +    QAPI_EVENT_BLOCK_JOB_CANCELLED = 4,
    +    QAPI_EVENT_BLOCK_JOB_COMPLETED = 5,
    +    QAPI_EVENT_BLOCK_JOB_ERROR = 6,
    +    QAPI_EVENT_BLOCK_JOB_READY = 7,
    +    QAPI_EVENT_BLOCK_WRITE_THRESHOLD = 8,
    +    QAPI_EVENT_DEVICE_DELETED = 9,
    +    QAPI_EVENT_DEVICE_TRAY_MOVED = 10,
    +    QAPI_EVENT_GUEST_PANICKED = 11,
    +    QAPI_EVENT_MEM_UNPLUG_ERROR = 12,
    +    QAPI_EVENT_MIGRATION = 13,
    +    QAPI_EVENT_NIC_RX_FILTER_CHANGED = 14,
    +    QAPI_EVENT_POWERDOWN = 15,
    +    QAPI_EVENT_QUORUM_FAILURE = 16,
    +    QAPI_EVENT_QUORUM_REPORT_BAD = 17,
    +    QAPI_EVENT_RESET = 18,
    +    QAPI_EVENT_RESUME = 19,
    +    QAPI_EVENT_RTC_CHANGE = 20,
    +    QAPI_EVENT_SHUTDOWN = 21,
    +    QAPI_EVENT_SPICE_CONNECTED = 22,
    +    QAPI_EVENT_SPICE_DISCONNECTED = 23,
         QAPI_EVENT_SPICE_INITIALIZED = 24,
    -    QAPI_EVENT_SPICE_DISCONNECTED = 25,
    -    QAPI_EVENT_SPICE_MIGRATE_COMPLETED = 26,
    -    QAPI_EVENT_MIGRATION = 27,
    -    QAPI_EVENT_ACPI_DEVICE_OST = 28,
    -    QAPI_EVENT_BALLOON_CHANGE = 29,
    -    QAPI_EVENT_GUEST_PANICKED = 30,
    -    QAPI_EVENT_QUORUM_FAILURE = 31,
    -    QAPI_EVENT_QUORUM_REPORT_BAD = 32,
    -    QAPI_EVENT_VSERPORT_CHANGE = 33,
    -    QAPI_EVENT_MEM_UNPLUG_ERROR = 34,
    +    QAPI_EVENT_SPICE_MIGRATE_COMPLETED = 25,
    +    QAPI_EVENT_STOP = 26,
    +    QAPI_EVENT_SUSPEND = 27,
    +    QAPI_EVENT_SUSPEND_DISK = 28,
    +    QAPI_EVENT_VNC_CONNECTED = 29,
    +    QAPI_EVENT_VNC_DISCONNECTED = 30,
    +    QAPI_EVENT_VNC_INITIALIZED = 31,
    +    QAPI_EVENT_VSERPORT_CHANGE = 32,
    +    QAPI_EVENT_WAKEUP = 33,
    +    QAPI_EVENT_WATCHDOG = 34,
         QAPI_EVENT_MAX = 35,
     } QAPIEvent;

* Guards QAPI_TYPES_BUILTIN_STRUCT_DECL and
  QAPI_TYPES_BUILTIN_CLEANUP_DECL replaced by just QAPI_TYPES_BUILTIN

    diff: o/qapi-types.h-*-guardend-QAPI_TYPES_BUILTIN: No such file or directory
    diff: n/qapi-types.h-*-guardend-QAPI_TYPES_BUILTIN_CLEANUP_DECL: No such file or directory
    diff: n/qapi-types.h-*-guardend-QAPI_TYPES_BUILTIN_STRUCT_DECL: No such file or directory
    diff: o/qapi-types.h-*-guardstart-QAPI_TYPES_BUILTIN: No such file or directory
    diff: n/qapi-types.h-*-guardstart-QAPI_TYPES_BUILTIN_CLEANUP_DECL: No such file or directory
    diff: n/qapi-types.h-*-guardstart-QAPI_TYPES_BUILTIN_STRUCT_DECL: No such file or directory

* A bunch of new forward declarations due to different ordering

    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsArchipelago_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsBlkdebug_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsBlkverify_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsFile_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsGenericCOWFormat_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsGenericFormat_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsNull_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsQcow2_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsQuorum_fields: No such file or directory
    diff: o/qapi-visit.c-*-decl-visit_type_BlockdevOptionsVVFAT_fields: No such file or directory

* New visitors due to "qapi-visit: Convert to QAPISchemaVisitor, fixing bugs"

    diff: o/qapi-visit.c-*-def-visit_type_BlockdevRefKind: No such file or directory
    diff: o/qapi-visit.c-*-def-visit_type_Qcow2OverlapChecksKind: No such file or directory

* Guard QAPI_VISIT_BUILTIN_VISITOR_DECL replaced by QAPI_VISIT_BUILTIN

    diff: o/qapi-visit.h-*-guardend-QAPI_VISIT_BUILTIN: No such file or directory
    diff: n/qapi-visit.h-*-guardend-QAPI_VISIT_BUILTIN_VISITOR_DECL: No such file or directory
    diff: o/qapi-visit.h-*-guardstart-QAPI_VISIT_BUILTIN: No such file or directory
    diff: n/qapi-visit.h-*-guardstart-QAPI_VISIT_BUILTIN_VISITOR_DECL: No such file or directory

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-commands.py | 7 +++++++
 scripts/qapi-event.py    | 9 +++++++++
 scripts/qapi-types.py    | 8 ++++++++
 scripts/qapi-visit.py    | 9 +++++++++
 scripts/qapi.py          | 8 ++++++++
 5 files changed, 41 insertions(+)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index e95fbb8..2dd1895 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -24,6 +24,7 @@ def generate_command_decl(name, args, ret_type):
                 arglist += "bool has_%s, " % c_name(memb.name)
             arglist += "%s %s, " % (argtype, c_name(memb.name))
     return mcgen('''
+// decl qmp_%(name)s
 %(ret_type)s qmp_%(name)s(%(args)sError **errp);
 ''',
                  ret_type=(ret_type and ret_type.c_type()) or 'void',
@@ -168,6 +169,7 @@ def gen_marshal_output(name, ret_type):
 
     ret = mcgen('''
 
+// def qmp_marshal_output_%(c_name)s
 static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *local_err = NULL;
@@ -207,10 +209,12 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
 
     ret = mcgen('''
 
+// def qmp_marshal_input_%(name)s
 %(header)s
 {
     Error *local_err = NULL;
 ''',
+                name=name,
                 header=hdr)
 
     if ret_type:
@@ -263,6 +267,7 @@ qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
 def gen_registry(registry):
     ret = mcgen('''
 
+// def qmp_init_marshal
 static void qmp_init_marshal(void)
 {
 ''')
@@ -270,6 +275,7 @@ static void qmp_init_marshal(void)
     ret += mcgen('''
 }
 
+// decl qapi_init
 qapi_init(qmp_init_marshal);
 ''')
     return ret
@@ -295,6 +301,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         if ret_type:
             self.defn += gen_marshal_output(name, ret_type)
         if middle_mode:
+            self.decl += '// decl gen_marshal_input_%s\n' % name
             self.decl += gen_marshal_input_decl(name, middle_mode) + ';\n'
         self.defn += gen_marshal_input(name, arg_type, ret_type, middle_mode)
         if not middle_mode:
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 7525bab..5128e9a 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -11,6 +11,7 @@
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
+import re;
 from qapi import *
 
 def _generate_event_api_name(event_name, params):
@@ -34,22 +35,30 @@ def _generate_event_api_name(event_name, params):
 # Following are the core functions that generate C APIs to emit event.
 
 def generate_event_declaration(api_name):
+    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
+    name = match.group(1)
     return mcgen('''
 
+// decl %(name)s
 %(api_name)s;
 ''',
+                 name=name,
                  api_name = api_name)
 
 def generate_event_implement(api_name, event_name, params):
+    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
+    name = match.group(1)
     # step 1: declare any variables
     ret = mcgen("""
 
+// def %(name)s
 %(api_name)s
 {
     QDict *qmp;
     Error *local_err = NULL;
     QMPEventFuncEmit emit;
 """,
+                name=name,
                 api_name = api_name)
 
     if params and params.members:
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 5ab3b3b..2cce286 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -16,6 +16,7 @@ from qapi import *
 def gen_fwd_object_or_array(name):
     return mcgen('''
 
+// typedef %(name)s
 typedef struct %(name)s %(name)s;
 ''',
                  name=c_name(name))
@@ -23,6 +24,7 @@ typedef struct %(name)s %(name)s;
 def gen_array(name, element_type):
     return mcgen('''
 
+// struct %(name)s
 struct %(name)s {
     union {
         %(c_type)s value;
@@ -57,6 +59,7 @@ def generate_struct_fields(members):
 def gen_struct(name, base, members):
     ret = mcgen('''
 
+// struct %(name)s
 struct %(name)s {
 ''',
                 name=c_name(name))
@@ -83,6 +86,7 @@ struct %(name)s {
 def gen_alternate_qtypes_decl(name):
     return mcgen('''
 
+// decl %(c_name)s_qtypes
 extern const int %(c_name)s_qtypes[];
 ''',
                  c_name=c_name(name))
@@ -90,6 +94,7 @@ extern const int %(c_name)s_qtypes[];
 def gen_alternate_qtypes(name, variants):
     ret = mcgen('''
 
+// def %(name)s_qtypes
 const int %(name)s_qtypes[QTYPE_MAX] = {
 ''',
                 name=c_name(name))
@@ -115,6 +120,7 @@ def gen_union(name, base, variants):
 
     ret = mcgen('''
 
+// struct %(name)s
 struct %(name)s {
 ''',
                 name=name)
@@ -166,6 +172,7 @@ struct %(name)s {
 def generate_type_cleanup_decl(name):
     ret = mcgen('''
 
+// decl qapi_free_%(name)s
 void qapi_free_%(name)s(%(name)s *obj);
 ''',
                 name=c_name(name))
@@ -174,6 +181,7 @@ void qapi_free_%(name)s(%(name)s *obj);
 def generate_type_cleanup(name):
     ret = mcgen('''
 
+// def qapi_free_%(name)s
 void qapi_free_%(name)s(%(name)s *obj)
 {
     QapiDeallocVisitor *md;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index a6ee17f..5893540 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -27,12 +27,14 @@ def generate_visit_implicit_struct(type):
         # Need a forward declaration
         ret += mcgen('''
 
+// decl visit_type_%(c_type)s_fields
 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
 ''',
                      c_type=type.c_name())
 
     ret += mcgen('''
 
+// def visit_type_implicit_%(c_type)s
 static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -58,6 +60,7 @@ def generate_visit_struct_fields(name, members, base = None):
 
     ret += mcgen('''
 
+// def visit_type_%(name)s_fields
 static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -140,6 +143,7 @@ def gen_visit_struct(name, base, members):
 
     ret += mcgen('''
 
+// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
 ''',
@@ -155,6 +159,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
 def gen_visit_list(name, element_type):
     return mcgen('''
 
+// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -185,6 +190,7 @@ out:
 def generate_visit_enum(name):
     return mcgen('''
 
+// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
 {
     visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
@@ -195,6 +201,7 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
 def gen_visit_alternate(name, variants):
     ret = mcgen('''
 
+// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -252,6 +259,7 @@ def gen_visit_union(name, base, variants):
 
     ret += mcgen('''
 
+// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -333,6 +341,7 @@ def gen_visit_decl(name, scalar=False):
     if not scalar:
         c_type += '*'
     return mcgen('''
+// decl visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp);
 ''',
                  c_name=c_name(name), c_type=c_type)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 641ea9f..97d0a8e 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1410,6 +1410,7 @@ def guardname(filename):
 def guardstart(name):
     return mcgen('''
 
+// guardstart %(name)s
 #ifndef %(name)s
 #define %(name)s
 
@@ -1419,6 +1420,7 @@ def guardstart(name):
 def guardend(name):
     return mcgen('''
 
+// guardend %(name)s
 #endif /* %(name)s */
 
 ''',
@@ -1427,6 +1429,7 @@ def guardend(name):
 def generate_enum_lookup(name, values):
     ret = mcgen('''
 
+// def %(name)s_lookup
 const char *const %(name)s_lookup[] = {
 ''',
                 name=c_name(name))
@@ -1449,12 +1452,14 @@ def generate_enum(name, values):
     name = c_name(name)
     lookup_decl = mcgen('''
 
+// decl %(name)s_lookup
 extern const char *const %(name)s_lookup[];
 ''',
                 name=name)
 
     enum_decl = mcgen('''
 
+// typedef %(name)s
 typedef enum %(name)s {
 ''',
                 name=name)
@@ -1558,12 +1563,14 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
     fdecl = maybe_open(do_h, h_file, 'w')
 
     fdef.write(mcgen('''
+// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 ''',
                      comment = c_comment))
 
     fdecl.write(mcgen('''
+// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 #ifndef %(guard)s
@@ -1576,6 +1583,7 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
 
 def close_output(fdef, fdecl):
     fdecl.write('''
+// epilogue
 #endif
 ''')
     fdecl.close()
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 17/32] Revert "qapi: Generate comments to simplify splitting for review"
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (15 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 16/32] qapi: Generate comments to simplify splitting for review Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 18/32] qapi: Replace dirty is_c_ptr() by method c_null() Markus Armbruster
                   ` (15 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

This reverts commit ebce6e3cd3fdf3c1d507a060c80822d98b034ba5.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-commands.py | 7 -------
 scripts/qapi-event.py    | 9 ---------
 scripts/qapi-types.py    | 8 --------
 scripts/qapi-visit.py    | 9 ---------
 scripts/qapi.py          | 8 --------
 5 files changed, 41 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 2dd1895..e95fbb8 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -24,7 +24,6 @@ def generate_command_decl(name, args, ret_type):
                 arglist += "bool has_%s, " % c_name(memb.name)
             arglist += "%s %s, " % (argtype, c_name(memb.name))
     return mcgen('''
-// decl qmp_%(name)s
 %(ret_type)s qmp_%(name)s(%(args)sError **errp);
 ''',
                  ret_type=(ret_type and ret_type.c_type()) or 'void',
@@ -169,7 +168,6 @@ def gen_marshal_output(name, ret_type):
 
     ret = mcgen('''
 
-// def qmp_marshal_output_%(c_name)s
 static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *local_err = NULL;
@@ -209,12 +207,10 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
 
     ret = mcgen('''
 
-// def qmp_marshal_input_%(name)s
 %(header)s
 {
     Error *local_err = NULL;
 ''',
-                name=name,
                 header=hdr)
 
     if ret_type:
@@ -267,7 +263,6 @@ qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
 def gen_registry(registry):
     ret = mcgen('''
 
-// def qmp_init_marshal
 static void qmp_init_marshal(void)
 {
 ''')
@@ -275,7 +270,6 @@ static void qmp_init_marshal(void)
     ret += mcgen('''
 }
 
-// decl qapi_init
 qapi_init(qmp_init_marshal);
 ''')
     return ret
@@ -301,7 +295,6 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         if ret_type:
             self.defn += gen_marshal_output(name, ret_type)
         if middle_mode:
-            self.decl += '// decl gen_marshal_input_%s\n' % name
             self.decl += gen_marshal_input_decl(name, middle_mode) + ';\n'
         self.defn += gen_marshal_input(name, arg_type, ret_type, middle_mode)
         if not middle_mode:
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 5128e9a..7525bab 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -11,7 +11,6 @@
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
-import re;
 from qapi import *
 
 def _generate_event_api_name(event_name, params):
@@ -35,30 +34,22 @@ def _generate_event_api_name(event_name, params):
 # Following are the core functions that generate C APIs to emit event.
 
 def generate_event_declaration(api_name):
-    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
-    name = match.group(1)
     return mcgen('''
 
-// decl %(name)s
 %(api_name)s;
 ''',
-                 name=name,
                  api_name = api_name)
 
 def generate_event_implement(api_name, event_name, params):
-    match = re.match(r'void ([A-Za-z0-9_]+)', api_name)
-    name = match.group(1)
     # step 1: declare any variables
     ret = mcgen("""
 
-// def %(name)s
 %(api_name)s
 {
     QDict *qmp;
     Error *local_err = NULL;
     QMPEventFuncEmit emit;
 """,
-                name=name,
                 api_name = api_name)
 
     if params and params.members:
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 2cce286..5ab3b3b 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -16,7 +16,6 @@ from qapi import *
 def gen_fwd_object_or_array(name):
     return mcgen('''
 
-// typedef %(name)s
 typedef struct %(name)s %(name)s;
 ''',
                  name=c_name(name))
@@ -24,7 +23,6 @@ typedef struct %(name)s %(name)s;
 def gen_array(name, element_type):
     return mcgen('''
 
-// struct %(name)s
 struct %(name)s {
     union {
         %(c_type)s value;
@@ -59,7 +57,6 @@ def generate_struct_fields(members):
 def gen_struct(name, base, members):
     ret = mcgen('''
 
-// struct %(name)s
 struct %(name)s {
 ''',
                 name=c_name(name))
@@ -86,7 +83,6 @@ struct %(name)s {
 def gen_alternate_qtypes_decl(name):
     return mcgen('''
 
-// decl %(c_name)s_qtypes
 extern const int %(c_name)s_qtypes[];
 ''',
                  c_name=c_name(name))
@@ -94,7 +90,6 @@ extern const int %(c_name)s_qtypes[];
 def gen_alternate_qtypes(name, variants):
     ret = mcgen('''
 
-// def %(name)s_qtypes
 const int %(name)s_qtypes[QTYPE_MAX] = {
 ''',
                 name=c_name(name))
@@ -120,7 +115,6 @@ def gen_union(name, base, variants):
 
     ret = mcgen('''
 
-// struct %(name)s
 struct %(name)s {
 ''',
                 name=name)
@@ -172,7 +166,6 @@ struct %(name)s {
 def generate_type_cleanup_decl(name):
     ret = mcgen('''
 
-// decl qapi_free_%(name)s
 void qapi_free_%(name)s(%(name)s *obj);
 ''',
                 name=c_name(name))
@@ -181,7 +174,6 @@ void qapi_free_%(name)s(%(name)s *obj);
 def generate_type_cleanup(name):
     ret = mcgen('''
 
-// def qapi_free_%(name)s
 void qapi_free_%(name)s(%(name)s *obj)
 {
     QapiDeallocVisitor *md;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 5893540..a6ee17f 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -27,14 +27,12 @@ def generate_visit_implicit_struct(type):
         # Need a forward declaration
         ret += mcgen('''
 
-// decl visit_type_%(c_type)s_fields
 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
 ''',
                      c_type=type.c_name())
 
     ret += mcgen('''
 
-// def visit_type_implicit_%(c_type)s
 static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -60,7 +58,6 @@ def generate_visit_struct_fields(name, members, base = None):
 
     ret += mcgen('''
 
-// def visit_type_%(name)s_fields
 static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
 {
     Error *err = NULL;
@@ -143,7 +140,6 @@ def gen_visit_struct(name, base, members):
 
     ret += mcgen('''
 
-// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
 ''',
@@ -159,7 +155,6 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
 def gen_visit_list(name, element_type):
     return mcgen('''
 
-// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -190,7 +185,6 @@ out:
 def generate_visit_enum(name):
     return mcgen('''
 
-// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
 {
     visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
@@ -201,7 +195,6 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
 def gen_visit_alternate(name, variants):
     ret = mcgen('''
 
-// def visit_type_%(name)s
 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -259,7 +252,6 @@ def gen_visit_union(name, base, variants):
 
     ret += mcgen('''
 
-// def visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -341,7 +333,6 @@ def gen_visit_decl(name, scalar=False):
     if not scalar:
         c_type += '*'
     return mcgen('''
-// decl visit_type_%(c_name)s
 void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp);
 ''',
                  c_name=c_name(name), c_type=c_type)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 97d0a8e..641ea9f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1410,7 +1410,6 @@ def guardname(filename):
 def guardstart(name):
     return mcgen('''
 
-// guardstart %(name)s
 #ifndef %(name)s
 #define %(name)s
 
@@ -1420,7 +1419,6 @@ def guardstart(name):
 def guardend(name):
     return mcgen('''
 
-// guardend %(name)s
 #endif /* %(name)s */
 
 ''',
@@ -1429,7 +1427,6 @@ def guardend(name):
 def generate_enum_lookup(name, values):
     ret = mcgen('''
 
-// def %(name)s_lookup
 const char *const %(name)s_lookup[] = {
 ''',
                 name=c_name(name))
@@ -1452,14 +1449,12 @@ def generate_enum(name, values):
     name = c_name(name)
     lookup_decl = mcgen('''
 
-// decl %(name)s_lookup
 extern const char *const %(name)s_lookup[];
 ''',
                 name=name)
 
     enum_decl = mcgen('''
 
-// typedef %(name)s
 typedef enum %(name)s {
 ''',
                 name=name)
@@ -1563,14 +1558,12 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
     fdecl = maybe_open(do_h, h_file, 'w')
 
     fdef.write(mcgen('''
-// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 ''',
                      comment = c_comment))
 
     fdecl.write(mcgen('''
-// prologue
 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
 %(comment)s
 #ifndef %(guard)s
@@ -1583,7 +1576,6 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
 
 def close_output(fdef, fdecl):
     fdecl.write('''
-// epilogue
 #endif
 ''')
     fdecl.close()
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 18/32] qapi: Replace dirty is_c_ptr() by method c_null()
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (16 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 17/32] Revert "qapi: Generate comments to simplify splitting for review" Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 19/32] qapi: Clean up after recent conversions to QAPISchemaVisitor Markus Armbruster
                   ` (14 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

is_c_ptr() looks whether the end of the C text for the type looks like
a pointer.  Works, but is fragile.

We now have a better tool: use QAPISchemaType method c_null().  The
initializers for non-pointers become prettier: 0, false or the
enumeration constant with the value 0 instead of {0}.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-commands.py | 16 +++++-----------
 scripts/qapi.py          |  3 ---
 2 files changed, 5 insertions(+), 14 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index e95fbb8..dd207cd 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -91,18 +91,12 @@ def gen_visitor_input_vars_decl(args):
 bool has_%(argname)s = false;
 ''',
                              argname=c_name(memb.name))
-            if is_c_ptr(memb.type.c_type()):
-                ret += mcgen('''
-%(argtype)s %(argname)s = NULL;
+            ret += mcgen('''
+%(c_type)s %(c_name)s = %(c_null)s;
 ''',
-                             argname=c_name(memb.name),
-                             argtype=memb.type.c_type())
-            else:
-                ret += mcgen('''
-%(argtype)s %(argname)s = {0};
-''',
-                             argname=c_name(memb.name),
-                             argtype=memb.type.c_type())
+                         c_name=c_name(memb.name),
+                         c_type=memb.type.c_type(),
+                         c_null=memb.type.c_null())
 
     pop_indent()
     return ret
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 641ea9f..c6761f9 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1369,9 +1369,6 @@ def c_type(value, is_param=False):
         assert isinstance(value, str) and value != ""
         return c_name(value) + pointer_suffix
 
-def is_c_ptr(value):
-    return value.endswith(pointer_suffix)
-
 def genindent(count):
     ret = ""
     for i in range(count):
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 19/32] qapi: Clean up after recent conversions to QAPISchemaVisitor
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (17 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 18/32] qapi: Replace dirty is_c_ptr() by method c_null() Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 20/32] qapi-visit: Rearrange code a bit Markus Armbruster
                   ` (13 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Generate just 'FOO' instead of 'struct FOO' when possible.

Drop helper functions that are now unused.

Make pylint reasonably happy.

Rename generate_FOO() functions to gen_FOO() for consistency.

Use more consistent and sensible variable names.

Consistently use c_ for mapping keys when their value is a C
identifier or type.

Simplify gen_enum() and gen_visit_union()

Consistently use single quotes for C text string literals.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt   |   2 +-
 scripts/qapi-commands.py | 129 +++++++++++++++++++++++-----------------------
 scripts/qapi-event.py    | 121 ++++++++++++++++++++-----------------------
 scripts/qapi-types.py    |  64 +++++++++++------------
 scripts/qapi-visit.py    | 119 +++++++++++++++++++++---------------------
 scripts/qapi.py          | 131 +++++++++--------------------------------------
 6 files changed, 239 insertions(+), 327 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 7c500b6..e85f113 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -590,7 +590,7 @@ Example:
             UserDefOne *value;
             uint64_t padding;
         };
-        struct UserDefOneList *next;
+        UserDefOneList *next;
     };
 
     void qapi_free_UserDefOneList(UserDefOneList *obj);
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index dd207cd..3758dbd 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -15,20 +15,20 @@
 from qapi import *
 import re
 
-def generate_command_decl(name, args, ret_type):
-    arglist=""
-    if args:
-        for memb in args.members:
-            argtype = memb.type.c_type(is_param=True)
+def gen_command_decl(name, arg_type, ret_type):
+    argstr = ''
+    if arg_type:
+        for memb in arg_type.members:
             if memb.optional:
-                arglist += "bool has_%s, " % c_name(memb.name)
-            arglist += "%s %s, " % (argtype, c_name(memb.name))
+                argstr += 'bool has_%s, ' % c_name(memb.name)
+            argstr += '%s %s, ' % (memb.type.c_type(is_param=True),
+                                   c_name(memb.name))
     return mcgen('''
-%(ret_type)s qmp_%(name)s(%(args)sError **errp);
+%(c_type)s qmp_%(c_name)s(%(args)sError **errp);
 ''',
-                 ret_type=(ret_type and ret_type.c_type()) or 'void',
-                 name=c_name(name),
-                 args=arglist)
+                 c_type=(ret_type and ret_type.c_type()) or 'void',
+                 c_name=c_name(name),
+                 args=argstr)
 
 def gen_err_check(err):
     if not err:
@@ -40,37 +40,40 @@ if (%(err)s) {
 ''',
                  err=err)
 
-def gen_sync_call(name, args, ret_type):
-    ret = ""
-    arglist=""
-    retval=""
-    if ret_type:
-        retval = "retval = "
-    if args:
-        for memb in args.members:
+def gen_call(name, arg_type, ret_type):
+    ret = ''
+
+    argstr = ''
+    if arg_type:
+        for memb in arg_type.members:
             if memb.optional:
-                arglist += "has_%s, " % c_name(memb.name)
-            arglist += "%s, " % c_name(memb.name)
+                argstr += 'has_%s, ' % c_name(memb.name)
+            argstr += '%s, ' % c_name(memb.name)
+
+    lhs = ''
+    if ret_type:
+        lhs = 'retval = '
+
     push_indent()
     ret = mcgen('''
-%(retval)sqmp_%(name)s(%(args)s&local_err);
+%(lhs)sqmp_%(c_name)s(%(args)s&local_err);
 ''',
-                name=c_name(name), args=arglist, retval=retval)
+                c_name=c_name(name), args=argstr, lhs=lhs)
     if ret_type:
         ret += gen_err_check('local_err')
         ret += mcgen('''
 
 qmp_marshal_output_%(c_name)s(retval, ret, &local_err);
 ''',
-                            c_name=c_name(name))
+                     c_name=c_name(name))
     pop_indent()
     return ret
 
-def gen_visitor_input_containers_decl(args):
-    ret = ""
+def gen_visitor_input_containers_decl(arg_type):
+    ret = ''
 
     push_indent()
-    if args:
+    if arg_type:
         ret += mcgen('''
 QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
 QapiDeallocVisitor *md;
@@ -80,17 +83,17 @@ Visitor *v;
 
     return ret
 
-def gen_visitor_input_vars_decl(args):
-    ret = ""
+def gen_visitor_input_vars_decl(arg_type):
+    ret = ''
     push_indent()
 
-    if args:
-        for memb in args.members:
+    if arg_type:
+        for memb in arg_type.members:
             if memb.optional:
                 ret += mcgen('''
-bool has_%(argname)s = false;
+bool has_%(c_name)s = false;
 ''',
-                             argname=c_name(memb.name))
+                             c_name=c_name(memb.name))
             ret += mcgen('''
 %(c_type)s %(c_name)s = %(c_null)s;
 ''',
@@ -101,19 +104,19 @@ bool has_%(argname)s = false;
     pop_indent()
     return ret
 
-def gen_visitor_input_block(args, dealloc=False):
-    ret = ""
+def gen_visitor_input_block(arg_type, dealloc=False):
+    ret = ''
     errparg = '&local_err'
     errarg = 'local_err'
 
-    if not args:
+    if not arg_type:
         return ret
 
     push_indent()
 
     if dealloc:
         errparg = 'NULL'
-        errarg = None;
+        errarg = None
         ret += mcgen('''
 qmp_input_visitor_cleanup(mi);
 md = qapi_dealloc_visitor_new();
@@ -124,7 +127,7 @@ v = qapi_dealloc_get_visitor(md);
 v = qmp_input_get_visitor(mi);
 ''')
 
-    for memb in args.members:
+    for memb in arg_type.members:
         if memb.optional:
             ret += mcgen('''
 visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
@@ -138,10 +141,10 @@ if (has_%(c_name)s) {
                          c_name=c_name(memb.name))
             push_indent()
         ret += mcgen('''
-visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
+visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s);
 ''',
                      c_name=c_name(memb.name), name=memb.name,
-                     visitor=memb.type.c_name(), errp=errparg)
+                     c_type=memb.type.c_name(), errp=errparg)
         ret += gen_err_check(errarg)
         if memb.optional:
             pop_indent()
@@ -158,11 +161,11 @@ qapi_dealloc_visitor_cleanup(md);
 
 def gen_marshal_output(name, ret_type):
     if not ret_type:
-        return ""
+        return ''
 
     ret = mcgen('''
 
-static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
+static void qmp_marshal_output_%(c_cmd_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *local_err = NULL;
     QmpOutputVisitor *mo = qmp_output_visitor_new();
@@ -170,7 +173,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o
     Visitor *v;
 
     v = qmp_output_get_visitor(mo);
-    visit_type_%(visitor)s(v, &ret_in, "unused", &local_err);
+    visit_type_%(c_name)s(v, &ret_in, "unused", &local_err);
     if (local_err) {
         goto out;
     }
@@ -181,23 +184,23 @@ out:
     qmp_output_visitor_cleanup(mo);
     md = qapi_dealloc_visitor_new();
     v = qapi_dealloc_get_visitor(md);
-    visit_type_%(visitor)s(v, &ret_in, "unused", NULL);
+    visit_type_%(c_name)s(v, &ret_in, "unused", NULL);
     qapi_dealloc_visitor_cleanup(md);
 }
 ''',
-                c_ret_type=ret_type.c_type(), c_name=c_name(name),
-                visitor=ret_type.c_name())
+                c_type=ret_type.c_type(), c_cmd_name=c_name(name),
+                c_name=ret_type.c_name())
 
     return ret
 
-def gen_marshal_input_decl(name, middle_mode):
+def gen_marshal_input_decl(name):
     ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
     if not middle_mode:
-        ret = "static " + ret
+        ret = 'static ' + ret
     return ret
 
-def gen_marshal_input(name, args, ret_type, middle_mode):
-    hdr = gen_marshal_input_decl(name, middle_mode)
+def gen_marshal_input(name, arg_type, ret_type):
+    hdr = gen_marshal_input_decl(name)
 
     ret = mcgen('''
 
@@ -213,10 +216,10 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
 ''',
                      c_type=ret_type.c_type())
 
-    if args:
-        ret += gen_visitor_input_containers_decl(args)
-        ret += gen_visitor_input_vars_decl(args) + '\n'
-        ret += gen_visitor_input_block(args) + '\n'
+    if arg_type:
+        ret += gen_visitor_input_containers_decl(arg_type)
+        ret += gen_visitor_input_vars_decl(arg_type) + '\n'
+        ret += gen_visitor_input_block(arg_type) + '\n'
     else:
         ret += mcgen('''
 
@@ -224,9 +227,9 @@ def gen_marshal_input(name, args, ret_type, middle_mode):
 
 ''')
 
-    ret += gen_sync_call(name, args, ret_type)
+    ret += gen_call(name, arg_type, ret_type)
 
-    if re.search('^ *goto out\\;', ret, re.MULTILINE):
+    if re.search('^ *goto out;', ret, re.MULTILINE):
         ret += mcgen('''
 
 out:
@@ -234,7 +237,7 @@ out:
     ret += mcgen('''
     error_propagate(errp, local_err);
 ''')
-    ret += gen_visitor_input_block(args, dealloc=True)
+    ret += gen_visitor_input_block(arg_type, dealloc=True)
     ret += mcgen('''
 }
 ''')
@@ -249,8 +252,8 @@ def gen_register_command(name, success_response):
     ret = mcgen('''
 qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
 ''',
-                     name=name, c_name=c_name(name),
-                     opts=options)
+                name=name, c_name=c_name(name),
+                opts=options)
     pop_indent()
     return ret
 
@@ -285,12 +288,12 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
                       gen, success_response):
         if not gen:
             return
-        self.decl += generate_command_decl(name, arg_type, ret_type)
+        self.decl += gen_command_decl(name, arg_type, ret_type)
         if ret_type:
             self.defn += gen_marshal_output(name, ret_type)
         if middle_mode:
-            self.decl += gen_marshal_input_decl(name, middle_mode) + ';\n'
-        self.defn += gen_marshal_input(name, arg_type, ret_type, middle_mode)
+            self.decl += gen_marshal_input_decl(name) + ';\n'
+        self.defn += gen_marshal_input(name, arg_type, ret_type)
         if not middle_mode:
             self.regy += gen_register_command(name, success_response)
 
@@ -350,7 +353,7 @@ fdef.write(mcgen('''
 #include "%(prefix)sqmp-commands.h"
 
 ''',
-                prefix=prefix))
+                 prefix=prefix))
 
 fdecl.write(mcgen('''
 #include "%(prefix)sqapi-types.h"
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 7525bab..dbcc5a1 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -13,12 +13,12 @@
 
 from qapi import *
 
-def _generate_event_api_name(event_name, params):
-    api_name = "void qapi_event_send_%s(" % c_name(event_name).lower();
+def gen_event_send_proto(name, arg_type):
+    api_name = "void qapi_event_send_%s(" % c_name(name).lower()
     l = len(api_name)
 
-    if params:
-        for m in params.members:
+    if arg_type:
+        for m in arg_type.members:
             if m.optional:
                 api_name += "bool has_%s,\n" % c_name(m.name)
                 api_name += "".ljust(l)
@@ -28,53 +28,47 @@ def _generate_event_api_name(event_name, params):
             api_name += "".ljust(l)
 
     api_name += "Error **errp)"
-    return api_name;
+    return api_name
 
-
-# Following are the core functions that generate C APIs to emit event.
-
-def generate_event_declaration(api_name):
+def gen_event_send_decl(name, arg_type):
     return mcgen('''
 
-%(api_name)s;
+%(proto)s;
 ''',
-                 api_name = api_name)
+                 proto=gen_event_send_proto(name, arg_type))
 
-def generate_event_implement(api_name, event_name, params):
-    # step 1: declare any variables
-    ret = mcgen("""
+def gen_event_send(name, arg_type):
+    ret = mcgen('''
 
-%(api_name)s
+%(proto)s
 {
     QDict *qmp;
     Error *local_err = NULL;
     QMPEventFuncEmit emit;
-""",
-                api_name = api_name)
+''',
+                proto=gen_event_send_proto(name, arg_type))
 
-    if params and params.members:
-        ret += mcgen("""
+    if arg_type and arg_type.members:
+        ret += mcgen('''
     QmpOutputVisitor *qov;
     Visitor *v;
     QObject *obj;
 
-""")
+''')
 
-    # step 2: check emit function, create a dict
-    ret += mcgen("""
+    ret += mcgen('''
     emit = qmp_event_get_func_emit();
     if (!emit) {
         return;
     }
 
-    qmp = qmp_event_build_dict("%(event_name)s");
+    qmp = qmp_event_build_dict("%(name)s");
 
-""",
-                 event_name = event_name)
+''',
+                 name=name)
 
-    # step 3: visit the params if params != None
-    if params and params.members:
-        ret += mcgen("""
+    if arg_type and arg_type.members:
+        ret += mcgen('''
     qov = qmp_output_visitor_new();
     g_assert(qov);
 
@@ -82,45 +76,46 @@ def generate_event_implement(api_name, event_name, params):
     g_assert(v);
 
     /* Fake visit, as if all members are under a structure */
-    visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err);
+    visit_start_struct(v, NULL, "", "%(name)s", 0, &local_err);
     if (local_err) {
         goto clean;
     }
 
-""",
-                event_name = event_name)
+''',
+                     name=name)
 
-        for memb in params.members:
+        for memb in arg_type.members:
             if memb.optional:
-                ret += mcgen("""
-    if (has_%(var)s) {
-""",
-                             var=c_name(memb.name))
+                ret += mcgen('''
+    if (has_%(c_name)s) {
+''',
+                             c_name=c_name(memb.name))
                 push_indent()
 
+            # Ugly: need to cast away the const
             if memb.type.name == "str":
-                var_type = "(char **)"
+                cast = '(char **)'
             else:
-                var_type = ""
+                cast = ''
 
-            ret += mcgen("""
-    visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err);
+            ret += mcgen('''
+    visit_type_%(c_type)s(v, %(cast)s&%(c_name)s, "%(name)s", &local_err);
     if (local_err) {
         goto clean;
     }
-""",
-                         var_type = var_type,
-                         var=c_name(memb.name),
-                         type=memb.type.c_name(),
+''',
+                         cast=cast,
+                         c_name=c_name(memb.name),
+                         c_type=memb.type.c_name(),
                          name=memb.name)
 
             if memb.optional:
                 pop_indent()
-                ret += mcgen("""
+                ret += mcgen('''
     }
-""")
+''')
 
-        ret += mcgen("""
+        ret += mcgen('''
 
     visit_end_struct(v, &local_err);
     if (local_err) {
@@ -131,27 +126,24 @@ def generate_event_implement(api_name, event_name, params):
     g_assert(obj != NULL);
 
     qdict_put_obj(qmp, "data", obj);
-""")
+''')
 
-    # step 4: call qmp event api
-    ret += mcgen("""
-    emit(%(event_enum_value)s, qmp, &local_err);
+    ret += mcgen('''
+    emit(%(c_enum)s, qmp, &local_err);
 
-""",
-                 event_enum_value = c_enum_const(event_enum_name, event_name))
+''',
+                 c_enum=c_enum_const(event_enum_name, name))
 
-    # step 5: clean up
-    if params and params.members:
-        ret += mcgen("""
+    if arg_type and arg_type.members:
+        ret += mcgen('''
  clean:
     qmp_output_visitor_cleanup(qov);
-""")
-    ret += mcgen("""
+''')
+    ret += mcgen('''
     error_propagate(errp, local_err);
     QDECREF(qmp);
 }
-""")
-
+''')
     return ret
 
 class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
@@ -164,13 +156,12 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
         self.defn = ''
         self.event_names = []
     def visit_end(self):
-        self.decl += generate_enum(event_enum_name, self.event_names)
-        self.defn += generate_enum_lookup(event_enum_name, self.event_names)
+        self.decl += gen_enum(event_enum_name, self.event_names)
+        self.defn += gen_enum_lookup(event_enum_name, self.event_names)
         self.event_names = None
     def visit_event(self, name, info, arg_type):
-        api_name = _generate_event_api_name(name, arg_type)
-        self.decl += generate_event_declaration(api_name)
-        self.defn += generate_event_implement(api_name, name, arg_type)
+        self.decl += gen_event_send_decl(name, arg_type)
+        self.defn += gen_event_send(name, arg_type)
         self.event_names.append(name)
 
 (input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 5ab3b3b..b5ebeee 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -16,22 +16,22 @@ from qapi import *
 def gen_fwd_object_or_array(name):
     return mcgen('''
 
-typedef struct %(name)s %(name)s;
+typedef struct %(c_name)s %(c_name)s;
 ''',
-                 name=c_name(name))
+                 c_name=c_name(name))
 
 def gen_array(name, element_type):
     return mcgen('''
 
-struct %(name)s {
+struct %(c_name)s {
     union {
         %(c_type)s value;
         uint64_t padding;
     };
-    struct %(name)s *next;
+    %(c_name)s *next;
 };
 ''',
-                 name=c_name(name), c_type=element_type.c_type())
+                 c_name=c_name(name), c_type=element_type.c_type())
 
 def gen_struct_field(name, typ, optional):
     ret = ''
@@ -47,7 +47,7 @@ def gen_struct_field(name, typ, optional):
                  c_type=typ.c_type(), c_name=c_name(name))
     return ret
 
-def generate_struct_fields(members):
+def gen_struct_fields(members):
     ret = ''
 
     for memb in members:
@@ -57,20 +57,20 @@ def generate_struct_fields(members):
 def gen_struct(name, base, members):
     ret = mcgen('''
 
-struct %(name)s {
+struct %(c_name)s {
 ''',
-                name=c_name(name))
+                c_name=c_name(name))
 
     if base:
         ret += gen_struct_field('base', base, False)
 
-    ret += generate_struct_fields(members)
+    ret += gen_struct_fields(members)
 
     # Make sure that all structs have at least one field; this avoids
     # potential issues with attempting to malloc space for zero-length structs
     # in C, and also incompatibility with C++ (where an empty struct is size 1).
     if not base and not members:
-            ret += mcgen('''
+        ret += mcgen('''
     char qapi_dummy_field_for_empty_struct;
 ''')
 
@@ -90,9 +90,9 @@ extern const int %(c_name)s_qtypes[];
 def gen_alternate_qtypes(name, variants):
     ret = mcgen('''
 
-const int %(name)s_qtypes[QTYPE_MAX] = {
+const int %(c_name)s_qtypes[QTYPE_MAX] = {
 ''',
-                name=c_name(name))
+                c_name=c_name(name))
 
     for var in variants.variants:
         qtype = var.type.alternate_qtype()
@@ -101,7 +101,7 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
         ret += mcgen('''
     [%(qtype)s] = %(enum_const)s,
 ''',
-                     qtype = qtype,
+                     qtype=qtype,
                      enum_const=c_enum_const(variants.tag_member.type.name,
                                              var.name))
 
@@ -111,27 +111,25 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
     return ret
 
 def gen_union(name, base, variants):
-    name = c_name(name)
-
     ret = mcgen('''
 
-struct %(name)s {
+struct %(c_name)s {
 ''',
-                name=name)
+                c_name=c_name(name))
     if base:
         ret += mcgen('''
     /* Members inherited from %(c_name)s: */
 ''',
                      c_name=c_name(base.name))
-        ret += generate_struct_fields(base.members)
+        ret += gen_struct_fields(base.members)
         ret += mcgen('''
     /* Own members: */
 ''')
     else:
         ret += mcgen('''
-    %(discriminator_type_name)s kind;
+    %(c_type)s kind;
 ''',
-                     discriminator_type_name=c_name(variants.tag_member.type.name))
+                     c_type=c_name(variants.tag_member.type.name))
 
     # FIXME: What purpose does data serve, besides preventing a union that
     # has a branch named 'data'? We use it in qapi-visit.py to decide
@@ -163,18 +161,18 @@ struct %(name)s {
 
     return ret
 
-def generate_type_cleanup_decl(name):
+def gen_type_cleanup_decl(name):
     ret = mcgen('''
 
-void qapi_free_%(name)s(%(name)s *obj);
+void qapi_free_%(c_name)s(%(c_name)s *obj);
 ''',
-                name=c_name(name))
+                c_name=c_name(name))
     return ret
 
-def generate_type_cleanup(name):
+def gen_type_cleanup(name):
     ret = mcgen('''
 
-void qapi_free_%(name)s(%(name)s *obj)
+void qapi_free_%(c_name)s(%(c_name)s *obj)
 {
     QapiDeallocVisitor *md;
     Visitor *v;
@@ -185,11 +183,11 @@ void qapi_free_%(name)s(%(name)s *obj)
 
     md = qapi_dealloc_visitor_new();
     v = qapi_dealloc_get_visitor(md);
-    visit_type_%(name)s(v, &obj, NULL, NULL);
+    visit_type_%(c_name)s(v, &obj, NULL, NULL);
     qapi_dealloc_visitor_cleanup(md);
 }
 ''',
-                name=c_name(name))
+                c_name=c_name(name))
     return ret
 
 class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
@@ -218,18 +216,18 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
         self.decl = self.btin + self.decl
         self.btin = None
     def _gen_type_cleanup(self, name):
-        self.decl += generate_type_cleanup_decl(name)
-        self.defn += generate_type_cleanup(name)
+        self.decl += gen_type_cleanup_decl(name)
+        self.defn += gen_type_cleanup(name)
     def visit_enum_type(self, name, info, values):
-        self.fwdecl += generate_enum(name, values)
-        self.fwdefn += generate_enum_lookup(name, values)
+        self.fwdecl += gen_enum(name, values)
+        self.fwdefn += gen_enum_lookup(name, values)
     def visit_array_type(self, name, info, element_type):
         if isinstance(element_type, QAPISchemaBuiltinType):
             self.btin += gen_fwd_object_or_array(name)
             self.btin += gen_array(name, element_type)
-            self.btin += generate_type_cleanup_decl(name)
+            self.btin += gen_type_cleanup_decl(name)
             if do_builtins:
-                self.defn += generate_type_cleanup(name)
+                self.defn += gen_type_cleanup(name)
         else:
             self.fwdecl += gen_fwd_object_or_array(name)
             self.decl += gen_array(name, element_type)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index a6ee17f..08504ff 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -18,18 +18,19 @@ import re
 implicit_structs_seen = set()
 struct_fields_seen = set()
 
-def generate_visit_implicit_struct(type):
-    if type in implicit_structs_seen:
+def gen_visit_implicit_struct(typ):
+    if typ in implicit_structs_seen:
         return ''
-    implicit_structs_seen.add(type)
+    implicit_structs_seen.add(typ)
+
     ret = ''
-    if type.name not in struct_fields_seen:
+    if typ.name not in struct_fields_seen:
         # Need a forward declaration
         ret += mcgen('''
 
 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
 ''',
-                     c_type=type.c_name())
+                     c_type=typ.c_name())
 
     ret += mcgen('''
 
@@ -45,35 +46,35 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
     error_propagate(errp, err);
 }
 ''',
-                 c_type=type.c_name())
+                 c_type=typ.c_name())
     return ret
 
-def generate_visit_struct_fields(name, members, base = None):
+def gen_visit_struct_fields(name, base, members):
     struct_fields_seen.add(name)
 
     ret = ''
 
     if base:
-        ret += generate_visit_implicit_struct(base)
+        ret += gen_visit_implicit_struct(base)
 
     ret += mcgen('''
 
-static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
+static void visit_type_%(c_name)s_fields(Visitor *m, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;
 
 ''',
-                 name=c_name(name))
+                 c_name=c_name(name))
     push_indent()
 
     if base:
         ret += mcgen('''
-visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
+visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);
 if (err) {
     goto out;
 }
 ''',
-                     type=base.c_name(), c_name=c_name('base'))
+                     c_type=base.c_name(), c_name=c_name('base'))
 
     for memb in members:
         if memb.optional:
@@ -85,9 +86,9 @@ if (!err && (*obj)->has_%(c_name)s) {
             push_indent()
 
         ret += mcgen('''
-visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
+visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
 ''',
-                     type=memb.type.c_name(), c_name=c_name(memb.name),
+                     c_type=memb.type.c_name(), c_name=c_name(memb.name),
                      name=memb.name)
 
         if memb.optional:
@@ -102,7 +103,7 @@ if (err) {
 ''')
 
     pop_indent()
-    if re.search('^ *goto out\\;', ret, re.MULTILINE):
+    if re.search('^ *goto out;', ret, re.MULTILINE):
         ret += mcgen('''
 
 out:
@@ -113,8 +114,7 @@ out:
 ''')
     return ret
 
-
-def generate_visit_struct_body(name):
+def gen_visit_struct_body(name):
     # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
     # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
     # rather than leaving it non-NULL. As currently written, the caller must
@@ -132,20 +132,18 @@ def generate_visit_struct_body(name):
     error_propagate(errp, err);
 ''',
                 name=name, c_name=c_name(name))
-
     return ret
 
 def gen_visit_struct(name, base, members):
-    ret = generate_visit_struct_fields(name, members, base)
-
+    ret = gen_visit_struct_fields(name, base, members)
     ret += mcgen('''
 
-void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
 {
 ''',
-                 name=c_name(name))
+                 c_name=c_name(name))
 
-    ret += generate_visit_struct_body(name)
+    ret += gen_visit_struct_body(name)
 
     ret += mcgen('''
 }
@@ -155,7 +153,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
 def gen_visit_list(name, element_type):
     return mcgen('''
 
-void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
     GenericList *i, **prev;
@@ -168,7 +166,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
     for (prev = (GenericList **)obj;
          !err && (i = visit_next_list(m, prev, &err)) != NULL;
          prev = &i) {
-        %(name)s *native_i = (%(name)s *)i;
+        %(c_name)s *native_i = (%(c_name)s *)i;
         visit_type_%(c_elt_type)s(m, &native_i->value, NULL, &err);
     }
 
@@ -179,10 +177,9 @@ out:
     error_propagate(errp, err);
 }
 ''',
-                 name=c_name(name),
-                 c_elt_type=element_type.c_name())
+                 c_name=c_name(name), c_elt_type=element_type.c_name())
 
-def generate_visit_enum(name):
+def gen_visit_enum(name):
     return mcgen('''
 
 void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
@@ -195,33 +192,32 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
 def gen_visit_alternate(name, variants):
     ret = mcgen('''
 
-void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
 
-    visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
+    visit_start_implicit_struct(m, (void**) obj, sizeof(%(c_name)s), &err);
     if (err) {
         goto out;
     }
-    visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
+    visit_get_next_type(m, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
     if (err) {
         goto out_end;
     }
     switch ((*obj)->kind) {
 ''',
-                name=c_name(name))
+                c_name=c_name(name))
 
     for var in variants.variants:
-        enum_full_value = c_enum_const(variants.tag_member.type.name,
-                                       var.name)
         ret += mcgen('''
-    case %(enum_full_value)s:
+    case %(case)s:
         visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
         break;
 ''',
-                enum_full_value = enum_full_value,
-                c_type=var.type.c_name(),
-                c_name=c_name(var.name))
+                     case=c_enum_const(variants.tag_member.type.name,
+                                       var.name),
+                     c_type=var.type.c_name(),
+                     c_name=c_name(var.name))
 
     ret += mcgen('''
     default:
@@ -243,12 +239,12 @@ def gen_visit_union(name, base, variants):
 
     if base:
         members = [m for m in base.members if m != variants.tag_member]
-        ret += generate_visit_struct_fields(name, members)
+        ret += gen_visit_struct_fields(name, None, members)
 
     for var in variants.variants:
         # TODO ugly special case for simple union
         if not var.simple_union_type():
-            ret += generate_visit_implicit_struct(var.type)
+            ret += gen_visit_implicit_struct(var.type)
 
     ret += mcgen('''
 
@@ -266,19 +262,19 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
 
     if base:
         ret += mcgen('''
-        visit_type_%(name)s_fields(m, obj, &err);
+        visit_type_%(c_name)s_fields(m, obj, &err);
         if (err) {
             goto out_obj;
         }
 ''',
-                     name=c_name(name))
+                     c_name=c_name(name))
 
-    disc_key = variants.tag_member.name
+    tag_key = variants.tag_member.name
     if not variants.tag_name:
         # we pointlessly use a different key for simple unions
-        disc_key = 'type'
+        tag_key = 'type'
     ret += mcgen('''
-        visit_type_%(disc_type)s(m, &(*obj)->%(c_name)s, "%(disc_key)s", &err);
+        visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
         if (err) {
             goto out_obj;
         }
@@ -287,27 +283,32 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
         }
         switch ((*obj)->%(c_name)s) {
 ''',
-                 disc_type=variants.tag_member.type.c_name(),
+                 c_type=variants.tag_member.type.c_name(),
                  c_name=c_name(variants.tag_member.name),
-                 disc_key = disc_key)
+                 name=tag_key)
 
     for var in variants.variants:
         # TODO ugly special case for simple union
         simple_union_type = var.simple_union_type()
+        ret += mcgen('''
+        case %(case)s:
+''',
+                     case=c_enum_const(variants.tag_member.type.name, var.name))
         if simple_union_type:
-            fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
+            ret += mcgen('''
+            visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);
+''',
+                         c_type=simple_union_type.c_name(),
+                         c_name=c_name(var.name))
         else:
-            fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
-
-        enum_full_value = c_enum_const(variants.tag_member.type.name, var.name)
+            ret += mcgen('''
+            visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);
+''',
+                         c_type=var.type.c_name(),
+                         c_name=c_name(var.name))
         ret += mcgen('''
-        case %(enum_full_value)s:
-            ''' + fmt + '''
             break;
-''',
-                enum_full_value = enum_full_value,
-                c_type=(simple_union_type or var.type).c_name(),
-                c_name=c_name(var.name))
+''')
 
     ret += mcgen('''
         default:
@@ -356,7 +357,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
         self.btin = None
     def visit_enum_type(self, name, info, values):
         self.decl += gen_visit_decl(name, scalar=True)
-        self.defn += generate_visit_enum(name)
+        self.defn += gen_visit_enum(name)
     def visit_array_type(self, name, info, element_type):
         decl = gen_visit_decl(name)
         defn = gen_visit_list(name, element_type)
@@ -429,7 +430,7 @@ fdef.write(mcgen('''
 #include "qemu-common.h"
 #include "%(prefix)sqapi-visit.h"
 ''',
-                 prefix = prefix))
+                 prefix=prefix))
 
 fdecl.write(mcgen('''
 #include "qapi/visitor.h"
diff --git a/scripts/qapi.py b/scripts/qapi.py
index c6761f9..d314b13 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1021,9 +1021,6 @@ class QAPISchema(object):
         self._def_exprs()
         self.check()
 
-    def get_exprs(self):
-        return [expr_elem['expr'] for expr_elem in self.exprs]
-
     def _def_entity(self, ent):
         assert ent.name not in self.entity_dict
         self.entity_dict[ent.name] = ent
@@ -1211,23 +1208,6 @@ class QAPISchema(object):
 # Code generation helpers
 #
 
-def parse_args(typeinfo):
-    if isinstance(typeinfo, str):
-        struct = find_struct(typeinfo)
-        assert struct != None
-        typeinfo = struct['data']
-
-    for member in typeinfo:
-        argname = member
-        argentry = typeinfo[member]
-        optional = False
-        if member.startswith('*'):
-            argname = member[1:]
-            optional = True
-        # Todo: allow argentry to be OrderedDict, for providing the
-        # value of an optional argument.
-        yield (argname, argentry, optional)
-
 def camel_case(name):
     new_name = ''
     first = True
@@ -1308,67 +1288,9 @@ def c_name(name, protect=True):
         return "q_" + name
     return name.translate(c_name_trans)
 
-# Map type @name to the C typedef name for the list form.
-#
-# ['Name'] -> 'NameList', ['x-Foo'] -> 'x_FooList', ['int'] -> 'intList'
-def c_list_type(name):
-    return type_name(name) + 'List'
-
-# Map type @value to the C typedef form.
-#
-# Used for converting 'type' from a 'member':'type' qapi definition
-# into the alphanumeric portion of the type for a generated C parameter,
-# as well as generated C function names.  See c_type() for the rest of
-# the conversion such as adding '*' on pointer types.
-# 'int' -> 'int', '[x-Foo]' -> 'x_FooList', '__a.b_c' -> '__a_b_c'
-def type_name(value):
-    if type(value) == list:
-        return c_list_type(value[0])
-    if value in builtin_types.keys():
-        return value
-    return c_name(value)
-
 eatspace = '\033EATSPACE.'
 pointer_suffix = ' *' + eatspace
 
-# Map type @name to its C type expression.
-# If @is_param, const-qualify the string type.
-#
-# This function is used for computing the full C type of 'member':'name'.
-# A special suffix is added in c_type() for pointer types, and it's
-# stripped in mcgen(). So please notice this when you check the return
-# value of c_type() outside mcgen().
-def c_type(value, is_param=False):
-    if value == 'str':
-        if is_param:
-            return 'const char' + pointer_suffix
-        return 'char' + pointer_suffix
-
-    elif value == 'int':
-        return 'int64_t'
-    elif (value == 'int8' or value == 'int16' or value == 'int32' or
-          value == 'int64' or value == 'uint8' or value == 'uint16' or
-          value == 'uint32' or value == 'uint64'):
-        return value + '_t'
-    elif value == 'size':
-        return 'uint64_t'
-    elif value == 'bool':
-        return 'bool'
-    elif value == 'number':
-        return 'double'
-    elif type(value) == list:
-        return c_list_type(value[0]) + pointer_suffix
-    elif is_enum(value):
-        return c_name(value)
-    elif value == None:
-        return 'void'
-    elif value in events:
-        return camel_case(value) + 'Event' + pointer_suffix
-    else:
-        # complex type name
-        assert isinstance(value, str) and value != ""
-        return c_name(value) + pointer_suffix
-
 def genindent(count):
     ret = ""
     for i in range(count):
@@ -1421,60 +1343,57 @@ def guardend(name):
 ''',
                  name=guardname(name))
 
-def generate_enum_lookup(name, values):
+def gen_enum_lookup(name, values):
     ret = mcgen('''
 
-const char *const %(name)s_lookup[] = {
+const char *const %(c_name)s_lookup[] = {
 ''',
-                name=c_name(name))
+                c_name=c_name(name))
     for value in values:
         index = c_enum_const(name, value)
         ret += mcgen('''
     [%(index)s] = "%(value)s",
 ''',
-                     index = index, value = value)
+                     index=index, value=value)
 
     max_index = c_enum_const(name, 'MAX')
     ret += mcgen('''
     [%(max_index)s] = NULL,
 };
 ''',
-        max_index=max_index)
+                 max_index=max_index)
     return ret
 
-def generate_enum(name, values):
-    name = c_name(name)
-    lookup_decl = mcgen('''
-
-extern const char *const %(name)s_lookup[];
-''',
-                name=name)
-
-    enum_decl = mcgen('''
-
-typedef enum %(name)s {
-''',
-                name=name)
-
+def gen_enum(name, values):
     # append automatically generated _MAX value
-    enum_values = values + [ 'MAX' ]
+    enum_values = values + ['MAX']
+
+    ret = mcgen('''
+
+typedef enum %(c_name)s {
+''',
+                c_name=c_name(name))
 
     i = 0
     for value in enum_values:
-        enum_full_value = c_enum_const(name, value)
-        enum_decl += mcgen('''
-    %(enum_full_value)s = %(i)d,
+        ret += mcgen('''
+    %(c_enum)s = %(i)d,
 ''',
-                     enum_full_value = enum_full_value,
+                     c_enum=c_enum_const(name, value),
                      i=i)
         i += 1
 
-    enum_decl += mcgen('''
-} %(name)s;
+    ret += mcgen('''
+} %(c_name)s;
 ''',
-                 name=name)
+                 c_name=c_name(name))
 
-    return enum_decl + lookup_decl
+    ret += mcgen('''
+
+extern const char *const %(c_name)s_lookup[];
+''',
+                 c_name=c_name(name))
+    return ret
 
 #
 # Common command line parsing
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 20/32] qapi-visit: Rearrange code a bit
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (18 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 19/32] qapi: Clean up after recent conversions to QAPISchemaVisitor Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 21/32] qapi-commands: Rearrange code Markus Armbruster
                   ` (12 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Move gen_visit_decl() to a better place.  Inline
generate_visit_struct_body().

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-visit.py | 43 +++++++++++++++++--------------------------
 1 file changed, 17 insertions(+), 26 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 08504ff..6819866 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -18,6 +18,15 @@ import re
 implicit_structs_seen = set()
 struct_fields_seen = set()
 
+def gen_visit_decl(name, scalar=False):
+    c_type = c_name(name) + ' *'
+    if not scalar:
+        c_type += '*'
+    return mcgen('''
+void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp);
+''',
+                 c_name=c_name(name), c_type=c_type)
+
 def gen_visit_implicit_struct(typ):
     if typ in implicit_structs_seen:
         return ''
@@ -114,12 +123,17 @@ out:
 ''')
     return ret
 
-def gen_visit_struct_body(name):
+def gen_visit_struct(name, base, members):
+    ret = gen_visit_struct_fields(name, base, members)
+
     # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
     # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
     # rather than leaving it non-NULL. As currently written, the caller must
     # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
-    ret = mcgen('''
+    ret += mcgen('''
+
+void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
+{
     Error *err = NULL;
 
     visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
@@ -130,24 +144,10 @@ def gen_visit_struct_body(name):
         visit_end_struct(m, &err);
     }
     error_propagate(errp, err);
+}
 ''',
                 name=name, c_name=c_name(name))
-    return ret
 
-def gen_visit_struct(name, base, members):
-    ret = gen_visit_struct_fields(name, base, members)
-    ret += mcgen('''
-
-void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
-{
-''',
-                 c_name=c_name(name))
-
-    ret += gen_visit_struct_body(name)
-
-    ret += mcgen('''
-}
-''')
     return ret
 
 def gen_visit_list(name, element_type):
@@ -329,15 +329,6 @@ out:
 
     return ret
 
-def gen_visit_decl(name, scalar=False):
-    c_type = c_name(name) + ' *'
-    if not scalar:
-        c_type += '*'
-    return mcgen('''
-void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp);
-''',
-                 c_name=c_name(name), c_type=c_type)
-
 class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
     def __init__(self):
         self.decl = None
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 21/32] qapi-commands: Rearrange code
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (19 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 20/32] qapi-visit: Rearrange code a bit Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 22/32] qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO() Markus Armbruster
                   ` (11 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Rename gen_marshal_input() to gen_marshal(), because the generated
function marshals both arguments and results.

Rename gen_visitor_input_containers_decl() to gen_marshal_vars(), and
move the other variable declarations there, too.

Rename gen_visitor_input_block() to gen_marshal_input_visit(), and
rearrange its code slightly.

Rename gen_marshal_input_decl() to gen_marshal_proto(), because the
result isn't a full declaration, unlike gen_command_decl()'s.

New gen_marshal_decl() actually returns a full declaration.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-commands.py | 85 ++++++++++++++++++++++--------------------------
 1 file changed, 38 insertions(+), 47 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 3758dbd..7db30e5 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -56,6 +56,7 @@ def gen_call(name, arg_type, ret_type):
 
     push_indent()
     ret = mcgen('''
+
 %(lhs)sqmp_%(c_name)s(%(args)s&local_err);
 ''',
                 c_name=c_name(name), args=argstr, lhs=lhs)
@@ -69,25 +70,26 @@ qmp_marshal_output_%(c_name)s(retval, ret, &local_err);
     pop_indent()
     return ret
 
-def gen_visitor_input_containers_decl(arg_type):
-    ret = ''
+def gen_marshal_vars(arg_type, ret_type):
+    ret = mcgen('''
+    Error *local_err = NULL;
+''')
 
     push_indent()
+
+    if ret_type:
+        ret += mcgen('''
+%(c_type)s retval;
+''',
+                     c_type=ret_type.c_type())
+
     if arg_type:
         ret += mcgen('''
 QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
 QapiDeallocVisitor *md;
 Visitor *v;
 ''')
-    pop_indent()
 
-    return ret
-
-def gen_visitor_input_vars_decl(arg_type):
-    ret = ''
-    push_indent()
-
-    if arg_type:
         for memb in arg_type.members:
             if memb.optional:
                 ret += mcgen('''
@@ -100,14 +102,18 @@ bool has_%(c_name)s = false;
                          c_name=c_name(memb.name),
                          c_type=memb.type.c_type(),
                          c_null=memb.type.c_null())
+        ret += '\n'
+    else:
+        ret += mcgen('''
+
+(void)args;
+''')
 
     pop_indent()
     return ret
 
-def gen_visitor_input_block(arg_type, dealloc=False):
+def gen_marshal_input_visit(arg_type, dealloc=False):
     ret = ''
-    errparg = '&local_err'
-    errarg = 'local_err'
 
     if not arg_type:
         return ret
@@ -123,6 +129,8 @@ md = qapi_dealloc_visitor_new();
 v = qapi_dealloc_get_visitor(md);
 ''')
     else:
+        errparg = '&local_err'
+        errarg = 'local_err'
         ret += mcgen('''
 v = qmp_input_get_visitor(mi);
 ''')
@@ -160,10 +168,7 @@ qapi_dealloc_visitor_cleanup(md);
     return ret
 
 def gen_marshal_output(name, ret_type):
-    if not ret_type:
-        return ''
-
-    ret = mcgen('''
+    return mcgen('''
 
 static void qmp_marshal_output_%(c_cmd_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
 {
@@ -188,45 +193,31 @@ out:
     qapi_dealloc_visitor_cleanup(md);
 }
 ''',
-                c_type=ret_type.c_type(), c_cmd_name=c_name(name),
-                c_name=ret_type.c_name())
+                 c_type=ret_type.c_type(), c_cmd_name=c_name(name),
+                 c_name=ret_type.c_name())
 
-    return ret
-
-def gen_marshal_input_decl(name):
+def gen_marshal_proto(name):
     ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
     if not middle_mode:
         ret = 'static ' + ret
     return ret
 
-def gen_marshal_input(name, arg_type, ret_type):
-    hdr = gen_marshal_input_decl(name)
+def gen_marshal_decl(name):
+    return mcgen('''
+%(proto)s;
+''',
+                 proto=gen_marshal_proto(name))
 
+def gen_marshal(name, arg_type, ret_type):
     ret = mcgen('''
 
-%(header)s
+%(proto)s
 {
-    Error *local_err = NULL;
 ''',
-                header=hdr)
-
-    if ret_type:
-        ret += mcgen('''
-    %(c_type)s retval;
-''',
-                     c_type=ret_type.c_type())
-
-    if arg_type:
-        ret += gen_visitor_input_containers_decl(arg_type)
-        ret += gen_visitor_input_vars_decl(arg_type) + '\n'
-        ret += gen_visitor_input_block(arg_type) + '\n'
-    else:
-        ret += mcgen('''
-
-    (void)args;
-
-''')
+                proto=gen_marshal_proto(name))
 
+    ret += gen_marshal_vars(arg_type, ret_type)
+    ret += gen_marshal_input_visit(arg_type)
     ret += gen_call(name, arg_type, ret_type)
 
     if re.search('^ *goto out;', ret, re.MULTILINE):
@@ -237,7 +228,7 @@ out:
     ret += mcgen('''
     error_propagate(errp, local_err);
 ''')
-    ret += gen_visitor_input_block(arg_type, dealloc=True)
+    ret += gen_marshal_input_visit(arg_type, dealloc=True)
     ret += mcgen('''
 }
 ''')
@@ -292,8 +283,8 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         if ret_type:
             self.defn += gen_marshal_output(name, ret_type)
         if middle_mode:
-            self.decl += gen_marshal_input_decl(name) + ';\n'
-        self.defn += gen_marshal_input(name, arg_type, ret_type)
+            self.decl += gen_marshal_decl(name)
+        self.defn += gen_marshal(name, arg_type, ret_type)
         if not middle_mode:
             self.regy += gen_register_command(name, success_response)
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 22/32] qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO()
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (20 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 21/32] qapi-commands: Rearrange code Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 23/32] qapi: De-duplicate parameter list generation Markus Armbruster
                   ` (10 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

These functions marshal both input and output.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt        |   4 +-
 docs/writing-qmp-commands.txt |   8 +-
 monitor.c                     |   2 +-
 qmp-commands.hx               | 240 +++++++++++++++++++++---------------------
 qmp.c                         |   6 +-
 scripts/qapi-commands.py      |   4 +-
 6 files changed, 132 insertions(+), 132 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index e85f113..09d15dc 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -736,7 +736,7 @@ Example:
         qapi_dealloc_visitor_cleanup(md);
     }
 
-    static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp)
+    static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp)
     {
         Error *local_err = NULL;
         UserDefOne *retval;
@@ -769,7 +769,7 @@ Example:
 
     static void qmp_init_marshal(void)
     {
-        qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS);
+        qmp_register_command("my-command", qmp_marshal_my_command, QCO_NO_OPTIONS);
     }
 
     qapi_init(qmp_init_marshal);
diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt
index ab1fdd3..c65bdc6 100644
--- a/docs/writing-qmp-commands.txt
+++ b/docs/writing-qmp-commands.txt
@@ -127,7 +127,7 @@ following in the botton:
     {
         .name       = "hello-world",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_hello_world,
+        .mhandler.cmd_new = qmp_marshal_hello_world,
     },
 
 You're done. Now build qemu, run it as suggested in the "Testing" section,
@@ -179,7 +179,7 @@ The last step is to update the qmp-commands.hx file:
     {
         .name       = "hello-world",
         .args_type  = "message:s?",
-        .mhandler.cmd_new = qmp_marshal_input_hello_world,
+        .mhandler.cmd_new = qmp_marshal_hello_world,
     },
 
 Notice that the "args_type" member got our "message" argument. The character
@@ -461,7 +461,7 @@ The last step is to add the correspoding entry in the qmp-commands.hx file:
     {
         .name       = "query-alarm-clock",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_alarm_clock,
+        .mhandler.cmd_new = qmp_marshal_query_alarm_clock,
     },
 
 Time to test the new command. Build qemu, run it as described in the "Testing"
@@ -607,7 +607,7 @@ To test this you have to add the corresponding qmp-commands.hx entry:
     {
         .name       = "query-alarm-methods",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_alarm_methods,
+        .mhandler.cmd_new = qmp_marshal_query_alarm_methods,
     },
 
 Now Build qemu, run it as explained in the "Testing" section and try our new
diff --git a/monitor.c b/monitor.c
index 641cfae6..f654607 100644
--- a/monitor.c
+++ b/monitor.c
@@ -5109,7 +5109,7 @@ static QObject *get_qmp_greeting(void)
 {
     QObject *ver = NULL;
 
-    qmp_marshal_input_query_version(NULL, &ver, NULL);
+    qmp_marshal_query_version(NULL, &ver, NULL);
     return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver);
 }
 
diff --git a/qmp-commands.hx b/qmp-commands.hx
index ba630b1..41a39fc 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -63,7 +63,7 @@ EQMP
     {
         .name       = "quit",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_quit,
+        .mhandler.cmd_new = qmp_marshal_quit,
     },
 
 SQMP
@@ -84,7 +84,7 @@ EQMP
     {
         .name       = "eject",
         .args_type  = "force:-f,device:B",
-        .mhandler.cmd_new = qmp_marshal_input_eject,
+        .mhandler.cmd_new = qmp_marshal_eject,
     },
 
 SQMP
@@ -110,7 +110,7 @@ EQMP
     {
         .name       = "change",
         .args_type  = "device:B,target:F,arg:s?",
-        .mhandler.cmd_new = qmp_marshal_input_change,
+        .mhandler.cmd_new = qmp_marshal_change,
     },
 
 SQMP
@@ -146,7 +146,7 @@ EQMP
     {
         .name       = "screendump",
         .args_type  = "filename:F",
-        .mhandler.cmd_new = qmp_marshal_input_screendump,
+        .mhandler.cmd_new = qmp_marshal_screendump,
     },
 
 SQMP
@@ -169,7 +169,7 @@ EQMP
     {
         .name       = "stop",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_stop,
+        .mhandler.cmd_new = qmp_marshal_stop,
     },
 
 SQMP
@@ -190,7 +190,7 @@ EQMP
     {
         .name       = "cont",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_cont,
+        .mhandler.cmd_new = qmp_marshal_cont,
     },
 
 SQMP
@@ -211,7 +211,7 @@ EQMP
     {
         .name       = "system_wakeup",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_system_wakeup,
+        .mhandler.cmd_new = qmp_marshal_system_wakeup,
     },
 
 SQMP
@@ -232,7 +232,7 @@ EQMP
     {
         .name       = "system_reset",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_system_reset,
+        .mhandler.cmd_new = qmp_marshal_system_reset,
     },
 
 SQMP
@@ -253,7 +253,7 @@ EQMP
     {
         .name       = "system_powerdown",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_system_powerdown,
+        .mhandler.cmd_new = qmp_marshal_system_powerdown,
     },
 
 SQMP
@@ -310,7 +310,7 @@ EQMP
     {
         .name       = "device_del",
         .args_type  = "id:s",
-        .mhandler.cmd_new = qmp_marshal_input_device_del,
+        .mhandler.cmd_new = qmp_marshal_device_del,
     },
 
 SQMP
@@ -333,7 +333,7 @@ EQMP
     {
         .name       = "send-key",
         .args_type  = "keys:q,hold-time:i?",
-        .mhandler.cmd_new = qmp_marshal_input_send_key,
+        .mhandler.cmd_new = qmp_marshal_send_key,
     },
 
 SQMP
@@ -364,7 +364,7 @@ EQMP
     {
         .name       = "cpu",
         .args_type  = "index:i",
-        .mhandler.cmd_new = qmp_marshal_input_cpu,
+        .mhandler.cmd_new = qmp_marshal_cpu,
     },
 
 SQMP
@@ -389,7 +389,7 @@ EQMP
     {
         .name       = "cpu-add",
         .args_type  = "id:i",
-        .mhandler.cmd_new = qmp_marshal_input_cpu_add,
+        .mhandler.cmd_new = qmp_marshal_cpu_add,
     },
 
 SQMP
@@ -412,7 +412,7 @@ EQMP
     {
         .name       = "memsave",
         .args_type  = "val:l,size:i,filename:s,cpu:i?",
-        .mhandler.cmd_new = qmp_marshal_input_memsave,
+        .mhandler.cmd_new = qmp_marshal_memsave,
     },
 
 SQMP
@@ -441,7 +441,7 @@ EQMP
     {
         .name       = "pmemsave",
         .args_type  = "val:l,size:i,filename:s",
-        .mhandler.cmd_new = qmp_marshal_input_pmemsave,
+        .mhandler.cmd_new = qmp_marshal_pmemsave,
     },
 
 SQMP
@@ -469,7 +469,7 @@ EQMP
     {
         .name       = "inject-nmi",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_inject_nmi,
+        .mhandler.cmd_new = qmp_marshal_inject_nmi,
     },
 
 SQMP
@@ -492,7 +492,7 @@ EQMP
     {
         .name       = "ringbuf-write",
         .args_type  = "device:s,data:s,format:s?",
-        .mhandler.cmd_new = qmp_marshal_input_ringbuf_write,
+        .mhandler.cmd_new = qmp_marshal_ringbuf_write,
     },
 
 SQMP
@@ -523,7 +523,7 @@ EQMP
     {
         .name       = "ringbuf-read",
         .args_type  = "device:s,size:i,format:s?",
-        .mhandler.cmd_new = qmp_marshal_input_ringbuf_read,
+        .mhandler.cmd_new = qmp_marshal_ringbuf_read,
     },
 
 SQMP
@@ -559,7 +559,7 @@ EQMP
     {
         .name       = "xen-save-devices-state",
         .args_type  = "filename:F",
-    .mhandler.cmd_new = qmp_marshal_input_xen_save_devices_state,
+    .mhandler.cmd_new = qmp_marshal_xen_save_devices_state,
     },
 
 SQMP
@@ -586,7 +586,7 @@ EQMP
     {
         .name       = "xen-set-global-dirty-log",
         .args_type  = "enable:b",
-        .mhandler.cmd_new = qmp_marshal_input_xen_set_global_dirty_log,
+        .mhandler.cmd_new = qmp_marshal_xen_set_global_dirty_log,
     },
 
 SQMP
@@ -610,7 +610,7 @@ EQMP
     {
         .name       = "migrate",
         .args_type  = "detach:-d,blk:-b,inc:-i,uri:s",
-        .mhandler.cmd_new = qmp_marshal_input_migrate,
+        .mhandler.cmd_new = qmp_marshal_migrate,
     },
 
 SQMP
@@ -643,7 +643,7 @@ EQMP
     {
         .name       = "migrate_cancel",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_migrate_cancel,
+        .mhandler.cmd_new = qmp_marshal_migrate_cancel,
     },
 
 SQMP
@@ -664,7 +664,7 @@ EQMP
     {
         .name       = "migrate-incoming",
         .args_type  = "uri:s",
-        .mhandler.cmd_new = qmp_marshal_input_migrate_incoming,
+        .mhandler.cmd_new = qmp_marshal_migrate_incoming,
     },
 
 SQMP
@@ -692,7 +692,7 @@ EQMP
     {
         .name       = "migrate-set-cache-size",
         .args_type  = "value:o",
-        .mhandler.cmd_new = qmp_marshal_input_migrate_set_cache_size,
+        .mhandler.cmd_new = qmp_marshal_migrate_set_cache_size,
     },
 
 SQMP
@@ -715,7 +715,7 @@ EQMP
     {
         .name       = "query-migrate-cache-size",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_migrate_cache_size,
+        .mhandler.cmd_new = qmp_marshal_query_migrate_cache_size,
     },
 
 SQMP
@@ -737,7 +737,7 @@ EQMP
     {
         .name       = "migrate_set_speed",
         .args_type  = "value:o",
-        .mhandler.cmd_new = qmp_marshal_input_migrate_set_speed,
+        .mhandler.cmd_new = qmp_marshal_migrate_set_speed,
     },
 
 SQMP
@@ -760,7 +760,7 @@ EQMP
     {
         .name       = "migrate_set_downtime",
         .args_type  = "value:T",
-        .mhandler.cmd_new = qmp_marshal_input_migrate_set_downtime,
+        .mhandler.cmd_new = qmp_marshal_migrate_set_downtime,
     },
 
 SQMP
@@ -785,7 +785,7 @@ EQMP
         .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
         .params     = "protocol hostname port tls-port cert-subject",
         .help       = "set migration information for remote display",
-        .mhandler.cmd_new = qmp_marshal_input_client_migrate_info,
+        .mhandler.cmd_new = qmp_marshal_client_migrate_info,
     },
 
 SQMP
@@ -819,7 +819,7 @@ EQMP
         .args_type  = "paging:b,protocol:s,begin:i?,end:i?,format:s?",
         .params     = "-p protocol [begin] [length] [format]",
         .help       = "dump guest memory to file",
-        .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory,
+        .mhandler.cmd_new = qmp_marshal_dump_guest_memory,
     },
 
 SQMP
@@ -855,7 +855,7 @@ EQMP
     {
         .name       = "query-dump-guest-memory-capability",
         .args_type  = "",
-    .mhandler.cmd_new = qmp_marshal_input_query_dump_guest_memory_capability,
+    .mhandler.cmd_new = qmp_marshal_query_dump_guest_memory_capability,
     },
 
 SQMP
@@ -904,7 +904,7 @@ EQMP
     {
         .name       = "netdev_del",
         .args_type  = "id:s",
-        .mhandler.cmd_new = qmp_marshal_input_netdev_del,
+        .mhandler.cmd_new = qmp_marshal_netdev_del,
     },
 
 SQMP
@@ -954,7 +954,7 @@ EQMP
     {
         .name       = "object-del",
         .args_type  = "id:s",
-        .mhandler.cmd_new = qmp_marshal_input_object_del,
+        .mhandler.cmd_new = qmp_marshal_object_del,
     },
 
 SQMP
@@ -979,7 +979,7 @@ EQMP
     {
         .name       = "block_resize",
         .args_type  = "device:s?,node-name:s?,size:o",
-        .mhandler.cmd_new = qmp_marshal_input_block_resize,
+        .mhandler.cmd_new = qmp_marshal_block_resize,
     },
 
 SQMP
@@ -1004,7 +1004,7 @@ EQMP
     {
         .name       = "block-stream",
         .args_type  = "device:B,base:s?,speed:o?,backing-file:s?,on-error:s?",
-        .mhandler.cmd_new = qmp_marshal_input_block_stream,
+        .mhandler.cmd_new = qmp_marshal_block_stream,
     },
 
 SQMP
@@ -1047,7 +1047,7 @@ EQMP
     {
         .name       = "block-commit",
         .args_type  = "device:B,base:s?,top:s?,backing-file:s?,speed:o?",
-        .mhandler.cmd_new = qmp_marshal_input_block_commit,
+        .mhandler.cmd_new = qmp_marshal_block_commit,
     },
 
 SQMP
@@ -1111,7 +1111,7 @@ EQMP
         .name       = "drive-backup",
         .args_type  = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
                       "bitmap:s?,on-source-error:s?,on-target-error:s?",
-        .mhandler.cmd_new = qmp_marshal_input_drive_backup,
+        .mhandler.cmd_new = qmp_marshal_drive_backup,
     },
 
 SQMP
@@ -1165,7 +1165,7 @@ EQMP
         .name       = "blockdev-backup",
         .args_type  = "sync:s,device:B,target:B,speed:i?,"
                       "on-source-error:s?,on-target-error:s?",
-        .mhandler.cmd_new = qmp_marshal_input_blockdev_backup,
+        .mhandler.cmd_new = qmp_marshal_blockdev_backup,
     },
 
 SQMP
@@ -1205,33 +1205,33 @@ EQMP
     {
         .name       = "block-job-set-speed",
         .args_type  = "device:B,speed:o",
-        .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed,
+        .mhandler.cmd_new = qmp_marshal_block_job_set_speed,
     },
 
     {
         .name       = "block-job-cancel",
         .args_type  = "device:B,force:b?",
-        .mhandler.cmd_new = qmp_marshal_input_block_job_cancel,
+        .mhandler.cmd_new = qmp_marshal_block_job_cancel,
     },
     {
         .name       = "block-job-pause",
         .args_type  = "device:B",
-        .mhandler.cmd_new = qmp_marshal_input_block_job_pause,
+        .mhandler.cmd_new = qmp_marshal_block_job_pause,
     },
     {
         .name       = "block-job-resume",
         .args_type  = "device:B",
-        .mhandler.cmd_new = qmp_marshal_input_block_job_resume,
+        .mhandler.cmd_new = qmp_marshal_block_job_resume,
     },
     {
         .name       = "block-job-complete",
         .args_type  = "device:B",
-        .mhandler.cmd_new = qmp_marshal_input_block_job_complete,
+        .mhandler.cmd_new = qmp_marshal_block_job_complete,
     },
     {
         .name       = "transaction",
         .args_type  = "actions:q",
-        .mhandler.cmd_new = qmp_marshal_input_transaction,
+        .mhandler.cmd_new = qmp_marshal_transaction,
     },
 
 SQMP
@@ -1310,7 +1310,7 @@ EQMP
     {
         .name       = "block-dirty-bitmap-add",
         .args_type  = "node:B,name:s,granularity:i?",
-        .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_add,
+        .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_add,
     },
 
 SQMP
@@ -1338,7 +1338,7 @@ EQMP
     {
         .name       = "block-dirty-bitmap-remove",
         .args_type  = "node:B,name:s",
-        .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_remove,
+        .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_remove,
     },
 
 SQMP
@@ -1366,7 +1366,7 @@ EQMP
     {
         .name       = "block-dirty-bitmap-clear",
         .args_type  = "node:B,name:s",
-        .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_clear,
+        .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_clear,
     },
 
 SQMP
@@ -1395,7 +1395,7 @@ EQMP
     {
         .name       = "blockdev-snapshot-sync",
         .args_type  = "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?",
-        .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync,
+        .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_sync,
     },
 
 SQMP
@@ -1431,7 +1431,7 @@ EQMP
     {
         .name       = "blockdev-snapshot-internal-sync",
         .args_type  = "device:B,name:s",
-        .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_internal_sync,
+        .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_internal_sync,
     },
 
 SQMP
@@ -1461,7 +1461,7 @@ EQMP
         .name       = "blockdev-snapshot-delete-internal-sync",
         .args_type  = "device:B,id:s?,name:s?",
         .mhandler.cmd_new =
-                      qmp_marshal_input_blockdev_snapshot_delete_internal_sync,
+                      qmp_marshal_blockdev_snapshot_delete_internal_sync,
     },
 
 SQMP
@@ -1505,7 +1505,7 @@ EQMP
                       "on-source-error:s?,on-target-error:s?,"
                       "unmap:b?,"
                       "granularity:i?,buf-size:i?",
-        .mhandler.cmd_new = qmp_marshal_input_drive_mirror,
+        .mhandler.cmd_new = qmp_marshal_drive_mirror,
     },
 
 SQMP
@@ -1565,7 +1565,7 @@ EQMP
     {
         .name       = "change-backing-file",
         .args_type  = "device:s,image-node-name:s,backing-file:s",
-        .mhandler.cmd_new = qmp_marshal_input_change_backing_file,
+        .mhandler.cmd_new = qmp_marshal_change_backing_file,
     },
 
 SQMP
@@ -1604,7 +1604,7 @@ EQMP
     {
         .name       = "balloon",
         .args_type  = "value:M",
-        .mhandler.cmd_new = qmp_marshal_input_balloon,
+        .mhandler.cmd_new = qmp_marshal_balloon,
     },
 
 SQMP
@@ -1627,7 +1627,7 @@ EQMP
     {
         .name       = "set_link",
         .args_type  = "name:s,up:b",
-        .mhandler.cmd_new = qmp_marshal_input_set_link,
+        .mhandler.cmd_new = qmp_marshal_set_link,
     },
 
 SQMP
@@ -1653,7 +1653,7 @@ EQMP
         .args_type  = "fdname:s",
         .params     = "getfd name",
         .help       = "receive a file descriptor via SCM rights and assign it a name",
-        .mhandler.cmd_new = qmp_marshal_input_getfd,
+        .mhandler.cmd_new = qmp_marshal_getfd,
     },
 
 SQMP
@@ -1686,7 +1686,7 @@ EQMP
         .args_type  = "fdname:s",
         .params     = "closefd name",
         .help       = "close a file descriptor previously passed via SCM rights",
-        .mhandler.cmd_new = qmp_marshal_input_closefd,
+        .mhandler.cmd_new = qmp_marshal_closefd,
     },
 
 SQMP
@@ -1711,7 +1711,7 @@ EQMP
         .args_type  = "fdset-id:i?,opaque:s?",
         .params     = "add-fd fdset-id opaque",
         .help       = "Add a file descriptor, that was passed via SCM rights, to an fd set",
-        .mhandler.cmd_new = qmp_marshal_input_add_fd,
+        .mhandler.cmd_new = qmp_marshal_add_fd,
     },
 
 SQMP
@@ -1750,7 +1750,7 @@ EQMP
         .args_type  = "fdset-id:i,fd:i?",
         .params     = "remove-fd fdset-id fd",
         .help       = "Remove a file descriptor from an fd set",
-        .mhandler.cmd_new = qmp_marshal_input_remove_fd,
+        .mhandler.cmd_new = qmp_marshal_remove_fd,
     },
 
 SQMP
@@ -1782,7 +1782,7 @@ EQMP
         .name       = "query-fdsets",
         .args_type  = "",
         .help       = "Return information describing all fd sets",
-        .mhandler.cmd_new = qmp_marshal_input_query_fdsets,
+        .mhandler.cmd_new = qmp_marshal_query_fdsets,
     },
 
 SQMP
@@ -1831,7 +1831,7 @@ EQMP
     {
         .name       = "block_passwd",
         .args_type  = "device:s?,node-name:s?,password:s",
-        .mhandler.cmd_new = qmp_marshal_input_block_passwd,
+        .mhandler.cmd_new = qmp_marshal_block_passwd,
     },
 
 SQMP
@@ -1857,7 +1857,7 @@ EQMP
     {
         .name       = "block_set_io_throttle",
         .args_type  = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?",
-        .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle,
+        .mhandler.cmd_new = qmp_marshal_block_set_io_throttle,
     },
 
 SQMP
@@ -1907,7 +1907,7 @@ EQMP
     {
         .name       = "set_password",
         .args_type  = "protocol:s,password:s,connected:s?",
-        .mhandler.cmd_new = qmp_marshal_input_set_password,
+        .mhandler.cmd_new = qmp_marshal_set_password,
     },
 
 SQMP
@@ -1933,7 +1933,7 @@ EQMP
     {
         .name       = "expire_password",
         .args_type  = "protocol:s,time:s",
-        .mhandler.cmd_new = qmp_marshal_input_expire_password,
+        .mhandler.cmd_new = qmp_marshal_expire_password,
     },
 
 SQMP
@@ -1958,7 +1958,7 @@ EQMP
     {
         .name       = "add_client",
         .args_type  = "protocol:s,fdname:s,skipauth:b?,tls:b?",
-        .mhandler.cmd_new = qmp_marshal_input_add_client,
+        .mhandler.cmd_new = qmp_marshal_add_client,
     },
 
 SQMP
@@ -2009,7 +2009,7 @@ EQMP
     {
         .name       = "human-monitor-command",
         .args_type  = "command-line:s,cpu-index:i?",
-        .mhandler.cmd_new = qmp_marshal_input_human_monitor_command,
+        .mhandler.cmd_new = qmp_marshal_human_monitor_command,
     },
 
 SQMP
@@ -2088,7 +2088,7 @@ EQMP
     {
         .name       = "query-version",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_version,
+        .mhandler.cmd_new = qmp_marshal_query_version,
     },
 
 SQMP
@@ -2125,7 +2125,7 @@ EQMP
     {
         .name       = "query-commands",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_commands,
+        .mhandler.cmd_new = qmp_marshal_query_commands,
     },
 
 SQMP
@@ -2162,7 +2162,7 @@ EQMP
     {
         .name       = "query-events",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_events,
+        .mhandler.cmd_new = qmp_marshal_query_events,
     },
 
 SQMP
@@ -2207,7 +2207,7 @@ EQMP
     {
         .name       = "query-chardev",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_chardev,
+        .mhandler.cmd_new = qmp_marshal_query_chardev,
     },
 
 SQMP
@@ -2248,7 +2248,7 @@ EQMP
     {
         .name       = "query-chardev-backends",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_chardev_backends,
+        .mhandler.cmd_new = qmp_marshal_query_chardev_backends,
     },
 
 SQMP
@@ -2432,7 +2432,7 @@ EQMP
     {
         .name       = "query-block",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_block,
+        .mhandler.cmd_new = qmp_marshal_query_block,
     },
 
 SQMP
@@ -2559,7 +2559,7 @@ EQMP
     {
         .name       = "query-blockstats",
         .args_type  = "query-nodes:b?",
-        .mhandler.cmd_new = qmp_marshal_input_query_blockstats,
+        .mhandler.cmd_new = qmp_marshal_query_blockstats,
     },
 
 SQMP
@@ -2610,7 +2610,7 @@ EQMP
     {
         .name       = "query-cpus",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_cpus,
+        .mhandler.cmd_new = qmp_marshal_query_cpus,
     },
 
 SQMP
@@ -2649,7 +2649,7 @@ EQMP
     {
         .name       = "query-iothreads",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_iothreads,
+        .mhandler.cmd_new = qmp_marshal_query_iothreads,
     },
 
 SQMP
@@ -2866,7 +2866,7 @@ EQMP
     {
         .name       = "query-pci",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_pci,
+        .mhandler.cmd_new = qmp_marshal_query_pci,
     },
 
 SQMP
@@ -2890,7 +2890,7 @@ EQMP
     {
         .name       = "query-kvm",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_kvm,
+        .mhandler.cmd_new = qmp_marshal_query_kvm,
     },
 
 SQMP
@@ -2930,7 +2930,7 @@ EQMP
     {
         .name       = "query-status",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_status,
+        .mhandler.cmd_new = qmp_marshal_query_status,
     },
 
 SQMP
@@ -2974,7 +2974,7 @@ EQMP
     {
         .name       = "query-mice",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_mice,
+        .mhandler.cmd_new = qmp_marshal_query_mice,
     },
 
 SQMP
@@ -3037,12 +3037,12 @@ EQMP
     {
         .name       = "query-vnc",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_vnc,
+        .mhandler.cmd_new = qmp_marshal_query_vnc,
     },
     {
         .name       = "query-vnc-servers",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_vnc_servers,
+        .mhandler.cmd_new = qmp_marshal_query_vnc_servers,
     },
 
 SQMP
@@ -3119,7 +3119,7 @@ EQMP
     {
         .name       = "query-spice",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_spice,
+        .mhandler.cmd_new = qmp_marshal_query_spice,
     },
 #endif
 
@@ -3143,7 +3143,7 @@ EQMP
     {
         .name       = "query-name",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_name,
+        .mhandler.cmd_new = qmp_marshal_query_name,
     },
 
 SQMP
@@ -3166,7 +3166,7 @@ EQMP
     {
         .name       = "query-uuid",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_uuid,
+        .mhandler.cmd_new = qmp_marshal_query_uuid,
     },
 
 SQMP
@@ -3215,7 +3215,7 @@ EQMP
     {
         .name       = "query-command-line-options",
         .args_type  = "option:s?",
-        .mhandler.cmd_new = qmp_marshal_input_query_command_line_options,
+        .mhandler.cmd_new = qmp_marshal_query_command_line_options,
     },
 
 SQMP
@@ -3393,7 +3393,7 @@ EQMP
     {
         .name       = "query-migrate",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_migrate,
+        .mhandler.cmd_new = qmp_marshal_query_migrate,
     },
 
 SQMP
@@ -3421,7 +3421,7 @@ EQMP
         .name       = "migrate-set-capabilities",
         .args_type  = "capabilities:q",
         .params     = "capability:s,state:b",
-	.mhandler.cmd_new = qmp_marshal_input_migrate_set_capabilities,
+	.mhandler.cmd_new = qmp_marshal_migrate_set_capabilities,
     },
 SQMP
 query-migrate-capabilities
@@ -3447,7 +3447,7 @@ EQMP
     {
         .name       = "query-migrate-capabilities",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_migrate_capabilities,
+        .mhandler.cmd_new = qmp_marshal_query_migrate_capabilities,
     },
 
 SQMP
@@ -3473,7 +3473,7 @@ EQMP
         .name       = "migrate-set-parameters",
         .args_type  =
             "compress-level:i?,compress-threads:i?,decompress-threads:i?",
-	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
+	.mhandler.cmd_new = qmp_marshal_migrate_set_parameters,
     },
 SQMP
 query-migrate-parameters
@@ -3504,7 +3504,7 @@ EQMP
     {
         .name       = "query-migrate-parameters",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_migrate_parameters,
+        .mhandler.cmd_new = qmp_marshal_query_migrate_parameters,
     },
 
 SQMP
@@ -3532,19 +3532,19 @@ EQMP
     {
         .name       = "query-balloon",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_balloon,
+        .mhandler.cmd_new = qmp_marshal_query_balloon,
     },
 
     {
         .name       = "query-block-jobs",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_block_jobs,
+        .mhandler.cmd_new = qmp_marshal_query_block_jobs,
     },
 
     {
         .name       = "qom-list",
         .args_type  = "path:s",
-        .mhandler.cmd_new = qmp_marshal_input_qom_list,
+        .mhandler.cmd_new = qmp_marshal_qom_list,
     },
 
     {
@@ -3562,58 +3562,58 @@ EQMP
     {
         .name       = "nbd-server-start",
         .args_type  = "addr:q",
-        .mhandler.cmd_new = qmp_marshal_input_nbd_server_start,
+        .mhandler.cmd_new = qmp_marshal_nbd_server_start,
     },
     {
         .name       = "nbd-server-add",
         .args_type  = "device:B,writable:b?",
-        .mhandler.cmd_new = qmp_marshal_input_nbd_server_add,
+        .mhandler.cmd_new = qmp_marshal_nbd_server_add,
     },
     {
         .name       = "nbd-server-stop",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_nbd_server_stop,
+        .mhandler.cmd_new = qmp_marshal_nbd_server_stop,
     },
 
     {
         .name       = "change-vnc-password",
         .args_type  = "password:s",
-        .mhandler.cmd_new = qmp_marshal_input_change_vnc_password,
+        .mhandler.cmd_new = qmp_marshal_change_vnc_password,
     },
     {
         .name       = "qom-list-types",
         .args_type  = "implements:s?,abstract:b?",
-        .mhandler.cmd_new = qmp_marshal_input_qom_list_types,
+        .mhandler.cmd_new = qmp_marshal_qom_list_types,
     },
 
     {
         .name       = "device-list-properties",
         .args_type  = "typename:s",
-        .mhandler.cmd_new = qmp_marshal_input_device_list_properties,
+        .mhandler.cmd_new = qmp_marshal_device_list_properties,
     },
 
     {
         .name       = "query-machines",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_machines,
+        .mhandler.cmd_new = qmp_marshal_query_machines,
     },
 
     {
         .name       = "query-cpu-definitions",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_cpu_definitions,
+        .mhandler.cmd_new = qmp_marshal_query_cpu_definitions,
     },
 
     {
         .name       = "query-target",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_target,
+        .mhandler.cmd_new = qmp_marshal_query_target,
     },
 
     {
         .name       = "query-tpm",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_tpm,
+        .mhandler.cmd_new = qmp_marshal_query_tpm,
     },
 
 SQMP
@@ -3647,7 +3647,7 @@ EQMP
     {
         .name       = "query-tpm-models",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_tpm_models,
+        .mhandler.cmd_new = qmp_marshal_query_tpm_models,
     },
 
 SQMP
@@ -3668,7 +3668,7 @@ EQMP
     {
         .name       = "query-tpm-types",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_tpm_types,
+        .mhandler.cmd_new = qmp_marshal_query_tpm_types,
     },
 
 SQMP
@@ -3689,7 +3689,7 @@ EQMP
     {
         .name       = "chardev-add",
         .args_type  = "id:s,backend:q",
-        .mhandler.cmd_new = qmp_marshal_input_chardev_add,
+        .mhandler.cmd_new = qmp_marshal_chardev_add,
     },
 
 SQMP
@@ -3726,7 +3726,7 @@ EQMP
     {
         .name       = "chardev-remove",
         .args_type  = "id:s",
-        .mhandler.cmd_new = qmp_marshal_input_chardev_remove,
+        .mhandler.cmd_new = qmp_marshal_chardev_remove,
     },
 
 
@@ -3749,7 +3749,7 @@ EQMP
     {
         .name       = "query-rx-filter",
         .args_type  = "name:s?",
-        .mhandler.cmd_new = qmp_marshal_input_query_rx_filter,
+        .mhandler.cmd_new = qmp_marshal_query_rx_filter,
     },
 
 SQMP
@@ -3815,7 +3815,7 @@ EQMP
     {
         .name       = "blockdev-add",
         .args_type  = "options:q",
-        .mhandler.cmd_new = qmp_marshal_input_blockdev_add,
+        .mhandler.cmd_new = qmp_marshal_blockdev_add,
     },
 
 SQMP
@@ -3874,7 +3874,7 @@ EQMP
     {
         .name       = "query-named-block-nodes",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_named_block_nodes,
+        .mhandler.cmd_new = qmp_marshal_query_named_block_nodes,
     },
 
 SQMP
@@ -3936,7 +3936,7 @@ EQMP
     {
         .name       = "query-memdev",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_memdev,
+        .mhandler.cmd_new = qmp_marshal_query_memdev,
     },
 
 SQMP
@@ -3974,7 +3974,7 @@ EQMP
     {
         .name       = "query-memory-devices",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_memory_devices,
+        .mhandler.cmd_new = qmp_marshal_query_memory_devices,
     },
 
 SQMP
@@ -4001,7 +4001,7 @@ EQMP
     {
         .name       = "query-acpi-ospm-status",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_query_acpi_ospm_status,
+        .mhandler.cmd_new = qmp_marshal_query_acpi_ospm_status,
     },
 
 SQMP
@@ -4024,7 +4024,7 @@ EQMP
     {
         .name       = "rtc-reset-reinjection",
         .args_type  = "",
-        .mhandler.cmd_new = qmp_marshal_input_rtc_reset_reinjection,
+        .mhandler.cmd_new = qmp_marshal_rtc_reset_reinjection,
     },
 #endif
 
@@ -4045,7 +4045,7 @@ EQMP
     {
         .name       = "trace-event-get-state",
         .args_type  = "name:s",
-        .mhandler.cmd_new = qmp_marshal_input_trace_event_get_state,
+        .mhandler.cmd_new = qmp_marshal_trace_event_get_state,
     },
 
 SQMP
@@ -4063,7 +4063,7 @@ EQMP
     {
         .name       = "trace-event-set-state",
         .args_type  = "name:s,enable:b,ignore-unavailable:b?",
-        .mhandler.cmd_new = qmp_marshal_input_trace_event_set_state,
+        .mhandler.cmd_new = qmp_marshal_trace_event_set_state,
     },
 
 SQMP
@@ -4081,7 +4081,7 @@ EQMP
     {
         .name       = "x-input-send-event",
         .args_type  = "console:i?,events:q",
-        .mhandler.cmd_new = qmp_marshal_input_x_input_send_event,
+        .mhandler.cmd_new = qmp_marshal_x_input_send_event,
     },
 
 SQMP
@@ -4146,7 +4146,7 @@ EQMP
     {
         .name       = "block-set-write-threshold",
         .args_type  = "node-name:s,write-threshold:l",
-        .mhandler.cmd_new = qmp_marshal_input_block_set_write_threshold,
+        .mhandler.cmd_new = qmp_marshal_block_set_write_threshold,
     },
 
 SQMP
@@ -4174,7 +4174,7 @@ EQMP
     {
         .name       = "query-rocker",
         .args_type  = "name:s",
-        .mhandler.cmd_new = qmp_marshal_input_query_rocker,
+        .mhandler.cmd_new = qmp_marshal_query_rocker,
     },
 
 SQMP
@@ -4195,7 +4195,7 @@ EQMP
     {
         .name       = "query-rocker-ports",
         .args_type  = "name:s",
-        .mhandler.cmd_new = qmp_marshal_input_query_rocker_ports,
+        .mhandler.cmd_new = qmp_marshal_query_rocker_ports,
     },
 
 SQMP
@@ -4220,7 +4220,7 @@ EQMP
     {
         .name       = "query-rocker-of-dpa-flows",
         .args_type  = "name:s,tbl-id:i?",
-        .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_flows,
+        .mhandler.cmd_new = qmp_marshal_query_rocker_of_dpa_flows,
     },
 
 SQMP
@@ -4249,7 +4249,7 @@ EQMP
     {
         .name       = "query-rocker-of-dpa-groups",
         .args_type  = "name:s,type:i?",
-        .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_groups,
+        .mhandler.cmd_new = qmp_marshal_query_rocker_of_dpa_groups,
     },
 
 SQMP
diff --git a/qmp.c b/qmp.c
index 403805a..f31c6dc 100644
--- a/qmp.c
+++ b/qmp.c
@@ -151,9 +151,9 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
  * #ifdef CONFIG_SPICE.  Necessary for an accurate query-commands
  * result.  However, the QAPI schema is blissfully unaware of that,
  * and the QAPI code generator happily generates a dead
- * qmp_marshal_input_query_spice() that calls qmp_query_spice().
- * Provide it one, or else linking fails.
- * FIXME Educate the QAPI schema on CONFIG_SPICE.
+ * qmp_marshal_query_spice() that calls qmp_query_spice().  Provide it
+ * one, or else linking fails.  FIXME Educate the QAPI schema on
+ * CONFIG_SPICE.
  */
 SpiceInfo *qmp_query_spice(Error **errp)
 {
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 7db30e5..18f4088 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -197,7 +197,7 @@ out:
                  c_name=ret_type.c_name())
 
 def gen_marshal_proto(name):
-    ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
+    ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
     if not middle_mode:
         ret = 'static ' + ret
     return ret
@@ -241,7 +241,7 @@ def gen_register_command(name, success_response):
         options = 'QCO_NO_SUCCESS_RESP'
 
     ret = mcgen('''
-qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
+qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
 ''',
                 name=name, c_name=c_name(name),
                 opts=options)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 23/32] qapi: De-duplicate parameter list generation
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (21 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 22/32] qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO() Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 24/32] qapi-commands: De-duplicate output marshaling functions Markus Armbruster
                   ` (9 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Generated qapi-event.[ch] lose line breaks.  No change otherwise.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 scripts/qapi-commands.py | 11 ++---------
 scripts/qapi-event.py    | 18 +++---------------
 scripts/qapi.py          | 16 ++++++++++++++++
 3 files changed, 21 insertions(+), 24 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 18f4088..5a63db0 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -16,19 +16,12 @@ from qapi import *
 import re
 
 def gen_command_decl(name, arg_type, ret_type):
-    argstr = ''
-    if arg_type:
-        for memb in arg_type.members:
-            if memb.optional:
-                argstr += 'bool has_%s, ' % c_name(memb.name)
-            argstr += '%s %s, ' % (memb.type.c_type(is_param=True),
-                                   c_name(memb.name))
     return mcgen('''
-%(c_type)s qmp_%(c_name)s(%(args)sError **errp);
+%(c_type)s qmp_%(c_name)s(%(params)s);
 ''',
                  c_type=(ret_type and ret_type.c_type()) or 'void',
                  c_name=c_name(name),
-                 args=argstr)
+                 params=gen_params(arg_type, 'Error **errp'))
 
 def gen_err_check(err):
     if not err:
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index dbcc5a1..d100099 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -14,21 +14,9 @@
 from qapi import *
 
 def gen_event_send_proto(name, arg_type):
-    api_name = "void qapi_event_send_%s(" % c_name(name).lower()
-    l = len(api_name)
-
-    if arg_type:
-        for m in arg_type.members:
-            if m.optional:
-                api_name += "bool has_%s,\n" % c_name(m.name)
-                api_name += "".ljust(l)
-
-            api_name += "%s %s,\n" % (m.type.c_type(is_param=True),
-                                      c_name(m.name))
-            api_name += "".ljust(l)
-
-    api_name += "Error **errp)"
-    return api_name
+    return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
+        'c_name': c_name(name.lower()),
+        'param': gen_params(arg_type, 'Error **errp')}
 
 def gen_event_send_decl(name, arg_type):
     return mcgen('''
diff --git a/scripts/qapi.py b/scripts/qapi.py
index d314b13..0a325d5 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1395,6 +1395,22 @@ extern const char *const %(c_name)s_lookup[];
                  c_name=c_name(name))
     return ret
 
+def gen_params(arg_type, extra):
+    if not arg_type:
+        return extra
+    assert not arg_type.variants
+    ret = ''
+    sep = ''
+    for memb in arg_type.members:
+        ret += sep
+        sep = ', '
+        if memb.optional:
+            ret += 'bool has_%s, ' % c_name(memb.name)
+        ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
+    if extra:
+        ret += sep + extra
+    return ret
+
 #
 # Common command line parsing
 #
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 24/32] qapi-commands: De-duplicate output marshaling functions
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (22 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 23/32] qapi: De-duplicate parameter list generation Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 25/32] qapi: Improve built-in type documentation Markus Armbruster
                   ` (8 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

gen_marshal_output() uses its parameter name only for name of the
generated function.  Name it after the type being marshaled instead of
its caller, and drop duplicates.

Saves 7 copies of qmp_marshal_output_int() in qemu-ga, and one copy of
qmp_marshal_output_str() in qemu-system-*.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt   |  4 ++--
 scripts/qapi-commands.py | 17 ++++++++++-------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 09d15dc..147638f 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -713,7 +713,7 @@ Example:
     $ cat qapi-generated/example-qmp-marshal.c
 [Uninteresting stuff omitted...]
 
-    static void qmp_marshal_output_my_command(UserDefOne *ret_in, QObject **ret_out, Error **errp)
+    static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp)
     {
         Error *local_err = NULL;
         QmpOutputVisitor *mo = qmp_output_visitor_new();
@@ -756,7 +756,7 @@ Example:
             goto out;
         }
 
-        qmp_marshal_output_my_command(retval, ret, &local_err);
+        qmp_marshal_output_UserDefOne(retval, ret, &local_err);
 
     out:
         error_propagate(errp, local_err);
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 5a63db0..7ff7c31 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -59,7 +59,7 @@ def gen_call(name, arg_type, ret_type):
 
 qmp_marshal_output_%(c_name)s(retval, ret, &local_err);
 ''',
-                     c_name=c_name(name))
+                     c_name=ret_type.c_name())
     pop_indent()
     return ret
 
@@ -160,10 +160,10 @@ qapi_dealloc_visitor_cleanup(md);
     pop_indent()
     return ret
 
-def gen_marshal_output(name, ret_type):
+def gen_marshal_output(ret_type):
     return mcgen('''
 
-static void qmp_marshal_output_%(c_cmd_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
+static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *local_err = NULL;
     QmpOutputVisitor *mo = qmp_output_visitor_new();
@@ -186,8 +186,7 @@ out:
     qapi_dealloc_visitor_cleanup(md);
 }
 ''',
-                 c_type=ret_type.c_type(), c_cmd_name=c_name(name),
-                 c_name=ret_type.c_name())
+                 c_type=ret_type.c_type(), c_name=ret_type.c_name())
 
 def gen_marshal_proto(name):
     ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
@@ -260,21 +259,25 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         self.decl = None
         self.defn = None
         self.regy = None
+        self.visited_ret_types = None
     def visit_begin(self, schema):
         self.decl = ''
         self.defn = ''
         self.regy = ''
+        self.visited_ret_types = set()
     def visit_end(self):
         if not middle_mode:
             self.defn += gen_registry(self.regy)
         self.regy = None
+        self.visited_ret_types = None
     def visit_command(self, name, info, arg_type, ret_type,
                       gen, success_response):
         if not gen:
             return
         self.decl += gen_command_decl(name, arg_type, ret_type)
-        if ret_type:
-            self.defn += gen_marshal_output(name, ret_type)
+        if ret_type and ret_type not in self.visited_ret_types:
+            self.visited_ret_types.add(ret_type)
+            self.defn += gen_marshal_output(ret_type)
         if middle_mode:
             self.decl += gen_marshal_decl(name)
         self.defn += gen_marshal(name, arg_type, ret_type)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 25/32] qapi: Improve built-in type documentation
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (23 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 24/32] qapi-commands: De-duplicate output marshaling functions Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 26/32] qapi: Introduce a first class 'any' type Markus Armbruster
                   ` (7 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

Clarify how they map to JSON.  Add how they map to C.  Fix the
reference to StringInputVisitor.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 147638f..35a4a0f 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -140,17 +140,24 @@ must have a value that forms a struct name.
 
 === Built-in Types ===
 
-The following types are built-in to the parser:
-  'str' - arbitrary UTF-8 string
-  'int' - 64-bit signed integer (although the C code may place further
-          restrictions on acceptable range)
-  'number' - floating point number
-  'bool' - JSON value of true or false
-  'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum
-                                      bit size
-  'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts
-  'size' - like 'uint64', but allows scaled suffix from command line
-           visitor
+The following types are predefined, and map to C as follows:
+
+  Schema    C          JSON
+  str       char *     any JSON string, UTF-8
+  number    double     any JSON number
+  int       int64_t    a JSON number without fractional part
+                       that fits into the C integer type
+  int8      int8_t     likewise
+  int16     int16_t    likewise
+  int32     int32_t    likewise
+  int64     int64_t    likewise
+  uint8     uint8_t    likewise
+  uint16    uint16_t   likewise
+  uint32    uint32_t   likewise
+  uint64    uint64_t   likewise
+  size      uint64_t   like uint64_t, except StringInputVisitor
+                       accepts size suffixes
+  bool      bool       JSON true or false
 
 
 === Includes ===
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 26/32] qapi: Introduce a first class 'any' type
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (24 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 25/32] qapi: Improve built-in type documentation Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 27/32] qom: Don't use 'gen': false for qom-get, qom-set, object-add Markus Armbruster
                   ` (6 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

It's first class, because unlike '**', it actually works, i.e. doesn't
require 'gen': false.

'**' will go away next.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt                             |  1 +
 include/qapi/visitor-impl.h                        |  2 +
 include/qapi/visitor.h                             |  1 +
 qapi/qapi-dealloc-visitor.c                        |  9 ++++
 qapi/qapi-visit-core.c                             |  6 +++
 qapi/qmp-input-visitor.c                           | 11 +++++
 qapi/qmp-output-visitor.c                          |  9 ++++
 scripts/qapi-types.py                              |  1 +
 scripts/qapi.py                                    |  9 ++--
 tests/Makefile                                     |  3 +-
 tests/qapi-schema/args-any.err                     |  1 +
 .../{flat-union-base-star.exit => args-any.exit}   |  0
 tests/qapi-schema/args-any.json                    |  2 +
 .../{flat-union-base-star.out => args-any.out}     |  0
 tests/qapi-schema/flat-union-base-any.err          |  1 +
 tests/qapi-schema/flat-union-base-any.exit         |  1 +
 ...ion-base-star.json => flat-union-base-any.json} |  2 +-
 tests/qapi-schema/flat-union-base-any.out          |  0
 tests/qapi-schema/flat-union-base-star.err         |  1 -
 tests/qapi-schema/qapi-schema-test.json            |  5 +-
 tests/qapi-schema/qapi-schema-test.out             |  9 +++-
 tests/qapi-schema/type-bypass.out                  |  4 +-
 tests/test-qmp-commands.c                          |  5 ++
 tests/test-qmp-input-visitor.c                     | 45 ++++++++++++++++++
 tests/test-qmp-output-visitor.c                    | 53 ++++++++++++++++++++++
 25 files changed, 171 insertions(+), 10 deletions(-)
 create mode 100644 tests/qapi-schema/args-any.err
 rename tests/qapi-schema/{flat-union-base-star.exit => args-any.exit} (100%)
 create mode 100644 tests/qapi-schema/args-any.json
 rename tests/qapi-schema/{flat-union-base-star.out => args-any.out} (100%)
 create mode 100644 tests/qapi-schema/flat-union-base-any.err
 create mode 100644 tests/qapi-schema/flat-union-base-any.exit
 rename tests/qapi-schema/{flat-union-base-star.json => flat-union-base-any.json} (95%)
 create mode 100644 tests/qapi-schema/flat-union-base-any.out
 delete mode 100644 tests/qapi-schema/flat-union-base-star.err

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 35a4a0f..c713e3a 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -158,6 +158,7 @@ The following types are predefined, and map to C as follows:
   size      uint64_t   like uint64_t, except StringInputVisitor
                        accepts size suffixes
   bool      bool       JSON true or false
+  any       QObject *  any JSON value
 
 
 === Includes ===
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index f4a2f74..8c0ba57 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -40,6 +40,8 @@ struct Visitor
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
     void (*type_number)(Visitor *v, double *obj, const char *name,
                         Error **errp);
+    void (*type_any)(Visitor *v, QObject **obj, const char *name,
+                     Error **errp);
 
     /* May be NULL */
     void (*optional)(Visitor *v, bool *present, const char *name,
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 00ba104..cfc19a6 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -58,6 +58,7 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
 void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
 void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
+void visit_type_any(Visitor *v, QObject **obj, const char *name, Error **errp);
 bool visit_start_union(Visitor *v, bool data_present, Error **errp);
 void visit_end_union(Visitor *v, bool data_present, Error **errp);
 
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index d7f92c5..737deab 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -151,6 +151,14 @@ static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
 {
 }
 
+static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
+                                       const char *name, Error **errp)
+{
+    if (obj) {
+        qobject_decref(*obj);
+    }
+}
+
 static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name,
                                    Error **errp)
 {
@@ -216,6 +224,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_bool = qapi_dealloc_type_bool;
     v->visitor.type_str = qapi_dealloc_type_str;
     v->visitor.type_number = qapi_dealloc_type_number;
+    v->visitor.type_any = qapi_dealloc_type_anything;
     v->visitor.type_size = qapi_dealloc_type_size;
     v->visitor.start_union = qapi_dealloc_start_union;
 
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 5a7c900..59ed506 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -260,6 +260,12 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
     v->type_number(v, obj, name, errp);
 }
 
+void visit_type_any(Visitor *v, QObject **obj, const char *name,
+                    Error **errp)
+{
+    v->type_any(v, obj, name, errp);
+}
+
 void output_type_enum(Visitor *v, int *obj, const char * const strings[],
                       const char *kind, const char *name,
                       Error **errp)
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index e97b8a4..5dd9ed5 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -286,6 +286,16 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
     }
 }
 
+static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
+                               Error **errp)
+{
+    QmpInputVisitor *qiv = to_qiv(v);
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+
+    qobject_incref(qobj);
+    *obj = qobj;
+}
+
 static void qmp_input_optional(Visitor *v, bool *present, const char *name,
                                Error **errp)
 {
@@ -329,6 +339,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.type_bool = qmp_input_type_bool;
     v->visitor.type_str = qmp_input_type_str;
     v->visitor.type_number = qmp_input_type_number;
+    v->visitor.type_any = qmp_input_type_any;
     v->visitor.optional = qmp_input_optional;
     v->visitor.get_next_type = qmp_input_get_next_type;
 
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index efc19d5..1e36a7a 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -186,6 +186,14 @@ static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
     qmp_output_add(qov, name, qfloat_from_double(*obj));
 }
 
+static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name,
+                                Error **errp)
+{
+    QmpOutputVisitor *qov = to_qov(v);
+    qobject_incref(*obj);
+    qmp_output_add_obj(qov, name, *obj);
+}
+
 QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
 {
     QObject *obj = qmp_output_first(qov);
@@ -233,6 +241,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.type_bool = qmp_output_type_bool;
     v->visitor.type_str = qmp_output_type_str;
     v->visitor.type_number = qmp_output_type_number;
+    v->visitor.type_any = qmp_output_type_any;
 
     QTAILQ_INIT(&v->stack);
 
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index b5ebeee..70a57c0 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -305,6 +305,7 @@ fdef.write(mcgen('''
 fdecl.write(mcgen('''
 #include <stdbool.h>
 #include <stdint.h>
+#include "qapi/qmp/qobject.h"
 '''))
 
 schema = QAPISchema(input_file)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 0a325d5..a3e4b66 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -33,6 +33,7 @@ builtin_types = {
     'uint32':   'QTYPE_QINT',
     'uint64':   'QTYPE_QINT',
     'size':     'QTYPE_QINT',
+    'any':      None,           # any qtype_code possible, actually
 }
 
 # Whitelist of commands allowed to return a non-dictionary
@@ -1036,8 +1037,7 @@ class QAPISchema(object):
 
     def _def_builtin_type(self, name, json_type, c_type, c_null):
         self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type, c_null))
-        if name != '**':
-            self._make_array_type(name) # TODO really needed?
+        self._make_array_type(name) # TODO really needed?
 
     def _def_predefineds(self):
         for t in [('str',    'string',  'char' + pointer_suffix, 'NULL'),
@@ -1053,8 +1053,9 @@ class QAPISchema(object):
                   ('uint64', 'int',     'uint64_t', '0'),
                   ('size',   'int',     'uint64_t', '0'),
                   ('bool',   'boolean', 'bool',     'false'),
-                  ('**',     'value',   None,       None)]:
+                  ('any',    'value',   'QObject' + pointer_suffix , 'NULL')]:
             self._def_builtin_type(*t)
+        self.entity_dict['**'] = self.lookup_type('any') # TODO drop this alias
 
     def _make_implicit_enum_type(self, name, values):
         name = name + 'Kind'
@@ -1201,6 +1202,8 @@ class QAPISchema(object):
     def visit(self, visitor):
         visitor.visit_begin(self)
         for name in sorted(self.entity_dict.keys()):
+            if self.entity_dict[name].name != name:
+                continue        # ignore alias TODO drop alias and remove
             self.entity_dict[name].visit(visitor)
         visitor.visit_end()
 
diff --git a/tests/Makefile b/tests/Makefile
index b128e28..fc6169a 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -233,6 +233,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
 	args-array-empty.json args-array-unknown.json args-int.json \
 	args-unknown.json args-member-unknown.json args-member-array.json \
 	args-member-array-bad.json args-alternate.json args-union.json \
+	args-any.json \
 	returns-array-bad.json returns-int.json returns-dict.json \
 	returns-unknown.json returns-alternate.json returns-whitelist.json \
 	missing-colon.json missing-comma-list.json missing-comma-object.json \
@@ -249,7 +250,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
 	flat-union-invalid-branch-key.json flat-union-reverse-define.json \
 	flat-union-string-discriminator.json union-base-no-discriminator.json \
 	flat-union-bad-discriminator.json flat-union-bad-base.json \
-	flat-union-base-star.json \
+	flat-union-base-any.json \
 	flat-union-array-branch.json flat-union-int-branch.json \
 	flat-union-base-union.json flat-union-branch-clash.json \
 	alternate-nested.json alternate-unknown.json alternate-clash.json \
diff --git a/tests/qapi-schema/args-any.err b/tests/qapi-schema/args-any.err
new file mode 100644
index 0000000..bf9b5e0
--- /dev/null
+++ b/tests/qapi-schema/args-any.err
@@ -0,0 +1 @@
+tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any'
diff --git a/tests/qapi-schema/flat-union-base-star.exit b/tests/qapi-schema/args-any.exit
similarity index 100%
rename from tests/qapi-schema/flat-union-base-star.exit
rename to tests/qapi-schema/args-any.exit
diff --git a/tests/qapi-schema/args-any.json b/tests/qapi-schema/args-any.json
new file mode 100644
index 0000000..58fe5e4
--- /dev/null
+++ b/tests/qapi-schema/args-any.json
@@ -0,0 +1,2 @@
+# we do not allow an 'any' argument
+{ 'command': 'oops', 'data': 'any' }
diff --git a/tests/qapi-schema/flat-union-base-star.out b/tests/qapi-schema/args-any.out
similarity index 100%
rename from tests/qapi-schema/flat-union-base-star.out
rename to tests/qapi-schema/args-any.out
diff --git a/tests/qapi-schema/flat-union-base-any.err b/tests/qapi-schema/flat-union-base-any.err
new file mode 100644
index 0000000..ad4d629
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-any.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-base-any.json:8: Base 'any' is not a valid struct
diff --git a/tests/qapi-schema/flat-union-base-any.exit b/tests/qapi-schema/flat-union-base-any.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-any.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-base-star.json b/tests/qapi-schema/flat-union-base-any.json
similarity index 95%
rename from tests/qapi-schema/flat-union-base-star.json
rename to tests/qapi-schema/flat-union-base-any.json
index 5099439..fe66b71 100644
--- a/tests/qapi-schema/flat-union-base-star.json
+++ b/tests/qapi-schema/flat-union-base-any.json
@@ -6,7 +6,7 @@
 { 'struct': 'TestTypeB',
   'data': { 'integer': 'int' } }
 { 'union': 'TestUnion',
-  'base': '**',
+  'base': 'any',
   'discriminator': 'enum1',
   'data': { 'value1': 'TestTypeA',
             'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-base-any.out b/tests/qapi-schema/flat-union-base-any.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-base-star.err b/tests/qapi-schema/flat-union-base-star.err
deleted file mode 100644
index b7748f0..0000000
--- a/tests/qapi-schema/flat-union-base-star.err
+++ /dev/null
@@ -1 +0,0 @@
-tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid struct
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 90b4740..e855018 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -73,7 +73,8 @@
             'number': ['number'],
             'boolean': ['bool'],
             'string': ['str'],
-            'sizes': ['size'] } }
+            'sizes': ['size'],
+            'any': ['any'] } }
 
 # testing commands
 { 'command': 'user_def_cmd', 'data': {} }
@@ -83,6 +84,8 @@
   'returns': 'UserDefTwo' }
 { 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' },
   'returns': 'int' }
+# note: command name 'guest-sync' chosen to avoid "cannot use built-in" error
+{ 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' }
 
 # For testing integer range flattening in opts-visitor. The following schema
 # corresponds to the option format:
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index b30bccc..fbb590f 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -12,8 +12,12 @@ object :obj-__org.qemu_x-command-arg
     member b: __org.qemu_x-StructList optional=False
     member c: __org.qemu_x-Union2 optional=False
     member d: __org.qemu_x-Alt optional=False
+object :obj-anyList-wrapper
+    member data: anyList optional=False
 object :obj-boolList-wrapper
     member data: boolList optional=False
+object :obj-guest-sync-arg
+    member arg: any optional=False
 object :obj-int16List-wrapper
     member data: int16List optional=False
 object :obj-int32List-wrapper
@@ -100,7 +104,8 @@ object UserDefNativeListUnion
     case boolean: :obj-boolList-wrapper
     case string: :obj-strList-wrapper
     case sizes: :obj-sizeList-wrapper
-enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes']
+    case any: :obj-anyList-wrapper
+enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any']
 object UserDefOne
     base UserDefZero
     member string: str optional=False
@@ -149,6 +154,8 @@ object __org.qemu_x-Union2
     case __org.qemu_x-value: __org.qemu_x-Struct2
 command __org.qemu_x-command :obj-__org.qemu_x-command-arg -> __org.qemu_x-Union1
    gen=True success_response=True
+command guest-sync :obj-guest-sync-arg -> any
+   gen=True success_response=True
 command user_def_cmd None -> None
    gen=True success_response=True
 command user_def_cmd1 :obj-user_def_cmd1-arg -> None
diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out
index 0070d4b..db2a4e6 100644
--- a/tests/qapi-schema/type-bypass.out
+++ b/tests/qapi-schema/type-bypass.out
@@ -1,4 +1,4 @@
 object :obj-unsafe-arg
-    member arg: ** optional=False
-command unsafe :obj-unsafe-arg -> **
+    member arg: any optional=False
+command unsafe :obj-unsafe-arg -> any
    gen=False success_response=True
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 9918f23..8d5249e 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -51,6 +51,11 @@ int64_t qmp_user_def_cmd3(int64_t a, bool has_b, int64_t b, Error **errp)
     return a + (has_b ? b : 0);
 }
 
+QObject *qmp_guest_sync(QObject *arg, Error **errp)
+{
+    return arg;
+}
+
 __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
                                               __org_qemu_x_StructList *b,
                                               __org_qemu_x_Union2 *c,
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 508c11a..61715b3 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -298,6 +298,49 @@ static void test_visitor_in_list(TestInputVisitorData *data,
     qapi_free_UserDefOneList(head);
 }
 
+static void test_visitor_in_any(TestInputVisitorData *data,
+                                const void *unused)
+{
+    QObject *res = NULL;
+    Error *err = NULL;
+    Visitor *v;
+    QInt *qint;
+    QBool *qbool;
+    QString *qstring;
+    QDict *qdict;
+    QObject *qobj;
+
+    v = visitor_input_test_init(data, "-42");
+    visit_type_any(v, &res, NULL, &err);
+    g_assert(!err);
+    qint = qobject_to_qint(res);
+    g_assert(qint);
+    g_assert_cmpint(qint_get_int(qint), ==, -42);
+    qobject_decref(res);
+
+    v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
+    visit_type_any(v, &res, NULL, &err);
+    g_assert(!err);
+    qdict = qobject_to_qdict(res);
+    g_assert(qdict && qdict_size(qdict) == 3);
+    qobj = qdict_get(qdict, "integer");
+    g_assert(qobj);
+    qint = qobject_to_qint(qobj);
+    g_assert(qint);
+    g_assert_cmpint(qint_get_int(qint), ==, -42);
+    qobj = qdict_get(qdict, "boolean");
+    g_assert(qobj);
+    qbool = qobject_to_qbool(qobj);
+    g_assert(qbool);
+    g_assert(qbool_get_bool(qbool) == true);
+    qobj = qdict_get(qdict, "string");
+    g_assert(qobj);
+    qstring = qobject_to_qstring(qobj);
+    g_assert(qstring);
+    g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
+    qobject_decref(res);
+}
+
 static void test_visitor_in_union_flat(TestInputVisitorData *data,
                                        const void *unused)
 {
@@ -669,6 +712,8 @@ int main(int argc, char **argv)
                            &in_visitor_data, test_visitor_in_struct_nested);
     input_visitor_test_add("/visitor/input/list",
                            &in_visitor_data, test_visitor_in_list);
+    input_visitor_test_add("/visitor/input/any",
+                           &in_visitor_data, test_visitor_in_any);
     input_visitor_test_add("/visitor/input/union-flat",
                            &in_visitor_data, test_visitor_in_union_flat);
     input_visitor_test_add("/visitor/input/alternate",
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 338ada0..1a28dc2 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -428,6 +428,57 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
     qapi_free_UserDefTwoList(head);
 }
 
+static void test_visitor_out_any(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    QObject *qobj;
+    Error *err = NULL;
+    QInt *qint;
+    QBool *qbool;
+    QString *qstring;
+    QDict *qdict;
+    QObject *obj;
+
+    qobj = QOBJECT(qint_from_int(-42));
+    visit_type_any(data->ov, &qobj, NULL, &err);
+    g_assert(!err);
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QINT);
+    g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42);
+    qobject_decref(obj);
+    qobject_decref(qobj);
+
+    qdict = qdict_new();
+    qdict_put(qdict, "integer", qint_from_int(-42));
+    qdict_put(qdict, "boolean", qbool_from_bool(true));
+    qdict_put(qdict, "string", qstring_from_str("foo"));
+    qobj = QOBJECT(qdict);
+    visit_type_any(data->ov, &qobj, NULL, &err);
+    g_assert(!err);
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    qdict = qobject_to_qdict(obj);
+    g_assert(qdict);
+    qobj = qdict_get(qdict, "integer");
+    g_assert(qobj);
+    qint = qobject_to_qint(qobj);
+    g_assert(qint);
+    g_assert_cmpint(qint_get_int(qint), ==, -42);
+    qobj = qdict_get(qdict, "boolean");
+    g_assert(qobj);
+    qbool = qobject_to_qbool(qobj);
+    g_assert(qbool);
+    g_assert(qbool_get_bool(qbool) == true);
+    qobj = qdict_get(qdict, "string");
+    g_assert(qobj);
+    qstring = qobject_to_qstring(qobj);
+    g_assert(qstring);
+    g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
+    qobject_decref(obj);
+    qobject_decref(qobj);
+}
+
 static void test_visitor_out_union_flat(TestOutputVisitorData *data,
                                         const void *unused)
 {
@@ -832,6 +883,8 @@ int main(int argc, char **argv)
                             &out_visitor_data, test_visitor_out_struct_errors);
     output_visitor_test_add("/visitor/output/list",
                             &out_visitor_data, test_visitor_out_list);
+    output_visitor_test_add("/visitor/output/any",
+                            &out_visitor_data, test_visitor_out_any);
     output_visitor_test_add("/visitor/output/list-qapi-free",
                             &out_visitor_data, test_visitor_out_list_qapi_free);
     output_visitor_test_add("/visitor/output/union-flat",
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 27/32] qom: Don't use 'gen': false for qom-get, qom-set, object-add
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (25 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 26/32] qapi: Introduce a first class 'any' type Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 28/32] qapi-schema: Fix up misleading specification of netdev_add Markus Armbruster
                   ` (5 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

With the previous commit, the generated marshalers just work, and save
us a bit of handwritten code.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 include/monitor/monitor.h |  3 ---
 qapi-schema.json          |  9 +++------
 qmp-commands.hx           |  6 +++---
 qmp.c                     | 21 +++++++--------------
 scripts/qapi.py           |  1 +
 5 files changed, 14 insertions(+), 26 deletions(-)

diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 9aff47e..bc6cb6d 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -42,9 +42,6 @@ void monitor_read_command(Monitor *mon, int show_prompt);
 int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
                           void *opaque);
 
-void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp);
-void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp);
-void qmp_object_add(QDict *qdict, QObject **ret, Error **errp);
 void object_add(const char *type, const char *id, const QDict *qdict,
                 Visitor *v, Error **errp);
 
diff --git a/qapi-schema.json b/qapi-schema.json
index 4342a08..17d2e60 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1698,8 +1698,7 @@
 ##
 { 'command': 'qom-get',
   'data': { 'path': 'str', 'property': 'str' },
-  'returns': '**',
-  'gen': false }
+  'returns': 'any' }
 
 ##
 # @qom-set:
@@ -1716,8 +1715,7 @@
 # Since: 1.2
 ##
 { 'command': 'qom-set',
-  'data': { 'path': 'str', 'property': 'str', 'value': '**' },
-  'gen': false }
+  'data': { 'path': 'str', 'property': 'str', 'value': 'any' } }
 
 ##
 # @set_password:
@@ -2113,8 +2111,7 @@
 # Since: 2.0
 ##
 { 'command': 'object-add',
-  'data': {'qom-type': 'str', 'id': 'str', '*props': '**'},
-  'gen': false }
+  'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} }
 
 ##
 # @object-del:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 41a39fc..76942a6 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -928,7 +928,7 @@ EQMP
     {
         .name       = "object-add",
         .args_type  = "qom-type:s,id:s,props:q?",
-        .mhandler.cmd_new = qmp_object_add,
+        .mhandler.cmd_new = qmp_marshal_object_add,
     },
 
 SQMP
@@ -3550,13 +3550,13 @@ EQMP
     {
         .name       = "qom-set",
 	.args_type  = "path:s,property:s,value:q",
-	.mhandler.cmd_new = qmp_qom_set,
+	.mhandler.cmd_new = qmp_marshal_qom_set,
     },
 
     {
         .name       = "qom-get",
 	.args_type  = "path:s,property:s",
-	.mhandler.cmd_new = qmp_qom_get,
+	.mhandler.cmd_new = qmp_marshal_qom_get,
     },
 
     {
diff --git a/qmp.c b/qmp.c
index f31c6dc..b8b8b5b 100644
--- a/qmp.c
+++ b/qmp.c
@@ -228,12 +228,9 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
     return props;
 }
 
-/* FIXME: teach qapi about how to pass through Visitors */
-void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp)
+void qmp_qom_set(const char *path, const char *property, QObject *value,
+                 Error **errp)
 {
-    const char *path = qdict_get_str(qdict, "path");
-    const char *property = qdict_get_str(qdict, "property");
-    QObject *value = qdict_get(qdict, "value");
     Object *obj;
 
     obj = object_resolve_path(path, NULL);
@@ -246,20 +243,18 @@ void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp)
     object_property_set_qobject(obj, value, property, errp);
 }
 
-void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp)
+QObject *qmp_qom_get(const char *path, const char *property, Error **errp)
 {
-    const char *path = qdict_get_str(qdict, "path");
-    const char *property = qdict_get_str(qdict, "property");
     Object *obj;
 
     obj = object_resolve_path(path, NULL);
     if (!obj) {
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                   "Device '%s' not found", path);
-        return;
+        return NULL;
     }
 
-    *ret = object_property_get_qobject(obj, property, errp);
+    return object_property_get_qobject(obj, property, errp);
 }
 
 void qmp_set_password(const char *protocol, const char *password,
@@ -655,11 +650,9 @@ out:
     object_unref(obj);
 }
 
-void qmp_object_add(QDict *qdict, QObject **ret, Error **errp)
+void qmp_object_add(const char *type, const char *id,
+                    bool has_props, QObject *props, Error **errp)
 {
-    const char *type = qdict_get_str(qdict, "qom-type");
-    const char *id = qdict_get_str(qdict, "id");
-    QObject *props = qdict_get(qdict, "props");
     const QDict *pdict = NULL;
     QmpInputVisitor *qiv;
 
diff --git a/scripts/qapi.py b/scripts/qapi.py
index a3e4b66..ba1d6ec 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -40,6 +40,7 @@ builtin_types = {
 returns_whitelist = [
     # From QMP:
     'human-monitor-command',
+    'qom-get',
     'query-migrate-cache-size',
     'query-tpm-models',
     'query-tpm-types',
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 28/32] qapi-schema: Fix up misleading specification of netdev_add
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (26 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 27/32] qom: Don't use 'gen': false for qom-get, qom-set, object-add Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 29/32] qapi: Pseudo-type '**' is now unused, drop it Markus Armbruster
                   ` (4 subsequent siblings)
  32 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

It doesn't take a 'props' argument, let alone one in the format
"NAME=VALUE,..."

The bogus arguments specification doesn't matter due to 'gen': false.
Clean it up to be incomplete rather than wrong, and document the
incompleteness.

While there, improve netdev_add usage example in the manual: add a
device option to show how it's done.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt |  2 +-
 qapi-schema.json       | 13 +++++++------
 qmp-commands.hx        |  4 +++-
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index c713e3a..a5fccd4 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -463,7 +463,7 @@ try to avoid adding new commands that rely on this, and instead use
 type-safe unions.  For an example of bypass usage:
 
  { 'command': 'netdev_add',
-   'data': {'type': 'str', 'id': 'str', '*props': '**'},
+   'data': {'type': 'str', 'id': 'str'},
    'gen': false }
 
 Normally, the QAPI schema is used to describe synchronous exchanges,
diff --git a/qapi-schema.json b/qapi-schema.json
index 17d2e60..e91e3b9 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2065,11 +2065,12 @@
 #
 # @id: the name of the new network backend
 #
-# @props: #optional a list of properties to be passed to the backend in
-#         the format 'name=value', like 'ifname=tap0,script=no'
+# Additional arguments depend on the type.
 #
-# Notes: The semantics of @props is not well defined.  Future commands will be
-#        introduced that provide stronger typing for backend creation.
+# TODO This command effectively bypasses QAPI completely due to its
+# "additional arguments" business.  It shouldn't have been added to
+# the schema in this form.  It should be qapified properly, or
+# replaced by a properly qapified command.
 #
 # Since: 0.14.0
 #
@@ -2077,8 +2078,8 @@
 #          If @type is not a valid network backend, DeviceNotFound
 ##
 { 'command': 'netdev_add',
-  'data': {'type': 'str', 'id': 'str', '*props': '**'},
-  'gen': false }
+  'data': {'type': 'str', 'id': 'str'},
+  'gen': false }                # so we can get the additional arguments
 
 ##
 # @netdev_del:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 76942a6..b06d74c 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -892,7 +892,9 @@ Arguments:
 
 Example:
 
--> { "execute": "netdev_add", "arguments": { "type": "user", "id": "netdev1" } }
+-> { "execute": "netdev_add",
+     "arguments": { "type": "user", "id": "netdev1",
+                    "dnssearch": "example.org" } }
 <- { "return": {} }
 
 Note: The supported device options are the same ones supported by the '-netdev'
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 29/32] qapi: Pseudo-type '**' is now unused, drop it
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (27 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 28/32] qapi-schema: Fix up misleading specification of netdev_add Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 20:42   ` Eric Blake
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection Markus Armbruster
                   ` (3 subsequent siblings)
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

'gen': false needs to stay for now, because netdev_add is still using
it.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 docs/qapi-code-gen.txt                    | 18 ++++++------------
 scripts/qapi.py                           | 20 ++++----------------
 tests/Makefile                            |  2 +-
 tests/qapi-schema/type-bypass-no-gen.err  |  1 -
 tests/qapi-schema/type-bypass-no-gen.exit |  1 -
 tests/qapi-schema/type-bypass-no-gen.json |  2 --
 tests/qapi-schema/type-bypass-no-gen.out  |  0
 tests/qapi-schema/type-bypass.err         |  0
 tests/qapi-schema/type-bypass.exit        |  1 -
 tests/qapi-schema/type-bypass.json        |  2 --
 tests/qapi-schema/type-bypass.out         |  4 ----
 11 files changed, 11 insertions(+), 40 deletions(-)
 delete mode 100644 tests/qapi-schema/type-bypass-no-gen.err
 delete mode 100644 tests/qapi-schema/type-bypass-no-gen.exit
 delete mode 100644 tests/qapi-schema/type-bypass-no-gen.json
 delete mode 100644 tests/qapi-schema/type-bypass-no-gen.out
 delete mode 100644 tests/qapi-schema/type-bypass.err
 delete mode 100644 tests/qapi-schema/type-bypass.exit
 delete mode 100644 tests/qapi-schema/type-bypass.json
 delete mode 100644 tests/qapi-schema/type-bypass.out

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index a5fccd4..ce32d74 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -111,10 +111,7 @@ and field names within a type, should be all lower case with words
 separated by a hyphen.  However, some existing older commands and
 complex types use underscore; when extending such expressions,
 consistency is preferred over blindly avoiding underscore.  Event
-names should be ALL_CAPS with words separated by underscore.  The
-special string '**' appears for some commands that manually perform
-their own type checking rather than relying on the type-safe code
-produced by the qapi code generators.
+names should be ALL_CAPS with words separated by underscore.
 
 Any name (command, event, type, field, or enum value) beginning with
 "x-" is marked experimental, and may be withdrawn or changed
@@ -453,14 +450,11 @@ which would validate this Client JSON Protocol transaction:
  <= { "return": [ { "value": "one" }, { } ] }
 
 In rare cases, QAPI cannot express a type-safe representation of a
-corresponding Client JSON Protocol command.  In these cases, if the
-command expression includes the key 'gen' with boolean value false,
-then the 'data' or 'returns' member that intends to bypass generated
-type-safety and do its own manual validation should use an inline
-dictionary definition, with a value of '**' rather than a valid type
-name for the keys that the generated code will not validate.  Please
-try to avoid adding new commands that rely on this, and instead use
-type-safe unions.  For an example of bypass usage:
+corresponding Client JSON Protocol command.  You then have to suppress
+generation of a marshalling function by including a key 'gen' with
+boolean value false, and instead write your own function.  Please try
+to avoid adding new commands that rely on this, and instead use
+type-safe unions.  For an example of this usage:
 
  { 'command': 'netdev_add',
    'data': {'type': 'str', 'id': 'str'},
diff --git a/scripts/qapi.py b/scripts/qapi.py
index ba1d6ec..7fe9f30 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -428,15 +428,12 @@ def is_enum(name):
 
 def check_type(expr_info, source, value, allow_array = False,
                allow_dict = False, allow_optional = False,
-               allow_star = False, allow_metas = []):
+               allow_metas = []):
     global all_names
 
     if value is None:
         return
 
-    if allow_star and value == '**':
-        return
-
     # Check if array type for value is okay
     if isinstance(value, list):
         if not allow_array:
@@ -450,10 +447,6 @@ def check_type(expr_info, source, value, allow_array = False,
 
     # Check if type name for value is okay
     if isinstance(value, str):
-        if value == '**':
-            raise QAPIExprError(expr_info,
-                                "%s uses '**' but did not request 'gen':false"
-                                % source)
         if not value in all_names:
             raise QAPIExprError(expr_info,
                                 "%s uses unknown type '%s'"
@@ -479,7 +472,7 @@ def check_type(expr_info, source, value, allow_array = False,
         # Todo: allow dictionaries to represent default values of
         # an optional argument.
         check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
-                   allow_array=True, allow_star=allow_star,
+                   allow_array=True,
                    allow_metas=['built-in', 'union', 'alternate', 'struct',
                                 'enum'])
 
@@ -499,18 +492,16 @@ def check_member_clash(expr_info, base_name, data, source = ""):
 
 def check_command(expr, expr_info):
     name = expr['command']
-    allow_star = expr.has_key('gen')
 
     check_type(expr_info, "'data' for command '%s'" % name,
                expr.get('data'), allow_dict=True, allow_optional=True,
-               allow_metas=['struct'], allow_star=allow_star)
+               allow_metas=['struct'])
     returns_meta = ['union', 'struct']
     if name in returns_whitelist:
         returns_meta += ['built-in', 'alternate', 'enum']
     check_type(expr_info, "'returns' for command '%s'" % name,
                expr.get('returns'), allow_array=True,
-               allow_optional=True, allow_metas=returns_meta,
-               allow_star=allow_star)
+               allow_optional=True, allow_metas=returns_meta)
 
 def check_event(expr, expr_info):
     global events
@@ -1056,7 +1047,6 @@ class QAPISchema(object):
                   ('bool',   'boolean', 'bool',     'false'),
                   ('any',    'value',   'QObject' + pointer_suffix , 'NULL')]:
             self._def_builtin_type(*t)
-        self.entity_dict['**'] = self.lookup_type('any') # TODO drop this alias
 
     def _make_implicit_enum_type(self, name, values):
         name = name + 'Kind'
@@ -1203,8 +1193,6 @@ class QAPISchema(object):
     def visit(self, visitor):
         visitor.visit_begin(self)
         for name in sorted(self.entity_dict.keys()):
-            if self.entity_dict[name].name != name:
-                continue        # ignore alias TODO drop alias and remove
             self.entity_dict[name].visit(visitor)
         visitor.visit_end()
 
diff --git a/tests/Makefile b/tests/Makefile
index fc6169a..68adad4 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -228,7 +228,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
 	bad-type-dict.json double-data.json unknown-expr-key.json \
 	redefined-type.json redefined-command.json redefined-builtin.json \
 	redefined-event.json command-int.json bad-data.json event-max.json \
-	type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
+	type-bypass-bad-gen.json \
 	args-invalid.json \
 	args-array-empty.json args-array-unknown.json args-int.json \
 	args-unknown.json args-member-unknown.json args-member-array.json \
diff --git a/tests/qapi-schema/type-bypass-no-gen.err b/tests/qapi-schema/type-bypass-no-gen.err
deleted file mode 100644
index 20cef0a..0000000
--- a/tests/qapi-schema/type-bypass-no-gen.err
+++ /dev/null
@@ -1 +0,0 @@
-tests/qapi-schema/type-bypass-no-gen.json:2: Member 'arg' of 'data' for command 'unsafe' uses '**' but did not request 'gen':false
diff --git a/tests/qapi-schema/type-bypass-no-gen.exit b/tests/qapi-schema/type-bypass-no-gen.exit
deleted file mode 100644
index d00491f..0000000
--- a/tests/qapi-schema/type-bypass-no-gen.exit
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/tests/qapi-schema/type-bypass-no-gen.json b/tests/qapi-schema/type-bypass-no-gen.json
deleted file mode 100644
index 4feae37..0000000
--- a/tests/qapi-schema/type-bypass-no-gen.json
+++ /dev/null
@@ -1,2 +0,0 @@
-# type bypass only works with 'gen':false
-{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**' }
diff --git a/tests/qapi-schema/type-bypass-no-gen.out b/tests/qapi-schema/type-bypass-no-gen.out
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/qapi-schema/type-bypass.err b/tests/qapi-schema/type-bypass.err
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/qapi-schema/type-bypass.exit b/tests/qapi-schema/type-bypass.exit
deleted file mode 100644
index 573541a..0000000
--- a/tests/qapi-schema/type-bypass.exit
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tests/qapi-schema/type-bypass.json b/tests/qapi-schema/type-bypass.json
deleted file mode 100644
index 48b2137..0000000
--- a/tests/qapi-schema/type-bypass.json
+++ /dev/null
@@ -1,2 +0,0 @@
-# Use of 'gen':false allows bypassing type system
-{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**', 'gen': false }
diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out
deleted file mode 100644
index db2a4e6..0000000
--- a/tests/qapi-schema/type-bypass.out
+++ /dev/null
@@ -1,4 +0,0 @@
-object :obj-unsafe-arg
-    member arg: any optional=False
-command unsafe :obj-unsafe-arg -> any
-   gen=False success_response=True
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (28 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 29/32] qapi: Pseudo-type '**' is now unused, drop it Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-03 20:50   ` Eric Blake
                     ` (2 more replies)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 31/32] qapi-introspect: Map all integer types to 'int' Markus Armbruster
                   ` (2 subsequent siblings)
  32 siblings, 3 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

qapi/introspect.json defines the introspection schema.  It's designed
for QMP introspection, but should do for similar uses, such as QGA.

The introspection schema does not reflect all the rules and
restrictions that apply to QAPI schemata.  A valid QAPI schema has an
introspection value conforming to the introspection schema, but the
converse is not true.

Introspection lowers away a number of schema details, and makes
implicit things explicit:

* The built-in types are declared with their JSON type.

  TODO Should we map all the integer types to just int?

* Implicit type definitions are made explicit, and given
  auto-generated names:

  - Array types, named by appending "List" to the name of their
    element type, like in generated C.

  - The enumeration types implicitly defined by simple union types,
    named by appending "Kind" to the name of their simple union type,
    like in generated C.

  - Types that don't occur in generated C.  Their names start with ':'
    so they don't clash with the user's names.

* All type references are by name.

* The struct and union types are generalized into an object type.

* Base types are flattened.

* Commands take a single argument and return a single result.

  Dictionary argument or list result is an implicit type definition.

  The empty object type is used when a command takes no arguments or
  produces no results.

  The argument is always of object type, but the introspection schema
  doesn't reflect that.

  The 'gen': false directive is omitted as implementation detail.

  The 'success-response' directive is omitted as well for now, even
  though it's not an implementation detail, because it's not used by
  QMP.

* Events carry a single data value.

  Implicit type definition and empty object type use, just like for
  commands.

  The value is of object type, but the introspection schema doesn't
  reflect that.

* Types not used by commands or events are omitted.

  Indirect use counts as use.

* Optional members have a default, which can only be null right now

  Instead of a mandatory "optional" flag, we have an optional default.
  No default means mandatory, default null means optional without
  default value.  Non-null is available for optional with default
  (possible future extension).

* Clients should *not* look up types by name, because type names are
  not ABI.  Look up the command or event you're interested in, then
  follow the references.

  TODO Should we hide the type names to eliminate the temptation?

New generator scripts/qapi-introspect.py computes an introspection
value for its input, and generates a C variable holding it.

It can generate awfully long lines.  Marked TODO.

A new test-qmp-input-visitor test case feeds its result for both
tests/qapi-schema/qapi-schema-test.json and qapi-schema.json to a
QmpInputVisitor to verify it actually conforms to the schema.

New QMP command query-schema takes its return value from that
variable.  Its reply is some 85KiBytes for me right now.

If this turns out to be too much, we have a couple of options:

* We can use shorter names in the JSON.  Not the QMP style.

* Optionally return the sub-schema for commands and events given as
  arguments.

  Right now qmp_query_schema() sends the string literal computed by
  qmp-introspect.py.  To compute sub-schema at run time, we'd have to
  duplicate parts of qapi-introspect.py in C.  Unattractive.

* Let clients cache the output of query-schema.

  It changes only on QEMU upgrades, i.e. rarely.  Provide a command
  query-schema-hash.  Clients can have a cache indexed by hash, and
  re-query the schema only when they don't have it cached.  Even
  simpler: put the hash in the QMP greeting.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 .gitignore                                      |   1 +
 Makefile                                        |   9 +-
 Makefile.objs                                   |   4 +-
 docs/qapi-code-gen.txt                          | 226 +++++++++++++++++++-
 monitor.c                                       |  15 ++
 qapi-schema.json                                |   3 +
 qapi/introspect.json                            | 271 ++++++++++++++++++++++++
 qmp-commands.hx                                 |  17 ++
 scripts/qapi-introspect.py                      | 174 +++++++++++++++
 scripts/qapi.py                                 |  12 +-
 tests/.gitignore                                |   1 +
 tests/Makefile                                  |  10 +-
 tests/qapi-schema/alternate-good.out            |   1 +
 tests/qapi-schema/args-member-array.out         |   1 +
 tests/qapi-schema/comments.out                  |   1 +
 tests/qapi-schema/empty.out                     |   1 +
 tests/qapi-schema/enum-empty.out                |   1 +
 tests/qapi-schema/event-case.out                |   1 +
 tests/qapi-schema/flat-union-reverse-define.out |   1 +
 tests/qapi-schema/ident-with-escape.out         |   1 +
 tests/qapi-schema/include-relpath.out           |   1 +
 tests/qapi-schema/include-repetition.out        |   1 +
 tests/qapi-schema/include-simple.out            |   1 +
 tests/qapi-schema/indented-expr.out             |   1 +
 tests/qapi-schema/qapi-schema-test.out          |   1 +
 tests/qapi-schema/returns-int.out               |   1 +
 tests/test-qmp-input-strict.c                   |  55 +++++
 27 files changed, 801 insertions(+), 11 deletions(-)
 create mode 100644 qapi/introspect.json
 create mode 100644 scripts/qapi-introspect.py

diff --git a/.gitignore b/.gitignore
index cb4b8ec..ce02e3c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,7 @@
 /qapi-visit.[ch]
 /qapi-event.[ch]
 /qmp-commands.h
+/qmp-introspect.[ch]
 /qmp-marshal.c
 /qemu-doc.html
 /qemu-tech.html
diff --git a/Makefile b/Makefile
index 9ce3972..b053195 100644
--- a/Makefile
+++ b/Makefile
@@ -52,6 +52,8 @@ endif
 GENERATED_HEADERS = config-host.h qemu-options.def
 GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
 GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
+GENERATED_HEADERS += qmp-introspect.h
+GENERATED_SOURCES += qmp-introspect.c
 
 GENERATED_HEADERS += trace/generated-events.h
 GENERATED_SOURCES += trace/generated-events.c
@@ -264,7 +266,7 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 
 qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
-               $(SRC_PATH)/qapi/event.json
+               $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
 
 qapi-types.c qapi-types.h :\
 $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
@@ -286,6 +288,11 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
 		$(gen-out-type) -o "." -m $<, \
 		"  GEN   $@")
+qmp-introspect.h qmp-introspect.c :\
+$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
+		$(gen-out-type) -o "." $<, \
+		"  GEN   $@")
 
 QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
 $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
diff --git a/Makefile.objs b/Makefile.objs
index f094eff..b5a9e99 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -1,7 +1,8 @@
 #######################################################################
 # Common libraries for tools and emulators
 stub-obj-y = stubs/
-util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
+util-obj-y = util/ qobject/ qapi/
+util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
 util-obj-y += crypto/
 
 #######################################################################
@@ -83,6 +84,7 @@ common-obj-$(CONFIG_FDT) += device_tree.o
 # qapi
 
 common-obj-y += qmp-marshal.o
+common-obj-y += qmp-introspect.o
 common-obj-y += qmp.o hmp.o
 endif
 
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index ce32d74..1f8f891 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -494,13 +494,195 @@ Resulting in this JSON object:
   "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
 
 
+== Client JSON Protocol introspection ==
+
+Clients of a Client JSON Protocol commonly need to figure out what
+exactly the server (QEMU) supports.
+
+For this purpose, QMP provides introspection via command query-schema.
+QGA currently doesn't support introspection.
+
+query-schema returns a JSON array of SchemaInfo objects.  These
+objects together describe the wire ABI, as defined in the QAPI schema.
+
+Like any other command, query-schema is itself defined in the QAPI
+schema, along with the SchemaInfo type.  This text attempts to give an
+overview how things work.  For details you need to consult the QAPI
+schema.
+
+SchemaInfo objects have common members "name" and "meta-type", and
+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.
+
+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.
+
+QAPI schema definitions not reachable that way are omitted.
+
+The SchemaInfo for a command has meta-type "command", and variant
+members "arg-type" and "ret-type".  The arguments member clients pass
+with a command on the wire must conform to the type named by
+"arg-type", which is always an object type.  The return value the
+server passes in a success response conforms to the type named by
+"ret-type".
+
+If the command takes no arguments, "arg-type" names an object type
+without members.  Likewise, if the command returns nothing, "ret-type"
+names an object type without members.
+
+Example: the SchemaInfo for command query-schema
+
+    { "name": "query-schema", "meta-type": "command",
+      "arg-type": ":empty", "ret-type": "SchemaInfoList" }
+
+    Type ":empty" is an object type without members, and type
+    "SchemaInfoList" is the array of SchemaInfo type.
+
+The SchemaInfo for an event has meta-type "event", and variant member
+"arg-type".  The data member the server passes with an event conforms
+to the type named by "arg-type".  It is always an object type.
+
+If the event carries no additional information, "arg-type" names an
+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.
+
+Example: the SchemaInfo for EVENT_C from section Events
+
+    { "name": "EVENT_C", "meta-type": "event",
+      "arg-type": ":obj-EVENT_C-arg" }
+
+    Type ":obj-EVENT_C-arg" is an implicitly defined object type with
+    the two members from the event's definition.
+
+The SchemaInfo for struct and union types has meta-type "object".
+
+The SchemaInfo for a struct type has variant member "members".
+
+The SchemaInfo for a union type additionally has variant members "tag"
+and "variants".
+
+"members" is a JSON array describing the object's common members.
+Each element is a JSON object with members "name" (the member's name),
+"type" (the name of its type), and optionally "default".  The member
+is optional if "default" is present.  Currently, "default" can only
+have value null.  Other values are reserved for future extensions.
+
+Example: the SchemaInfo for MyType from section Struct types
+
+    { "name": "MyType", "meta-type": "object",
+      "members": [
+          { "name": "member1", "type": "str" },
+          { "name": "member2", "type": "int" },
+          { "name": "member3", "type": "str", "default": null } ] }
+
+"tag" is the name of the common member serving as type tag.
+"variants" is a JSON array describing the object's variant members.
+Each element is a JSON object with members "case" (the value of type
+tag this element applies to) and "type" (the name of an object type
+that provides the variant members for this type tag value).
+
+Example: the SchemaInfo for flat union BlockdevOptions from section
+Union types
+
+    { "name": "BlockdevOptions", "meta-type": "object",
+      "members": [
+          { "name": "driver", "type": "BlockdevDriver" },
+          { "name": "readonly", "type": "bool"} ],
+      "tag": "driver",
+      "variants": [
+          { "case": "file", "type": "FileOptions" },
+          { "case": "qcow2", "type": "Qcow2Options" } ] }
+
+Note that base types are "flattened": its members are included in the
+"members" array.
+
+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.
+
+Example: the SchemaInfo for simple union BlockdevOptions from section
+Union types
+
+    { "name": "BlockdevOptions", "meta-type": "object",
+      "members": [
+          { "name": "kind", "type": "BlockdevOptionsKind" } ],
+      "tag": "type",
+      "variants": [
+          { "case": "file", "type": ":obj-FileOptions-wrapper" },
+          { "case": "qcow2", "type": ":obj-Qcow2Options-wrapper" } ] }
+
+    Enumeration type "BlockdevOptionsKind" and the object types
+    ":obj-FileOptions-wrapper", ":obj-Qcow2Options-wrapper" are
+    implicitly defined.
+
+The SchemaInfo for an alternate type has meta-type "alternate", and
+variant member "members".  "members" is a JSON array.  Each element is
+a JSON object with member "type", which names a type.  Values of the
+alternate type conform to exactly one of its member types.
+
+Example: the SchemaInfo for BlockRef from section Alternate types
+
+    { "name": "BlockRef", "meta-type": "alternate",
+      "members": [
+          { "type": "BlockdevOptions" },
+          { "type": "str" } ] }
+
+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.
+
+Example: the SchemaInfo for ['str']
+
+    { "name": "strList", "meta-type": "array",
+      "element-type": "str" }
+
+The SchemaInfo for an enumeration type has meta-type "enum" and
+variant member "values".
+
+Example: the SchemaInfo for MyEnum from section Enumeration types
+
+    { "name": "MyEnum", "meta-type": "enum",
+      "values": [ "value1", "value2", "value3" ] }
+
+The SchemaInfo for a built-in type has the same name as the type in
+the QAPI schema (see section Built-in Types).  It has variant member
+"json-type" that shows how values of this type are encoded on the
+wire.
+
+Example: the SchemaInfo for str
+
+    { "name": "str", "meta-type": "builtin", "json-type": "string" }
+
+As explained above, type names are not part of the wire ABI.  Not even
+the names of built-in types.  Clients should examine member
+"json-type" instead of hard-coding names of built-in types.
+
+
 == Code generation ==
 
-Schemas are fed into 3 scripts to generate all the code/files that, paired
-with the core QAPI libraries, comprise everything required to take JSON
-commands read in by a Client JSON Protocol server, unmarshal the arguments into
-the underlying C types, call into the corresponding C function, and map the
-response back to a Client JSON Protocol response to be returned to the user.
+Schemas are fed into four scripts to generate all the code/files that,
+paired with the core QAPI libraries, comprise everything required to
+take JSON commands read in by a Client JSON Protocol server, unmarshal
+the arguments into the underlying C types, call into the corresponding
+C function, and map the response back to a Client JSON Protocol
+response to be returned to the user.
 
 As an example, we'll use the following schema, which describes a single
 complex user-defined type (which will produce a C struct, along with a list
@@ -848,3 +1030,37 @@ Example:
     extern const char *const example_QAPIEvent_lookup[];
 
     #endif
+
+=== scripts/qapi-introspect.py ===
+
+Used to generate the introspection C code for a schema. The following
+files are created:
+
+$(prefix)qmp-introspect.c - Defines a string holding a JSON
+                            description of the schema.
+$(prefix)qmp-introspect.h - Declares the above string.
+
+Example:
+
+    $ python scripts/qapi-introspect.py --output-dir="qapi-generated"
+    --prefix="example-" example-schema.json
+    $ cat qapi-generated/example-qmp-introspect.c
+[Uninteresting stuff omitted...]
+
+    const char example_qmp_schema_json[] = "["
+        "{\"arg-type\": \":empty\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
+        "{\"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\"}]";
+    $ cat qapi-generated/example-qmp-introspect.h
+[Uninteresting stuff omitted...]
+
+    #ifndef EXAMPLE_QMP_INTROSPECT_H
+    #define EXAMPLE_QMP_INTROSPECT_H
+
+    extern const char example_qmp_schema_json[];
+
+    #endif
diff --git a/monitor.c b/monitor.c
index f654607..47494c2 100644
--- a/monitor.c
+++ b/monitor.c
@@ -74,6 +74,7 @@
 #include "block/qapi.h"
 #include "qapi/qmp-event.h"
 #include "qapi-event.h"
+#include "qmp-introspect.h"
 #include "sysemu/block-backend.h"
 
 /* for hmp_info_irq/pic */
@@ -924,6 +925,20 @@ EventInfoList *qmp_query_events(Error **errp)
     return ev_list;
 }
 
+/*
+ * Minor hack: generated marshalling suppressed for this command
+ * ('gen': false in the schema) so we can parse the JSON string
+ * directly into QObject instead of first parsing it with
+ * visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it
+ * to QObject with generated output marshallers, every time.  Instead,
+ * we do it in test-qmp-input-visitor.c, just to make sure
+ * qapi-introspect.py's output actually conforms to the schema.
+ */
+static void qmp_query_schema(QDict *qdict, QObject **ret_data, Error **errp)
+{
+    *ret_data = qobject_from_json(qmp_schema_json);
+}
+
 /* set the current CPU defined by the user */
 int monitor_set_cpu(int cpu_index)
 {
diff --git a/qapi-schema.json b/qapi-schema.json
index e91e3b9..af19810 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -14,6 +14,9 @@
 # Tracing commands
 { 'include': 'qapi/trace.json' }
 
+# QAPI introspection
+{ 'include': 'qapi/introspect.json' }
+
 ##
 # @LostTickPolicy:
 #
diff --git a/qapi/introspect.json b/qapi/introspect.json
new file mode 100644
index 0000000..171baba
--- /dev/null
+++ b/qapi/introspect.json
@@ -0,0 +1,271 @@
+# -*- Mode: Python -*-
+#
+# QAPI/QMP introspection
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# Authors:
+#  Markus Armbruster <armbru@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+##
+# @query-schema
+#
+# Command query-schema exposes the QMP wire ABI as an array of
+# SchemaInfo.  This lets QMP clients figure out what commands and
+# events are available in this QEMU, and their parameters and results.
+#
+# Returns: array of @SchemaInfo, where each element describes an
+# entity in the ABI: command, event, type, ...
+#
+# Note: the QAPI schema is also used to help define *internal*
+# interfaces, by defining QAPI types.  These are not part of the QMP
+# wire ABI, and therefore not returned by this command.
+#
+# Since: 2.5
+##
+{ 'command': 'query-schema',
+  'returns': [ 'SchemaInfo' ],
+  'gen': false }                # just to simplify qmp_query_json()
+
+##
+# @SchemaMetaType
+#
+# This is a @SchemaInfo's meta type, i.e. the kind of entity it
+# describes.
+#
+# @builtin: a predefined type such as 'int' or 'bool'.
+#
+# @enum: an enumeration type
+#
+# @array: an array type
+#
+# @object: an object type (struct or union)
+#
+# @alternate: an alternate type
+#
+# @command: a QMP command
+#
+# @event: a QMP event
+#
+# Since: 2.5
+##
+{ 'enum': 'SchemaMetaType',
+  'data': [ 'builtin', 'enum', 'array', 'object', 'alternate',
+            'command', 'event' ] }
+
+##
+# @SchemaInfoBase
+#
+# Members common to any @SchemaInfo.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoBase',
+  'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } }
+
+##
+# @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.
+#
+# 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.
+#
+# Since: 2.5
+##
+{ 'union': 'SchemaInfo',
+  'base': 'SchemaInfoBase',
+  'discriminator': 'meta-type',
+  'data': {
+      'builtin': 'SchemaInfoBuiltin',
+      'enum': 'SchemaInfoEnum',
+      'array': 'SchemaInfoArray',
+      'object': 'SchemaInfoObject',
+      'alternate': 'SchemaInfoAlternate',
+      'command': 'SchemaInfoCommand',
+      'event': 'SchemaInfoEvent' } }
+
+##
+# @SchemaInfoBuiltin
+#
+# Additional SchemaInfo members for meta-type 'builtin'.
+#
+# @json-type: the JSON type used for this type on the wire.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoBuiltin',
+  'data': { 'json-type': 'JSONType' } }
+
+##
+# @JSONType
+#
+# The four primitive and two structured types according to RFC 7159
+# section 1, plus 'int' (split off 'number'), plus the obvious top
+# type 'value'.
+#
+# Since: 2.5
+##
+{ 'enum': 'JSONType',
+  'data': [ 'string', 'number', 'int', 'boolean', 'null',
+            'object', 'array', 'value' ] }
+
+##
+# @SchemaInfoEnum
+#
+# Additional SchemaInfo members for meta-type 'enum'.
+#
+# @values: the enumeration type's values.
+#
+# Values of this type are JSON string on the wire.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoEnum',
+  'data': { 'values': ['str'] } }
+
+##
+# @SchemaInfoArray
+#
+# Additional SchemaInfo members for meta-type 'array'.
+#
+# @element-type: the array type's element type.
+#
+# Values of this type are JSON array on the wire.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoArray',
+  'data': { 'element-type': 'str' } }
+
+##
+# @SchemaInfoObject
+#
+# Additional SchemaInfo members for meta-type 'object'.
+#
+# @members: the object type's (non-variant) members.
+#
+# @tag: #optional the name of the member serving as type tag.  An
+# element of @members with this name must exist.
+#
+# @variants: #optional variant members, i.e. additional members that
+# depend on the type tag's value.  Present exactly when @tag is
+# present.
+#
+# Values of this type are JSON object on the wire.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoObject',
+  'data': { 'members': [ 'SchemaInfoObjectMember' ],
+            '*tag': 'str',
+            '*variants': [ 'SchemaInfoObjectVariant' ] } }
+
+##
+# @SchemaInfoObjectMember
+#
+# An object member.
+#
+# @name: the member's name, as defined in the QAPI schema.
+#
+# @type: the name of the member's type.
+#
+# @default: #optional default when used as command parameter.
+#           If absent, the parameter is mandatory.
+#           If present, the value must be null.  The parameter is
+#           optional, and behavior when it's missing is not specified
+#           here.
+#           Future extension: if present and non-null, the parameter
+#	    is optional, and defaults to this value.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoObjectMember',
+  'data': { 'name': 'str', 'type': 'str', '*default': 'any' } }
+# @default's type must be null or match @type
+
+##
+# @SchemaInfoObjectVariant
+#
+# The variant members for a value of the type tag.
+#
+# @case: a value of the type tag.
+#
+# @type: the name of the object type that provides the variant members
+# when the type tag has value @case.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoObjectVariant',
+  'data': { 'case': 'str', 'type': 'str' } }
+
+##
+# @SchemaInfoAlternate
+#
+# Additional SchemaInfo members for meta-type 'alternate'.
+#
+# @members: the alternate type's members.  The members wire encoding
+# is distinct, see docs/qapi-code-gen.txt section Alternate types.
+#
+# On the wire, this can be any of the members.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoAlternate',
+  'data': { 'members': [ 'SchemaInfoAlternateMember' ] } }
+
+##
+# @SchemaInfoAlternateMember
+#
+# An alternate member.
+#
+# @type: the name of the member's type.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoAlternateMember',
+  'data': { 'type': 'str' } }
+
+##
+# @SchemaInfoCommand
+#
+# Additional SchemaInfo members for meta-type 'command'.
+#
+# @arg-type: the name of the object type that provides the command's
+# parameters.
+#
+# @ret-type: the name of the command's result type.
+#
+# TODO @success-response (currently irrelevant, because it's QGA, not QMP)
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoCommand',
+  'data': { 'arg-type': 'str', 'ret-type': 'str' } }
+
+##
+# @SchemaInfoEvent
+#
+# Additional SchemaInfo members for meta-type 'event'.
+#
+# @arg-type: the name of the object type that provides the event's
+# parameters.
+#
+# Since: 2.5
+##
+{ 'struct': 'SchemaInfoEvent',
+  'data': { 'arg-type': 'str' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index b06d74c..6232ca0 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2168,6 +2168,23 @@ EQMP
     },
 
 SQMP
+query-schema
+------------
+
+Return the QMP wire schema.  The returned value is a json-array of
+named schema entities.  Entities are commands, events and various
+types.  See docs/qapi-code-gen.txt for information on their structure
+and intended use.
+
+EQMP
+
+    {
+        .name       = "query-schema",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_query_schema,
+    },
+
+SQMP
 query-chardev
 -------------
 
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
new file mode 100644
index 0000000..4bcffc4
--- /dev/null
+++ b/scripts/qapi-introspect.py
@@ -0,0 +1,174 @@
+#
+# QAPI introspection generator
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# Authors:
+#  Markus Armbruster <armbru@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+
+from qapi import *
+import string
+
+# Caveman's json.dumps() replacement (we're stuck at 2.4)
+# TODO try to use json.dumps() once we get unstuck
+def to_json(obj, level=0):
+    if obj == None:
+        ret = 'null'
+    elif isinstance(obj, str):
+        ret = '"' + obj.replace('"', r'\"') + '"'
+    elif isinstance(obj, list):
+        elts = [to_json(elt, level + 1)
+                for elt in obj]
+        ret = '[' + ', '.join(elts) + ']'
+    elif isinstance(obj, dict):
+        elts = ['"%s": %s' % (key, to_json(obj[key], level + 1))
+                for key in sorted(obj.keys())]
+        ret = '{' + ', '.join(elts) + '}'
+    else:
+        assert False                # not implemented
+    if level == 1:
+        ret = '\n' + ret
+    return ret
+
+def to_c_string(string):
+    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
+
+class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
+    def __init__(self):
+        self.defn = None
+        self.decl = None
+        self.schema = None
+        self.jsons = None
+        self.used_types = None
+
+    def visit_begin(self, schema):
+        self.schema = schema
+        self.jsons = []
+        self.used_types = []
+        return QAPISchemaType   # don't visit types for now
+
+    def visit_end(self):
+        # visit the types that are actually used
+        for typ in self.used_types:
+            typ.visit(self)
+        self.jsons.sort()
+        # generate C
+        # TODO can generate awfully long lines
+        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')
+        c_string = '\n    '.join([to_c_string(line) for line in lines])
+        self.defn = mcgen('''
+const char %(c_name)s[] = %(c_string)s;
+''',
+                          c_name=c_name(name),
+                          c_string=c_string)
+        self.schema = None
+        self.jsons = None
+        self.used_types = None
+
+    def _use_type(self, typ):
+        if typ not in self.used_types:
+            self.used_types.append(typ)
+        return typ.name
+
+    def _gen_json(self, name, mtype, obj):
+        obj['name'] = name
+        obj['meta-type'] = mtype
+        self.jsons.append(obj)
+
+    def _gen_member(self, member):
+        ret = {'name': member.name, 'type': self._use_type(member.type)}
+        if member.optional:
+            ret['default'] = None
+        return ret
+
+    def _gen_variants(self, tag_name, variants):
+        return {'tag': tag_name or 'type',
+                'variants': [self._gen_variant(v) for v in variants]}
+
+    def _gen_variant(self, variant):
+        return {'case': variant.name, 'type': self._use_type(variant.type)}
+
+    def visit_builtin_type(self, name, info, json_type):
+        self._gen_json(name, 'builtin', {'json-type': json_type})
+
+    def visit_enum_type(self, name, info, values):
+        self._gen_json(name, 'enum', {'values': values})
+
+    def visit_array_type(self, name, info, element_type):
+        self._gen_json(name, 'array',
+                       {'element-type': self._use_type(element_type)})
+
+    def visit_object_type_flat(self, name, info, members, variants):
+        obj = {'members': [self._gen_member(m) for m in members]}
+        if variants:
+            obj.update(self._gen_variants(variants.tag_name,
+                                          variants.variants))
+        self._gen_json(name, 'object', obj)
+
+    def visit_alternate_type(self, name, info, variants):
+        self._gen_json(name, 'alternate',
+                       {'members': [{'type': self._use_type(m.type)}
+                                    for m in variants.variants]})
+
+    def visit_command(self, name, info, arg_type, ret_type,
+                      gen, success_response):
+        arg_type = arg_type or self.schema.the_empty_object_type
+        ret_type = ret_type or self.schema.the_empty_object_type
+        self._gen_json(name, 'command',
+                       {'arg-type': self._use_type(arg_type),
+                        'ret-type': self._use_type(ret_type)})
+
+    def visit_event(self, name, info, arg_type):
+        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()
+
+c_comment = '''
+/*
+ * QAPI/QMP schema introspection
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+'''
+h_comment = '''
+/*
+ * QAPI/QMP schema introspection
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+'''
+
+(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
+                            'qmp-introspect.c', 'qmp-introspect.h',
+                            c_comment, h_comment)
+
+fdef.write(mcgen('''
+#include "%(prefix)sqmp-introspect.h"
+
+''',
+                 prefix=prefix))
+
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenIntrospectVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
+
+close_output(fdef, fdecl)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 7fe9f30..8c651f9 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -773,6 +773,8 @@ class QAPISchemaVisitor(object):
         pass
     def visit_object_type(self, name, info, base, members, variants):
         pass
+    def visit_object_type_flat(self, name, info, members, variants):
+        pass
     def visit_alternate_type(self, name, info, variants):
         pass
     def visit_command(self, name, info, arg_type, ret_type,
@@ -896,6 +898,8 @@ class QAPISchemaObjectType(QAPISchemaType):
     def visit(self, visitor):
         visitor.visit_object_type(self.name, self.info,
                                   self.base, self.local_members, self.variants)
+        visitor.visit_object_type_flat(self.name, self.info,
+                                       self.members, self.variants)
 
 class QAPISchemaObjectTypeMember(object):
     def __init__(self, name, typ, optional):
@@ -1047,6 +1051,9 @@ class QAPISchema(object):
                   ('bool',   'boolean', 'bool',     'false'),
                   ('any',    'value',   'QObject' + pointer_suffix , 'NULL')]:
             self._def_builtin_type(*t)
+        self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
+                                                          [], None)
+        self._def_entity(self.the_empty_object_type)
 
     def _make_implicit_enum_type(self, name, values):
         name = name + 'Kind'
@@ -1191,9 +1198,10 @@ class QAPISchema(object):
             ent.check(self)
 
     def visit(self, visitor):
-        visitor.visit_begin(self)
+        ignore = visitor.visit_begin(self)
         for name in sorted(self.entity_dict.keys()):
-            self.entity_dict[name].visit(visitor)
+            if not ignore or not isinstance(self.entity_dict[name], ignore):
+                self.entity_dict[name].visit(visitor)
         visitor.visit_end()
 
 #
diff --git a/tests/.gitignore b/tests/.gitignore
index ccc92e4..e476d52 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -28,6 +28,7 @@ test-qmp-commands.h
 test-qmp-event
 test-qmp-input-strict
 test-qmp-input-visitor
+test-qmp-introspect.[ch]
 test-qmp-marshal.c
 test-qmp-output-visitor
 test-rcu-list
diff --git a/tests/Makefile b/tests/Makefile
index 68adad4..92d682f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -263,7 +263,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
 	struct-base-clash.json struct-base-clash-deep.json )
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
-		     tests/test-qmp-commands.h tests/test-qapi-event.h
+	tests/test-qmp-commands.h tests/test-qapi-event.h \
+	tests/test-qmp-introspect.h
 
 test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
 	tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \
@@ -276,7 +277,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
 	tests/rcutorture.o tests/test-rcu-list.o
 
 test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
-		  tests/test-qapi-event.o
+		  tests/test-qapi-event.o tests/test-qmp-introspect.o
 
 $(test-obj-y): QEMU_INCLUDES += -Itests
 QEMU_CFLAGS += -I$(SRC_PATH)/tests
@@ -337,6 +338,11 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-eve
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
 		$(gen-out-type) -o tests -p "test-" $<, \
 		"  GEN   $@")
+tests/test-qmp-introspect.c tests/test-qmp-introspect.h :\
+$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
+		$(gen-out-type) -o tests -p "test-" $<, \
+		"  GEN   $@")
 
 tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
diff --git a/tests/qapi-schema/alternate-good.out b/tests/qapi-schema/alternate-good.out
index 3d765ff..65af727 100644
--- a/tests/qapi-schema/alternate-good.out
+++ b/tests/qapi-schema/alternate-good.out
@@ -1,3 +1,4 @@
+object :empty
 alternate Alt
     case value: int
     case string: Enum
diff --git a/tests/qapi-schema/args-member-array.out b/tests/qapi-schema/args-member-array.out
index b67384c..b3b92df 100644
--- a/tests/qapi-schema/args-member-array.out
+++ b/tests/qapi-schema/args-member-array.out
@@ -1,3 +1,4 @@
+object :empty
 object :obj-okay-arg
     member member1: intList optional=False
     member member2: defList optional=False
diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out
index 6161b90..9e2c656 100644
--- a/tests/qapi-schema/comments.out
+++ b/tests/qapi-schema/comments.out
@@ -1 +1,2 @@
+object :empty
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out
index e69de29..272b161 100644
--- a/tests/qapi-schema/empty.out
+++ b/tests/qapi-schema/empty.out
@@ -0,0 +1 @@
+object :empty
diff --git a/tests/qapi-schema/enum-empty.out b/tests/qapi-schema/enum-empty.out
index e09b00f..a449d45 100644
--- a/tests/qapi-schema/enum-empty.out
+++ b/tests/qapi-schema/enum-empty.out
@@ -1 +1,2 @@
+object :empty
 enum MyEnum []
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index b5ae4c2..cdfd264 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1 +1,2 @@
+object :empty
 event oops None
diff --git a/tests/qapi-schema/flat-union-reverse-define.out b/tests/qapi-schema/flat-union-reverse-define.out
index 477fb31..a5a9134 100644
--- a/tests/qapi-schema/flat-union-reverse-define.out
+++ b/tests/qapi-schema/flat-union-reverse-define.out
@@ -1,3 +1,4 @@
+object :empty
 object TestBase
     member enum1: TestEnum optional=False
 enum TestEnum ['value1', 'value2']
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index 9577d1b..f4542b1 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -1,3 +1,4 @@
+object :empty
 object :obj-fooA-arg
     member bar1: str optional=False
 command fooA :obj-fooA-arg -> None
diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out
index 6161b90..9e2c656 100644
--- a/tests/qapi-schema/include-relpath.out
+++ b/tests/qapi-schema/include-relpath.out
@@ -1 +1,2 @@
+object :empty
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out
index 6161b90..9e2c656 100644
--- a/tests/qapi-schema/include-repetition.out
+++ b/tests/qapi-schema/include-repetition.out
@@ -1 +1,2 @@
+object :empty
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out
index 6161b90..9e2c656 100644
--- a/tests/qapi-schema/include-simple.out
+++ b/tests/qapi-schema/include-simple.out
@@ -1 +1,2 @@
+object :empty
 enum Status ['good', 'bad', 'ugly']
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index c5af55a..226d300 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,3 +1,4 @@
+object :empty
 command eins None -> None
    gen=True success_response=True
 command zwei None -> None
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index fbb590f..a9c87a0 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,3 +1,4 @@
+object :empty
 object :obj-EVENT_C-arg
     member a: int optional=True
     member b: UserDefOne optional=True
diff --git a/tests/qapi-schema/returns-int.out b/tests/qapi-schema/returns-int.out
index 1ac3e1e..a2da259 100644
--- a/tests/qapi-schema/returns-int.out
+++ b/tests/qapi-schema/returns-int.out
@@ -1,2 +1,3 @@
+object :empty
 command guest-get-time None -> int
    gen=True success_response=True
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index a2ae786..53a7693 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -19,6 +19,9 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qapi/qmp/types.h"
+#include "test-qmp-introspect.h"
+#include "qmp-introspect.h"
+#include "qapi-visit.h"
 
 typedef struct TestInputVisitorData {
     QObject *obj;
@@ -62,6 +65,30 @@ Visitor *validate_test_init(TestInputVisitorData *data,
     return v;
 }
 
+/* similar to validate_test_init(), but does not expect a string
+ * literal/format json_string argument and so can be used for
+ * programatically generated strings (and we can't pass in programatically
+ * generated strings via %s format parameters since qobject_from_jsonv()
+ * will wrap those in double-quotes and treat the entire object as a
+ * string)
+ */
+static Visitor *validate_test_init_raw(TestInputVisitorData *data,
+                                       const char *json_string)
+{
+    Visitor *v;
+
+    data->obj = qobject_from_json(json_string);
+    g_assert(data->obj != NULL);
+
+    data->qiv = qmp_input_visitor_new_strict(data->obj);
+    g_assert(data->qiv != NULL);
+
+    v = qmp_input_get_visitor(data->qiv);
+    g_assert(v != NULL);
+
+    return v;
+}
+
 typedef struct TestStruct
 {
     int64_t integer;
@@ -293,6 +320,32 @@ static void test_validate_fail_alternate(TestInputVisitorData *data,
     qapi_free_UserDefAlternate(tmp);
 }
 
+static void do_test_validate_qmp_introspect(TestInputVisitorData *data,
+                                            const char *schema_json)
+{
+    SchemaInfoList *schema = NULL;
+    Error *err = NULL;
+    Visitor *v;
+
+    v = validate_test_init_raw(data, schema_json);
+
+    visit_type_SchemaInfoList(v, &schema, NULL, &err);
+    if (err) {
+        fprintf(stderr, "%s", error_get_pretty(err));
+    }
+    g_assert(!err);
+    g_assert(schema);
+
+    qapi_free_SchemaInfoList(schema);
+}
+
+static void test_validate_qmp_introspect(TestInputVisitorData *data,
+                                           const void *unused)
+{
+    do_test_validate_qmp_introspect(data, test_qmp_schema_json);
+    do_test_validate_qmp_introspect(data, qmp_schema_json);
+}
+
 static void validate_test_add(const char *testpath,
                                TestInputVisitorData *data,
                                void (*test_func)(TestInputVisitorData *data, const void *user_data))
@@ -333,6 +386,8 @@ int main(int argc, char **argv)
                       &testdata, test_validate_fail_alternate);
     validate_test_add("/visitor/input-strict/fail/union-native-list",
                       &testdata, test_validate_fail_union_native_list);
+    validate_test_add("/visitor/input-strict/pass/qmp-introspect",
+                      &testdata, test_validate_qmp_introspect);
 
     g_test_run();
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 31/32] qapi-introspect: Map all integer types to 'int'
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (29 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-04 14:00   ` Eric Blake
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 32/32] qapi-introspect: Hide type names Markus Armbruster
  2015-09-04 12:57 ` [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Marc-André Lureau
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

How many bits we use internally is an implementation detail.  It could
be pressed into external interface service as very approximate range
information, but that's probably a bad idea.  If we need range
information, we better do it properly.

Reduces output of query-schema by a negligible 0.5 out of 85KiB.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 docs/qapi-code-gen.txt     | 11 ++++++++---
 scripts/qapi-introspect.py |  7 +++++++
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 1f8f891..a079b51 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -662,14 +662,19 @@ Example: the SchemaInfo for MyEnum from section Enumeration types
       "values": [ "value1", "value2", "value3" ] }
 
 The SchemaInfo for a built-in type has the same name as the type in
-the QAPI schema (see section Built-in Types).  It has variant member
-"json-type" that shows how values of this type are encoded on the
-wire.
+the QAPI schema (see section Built-in Types), with one exception
+detailed below.  It has variant member "json-type" that shows how
+values of this type are encoded on the wire.
 
 Example: the SchemaInfo for str
 
     { "name": "str", "meta-type": "builtin", "json-type": "string" }
 
+The QAPI schema supports a number of integer types that only differ in
+how they map to C.  They are identical as far as SchemaInfo is
+concerned.  Therefore, they get all mapped to a single type "int" in
+SchemaInfo.
+
 As explained above, type names are not part of the wire ABI.  Not even
 the names of built-in types.  Clients should examine member
 "json-type" instead of hard-coding names of built-in types.
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 4bcffc4..2f6a4c6 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -74,6 +74,13 @@ const char %(c_name)s[] = %(c_string)s;
         self.used_types = None
 
     def _use_type(self, typ):
+        # Map the various integer types to plain int
+        if typ.json_type() == 'int':
+            typ = self.schema.lookup_type('int')
+        elif isinstance(typ, QAPISchemaArrayType) \
+             and typ.element_type.json_type() == 'int':
+            typ = self.schema.lookup_type('intList')
+        # Add type to work queue if new
         if typ not in self.used_types:
             self.used_types.append(typ)
         return typ.name
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 32/32] qapi-introspect: Hide type names
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (30 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 31/32] qapi-introspect: Map all integer types to 'int' Markus Armbruster
@ 2015-09-03 14:30 ` Markus Armbruster
  2015-09-04 14:07   ` Eric Blake
  2015-09-04 12:57 ` [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Marc-André Lureau
  32 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-03 14:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: mdroth

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>
---
 docs/qapi-code-gen.txt     | 36 +++++++++++++++++-------------------
 qapi/introspect.json       | 11 +++--------
 scripts/qapi-introspect.py | 41 +++++++++++++++++++++++++++++++++++------
 3 files changed, 55 insertions(+), 33 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index a079b51..94fc296 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -516,13 +516,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 document use
+meaningful type names instead.
+
+To examine a type, start with a the command or event using it, then
+follow references by name.
 
 QAPI schema definitions not reachable that way are omitted.
 
@@ -554,8 +557,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
 
@@ -609,12 +611,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
@@ -645,8 +644,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']
 
@@ -1053,13 +1051,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...]
 
diff --git a/qapi/introspect.json b/qapi/introspect.json
index 171baba..46e78a1 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -70,17 +70,12 @@
 # @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.
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 2f6a4c6..9cc101d 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -37,32 +37,37 @@ def to_c_string(string):
     return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
 
 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;
@@ -72,6 +77,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
@@ -83,9 +96,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)
@@ -137,7 +157,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 = '''
 /*
@@ -173,7 +202,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)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 01/32] qapi: Rename class QAPISchema to QAPISchemaParser
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 01/32] qapi: Rename class QAPISchema to QAPISchemaParser Markus Armbruster
@ 2015-09-03 15:55   ` Daniel P. Berrange
  0 siblings, 0 replies; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:55 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Thu, Sep 03, 2015 at 04:29:52PM +0200, Markus Armbruster wrote:
> I want to name a new class QAPISchema.
> 
> While there, make it a new-style class.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation Markus Armbruster
@ 2015-09-03 16:11   ` Daniel P. Berrange
  2015-09-04  7:12     ` Markus Armbruster
  2015-09-04  3:05   ` Eric Blake
  2015-09-04 12:34   ` Eric Blake
  2 siblings, 1 reply; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 16:11 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Thu, Sep 03, 2015 at 04:29:53PM +0200, Markus Armbruster wrote:
> The QAPI code generators work with a syntax tree (nested dictionaries)
> plus a few symbol tables (also dictionaries) on the side.
> 
> They have clearly outgrown these simple data structures.  There's lots
> of rummaging around in dictionaries, and information is recomputed on
> the fly.  For the work I'm going to do, I want more clearly defined
> and more convenient interfaces.
> 
> Going forward, I also want less coupling between the back-ends and the
> syntax tree, to make messing with the syntax easier.
> 
> Create a bunch of classes to represent QAPI schemata.
> 
> Have the QAPISchema initializer call the parser, then walk the syntax
> tree to create the new internal representation, and finally perform
> semantic analysis.
> 
> Shortcut: the semantic analysis still relies on existing check_exprs()
> to do the actual semantic checking.  All this code needs to move into
> the classes.  Mark as TODO.
> 
> Catching name collisions in generated code would be nice.  Mark as
> TODO.
> 
> We generate array types eagerly, even though most of them aren't used.
> Mark as TODO.
> 
> Nothing uses the new intermediate representation just yet, thus no
> change to generated files.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>

A few comments inline, but nothing worth blocking on, just style.


> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index a0165dd..c42bea1 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -747,15 +749,355 @@ def check_exprs(exprs):
>          else:
>              assert False, 'unexpected meta type'
>  
> -    return map(lambda expr_elem: expr_elem['expr'], exprs)
> -
> -def parse_schema(fname):
> -    try:
> -        schema = QAPISchemaParser(open(fname, "r"))
> -        return check_exprs(schema.exprs)
> -    except (QAPISchemaError, QAPIExprError), e:
> -        print >>sys.stderr, e
> -        exit(1)
> +    return exprs
> +
> +#
> +# Schema compiler frontend
> +#
> +
> +class QAPISchemaEntity(object):
> +    def __init__(self, name, info):
> +        assert isinstance(name, str)
> +        self.name = name
> +        self.info = info
> +    def check(self, schema):
> +        pass

>From a style POV, I'd recommend having a single blank
line before each new function def, to make things more
readable. I won't nack the patch for that, but if you
have need to rebase & post a new version of this series
I think it'd be nice to add the the extra blank lines
when defining all these classes.

Python has a nice tool called pep8 which can apply a
configurable bunch of  style checks, it is probably
worth someone wiring it up to make check in QEMU, since
we have an increasing amount of python code. An exercise
for a motivated reader.....

> +
> +class QAPISchemaType(QAPISchemaEntity):
> +    pass
> +
> +class QAPISchemaBuiltinType(QAPISchemaType):
> +    def __init__(self, name):
> +        QAPISchemaType.__init__(self, name, None)
> +
> +class QAPISchemaEnumType(QAPISchemaType):
> +    def __init__(self, name, info, values):
> +        QAPISchemaType.__init__(self, name, info)
> +        for v in values:
> +            assert isinstance(v, str)
> +        self.values = values
> +    def check(self, schema):
> +        assert len(set(self.values)) == len(self.values)
> +
> +class QAPISchemaArrayType(QAPISchemaType):
> +    def __init__(self, name, info, element_type):
> +        QAPISchemaType.__init__(self, name, info)
> +        assert isinstance(element_type, str)
> +        self.element_type_name = element_type
> +        self.element_type = None
> +    def check(self, schema):
> +        self.element_type = schema.lookup_type(self.element_type_name)
> +        assert self.element_type
> +
> +class QAPISchemaObjectType(QAPISchemaType):
> +    def __init__(self, name, info, base, local_members, variants):
> +        QAPISchemaType.__init__(self, name, info)
> +        assert base == None or isinstance(base, str)
> +        for m in local_members:
> +            assert isinstance(m, QAPISchemaObjectTypeMember)
> +        assert variants == None \
> +            or isinstance(variants, QAPISchemaObjectTypeVariants)
> +        self.base_name = base
> +        self.base = None
> +        self.local_members = local_members
> +        self.variants = variants
> +        self.members = None
> +    def check(self, schema):
> +        assert self.members != False    # not running in cycles
> +        if self.members:
> +            return
> +        self.members = False            # mark as being checked
> +        if self.base_name:
> +            self.base = schema.lookup_type(self.base_name)
> +            assert isinstance(self.base, QAPISchemaObjectType)
> +            assert not self.base.variants # not implemented
> +            self.base.check(schema)
> +            members = list(self.base.members)
> +        else:
> +            members = []
> +        seen = {}
> +        for m in members:
> +            seen[m.name] = m
> +        for m in self.local_members:
> +            m.check(schema, members, seen)
> +        if self.variants:
> +            self.variants.check(schema, members, seen)
> +        self.members = members
> +
> +class QAPISchemaObjectTypeMember(object):
> +    def __init__(self, name, typ, optional):
> +        assert isinstance(name, str)
> +        assert isinstance(typ, str)
> +        assert isinstance(optional, bool)
> +        self.name = name
> +        self.type_name = typ
> +        self.type = None
> +        self.optional = optional
> +    def check(self, schema, all_members, seen):
> +        assert self.name not in seen
> +        self.type = schema.lookup_type(self.type_name)
> +        assert self.type
> +        all_members.append(self)
> +        seen[self.name] = self
> +
> +class QAPISchemaObjectTypeVariants(object):
> +    def __init__(self, tag_name, tag_enum, variants):
> +        assert tag_name == None or isinstance(tag_name, str)
> +        assert tag_enum == None or isinstance(tag_enum, str)
> +        for v in variants:
> +            assert isinstance(v, QAPISchemaObjectTypeVariant)
> +        self.tag_name = tag_name
> +        if tag_name:
> +            assert not tag_enum
> +            self.tag_member = None
> +        else:
> +            self.tag_member = QAPISchemaObjectTypeMember('kind', tag_enum,
> +                                                         False)
> +        self.variants = variants
> +    def check(self, schema, members, seen):
> +        if self.tag_name:
> +            self.tag_member = seen[self.tag_name]
> +        else:
> +            self.tag_member.check(schema, members, seen)
> +        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
> +        for v in self.variants:
> +            vseen = dict(seen)
> +            v.check(schema, self.tag_member.type, vseen)
> +
> +class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
> +    def __init__(self, name, typ):
> +        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
> +    def check(self, schema, tag_type, seen):
> +        QAPISchemaObjectTypeMember.check(self, schema, [], seen)
> +        assert self.name in tag_type.values
> +    # TODO try to get rid of .simple_union_type()
> +    def simple_union_type(self):
> +        if isinstance(self.type, QAPISchemaObjectType) and not self.type.info:
> +            assert len(self.type.members) == 1
> +            assert not self.type.variants
> +            return self.type.members[0].type
> +        return None
> +
> +class QAPISchemaAlternateType(QAPISchemaType):
> +    def __init__(self, name, info, variants):
> +        QAPISchemaType.__init__(self, name, info)
> +        assert isinstance(variants, QAPISchemaObjectTypeVariants)
> +        assert not variants.tag_name
> +        self.variants = variants
> +    def check(self, schema):
> +        self.variants.check(schema, [], {})
> +
> +class QAPISchemaCommand(QAPISchemaEntity):
> +    def __init__(self, name, info, arg_type, ret_type, gen, success_response):
> +        QAPISchemaEntity.__init__(self, name, info)
> +        assert not arg_type or isinstance(arg_type, str)
> +        assert not ret_type or isinstance(ret_type, str)
> +        self.arg_type_name = arg_type
> +        self.arg_type = None
> +        self.ret_type_name = ret_type
> +        self.ret_type = None
> +        self.gen = gen
> +        self.success_response = success_response
> +    def check(self, schema):
> +        if self.arg_type_name:
> +            self.arg_type = schema.lookup_type(self.arg_type_name)
> +            assert isinstance(self.arg_type, QAPISchemaObjectType)
> +            assert not self.arg_type.variants # not implemented
> +        if self.ret_type_name:
> +            self.ret_type = schema.lookup_type(self.ret_type_name)
> +            assert isinstance(self.ret_type, QAPISchemaType)
> +
> +class QAPISchemaEvent(QAPISchemaEntity):
> +    def __init__(self, name, info, arg_type):
> +        QAPISchemaEntity.__init__(self, name, info)
> +        assert not arg_type or isinstance(arg_type, str)
> +        self.arg_type_name = arg_type
> +        self.arg_type = None
> +    def check(self, schema):
> +        if self.arg_type_name:
> +            self.arg_type = schema.lookup_type(self.arg_type_name)
> +            assert isinstance(self.arg_type, QAPISchemaObjectType)
> +            assert not self.arg_type.variants # not implemented
> +
> +class QAPISchema(object):
> +    def __init__(self, fname):
> +        try:
> +            self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
> +        except (QAPISchemaError, QAPIExprError), err:

Preferred syntax for exceptions these days is

  except Foo as err

instead of

  except Foo, err

since the former is portable to Python3, should we need to consider
that in future. Again, not a show stopper.

> +            print >>sys.stderr, err
> +            exit(1)

I think it is probably better practice to not have classes print
to stderr / exit.  I'd just let the error propagate to the caller
and have the top level script catch exceptions, print error message
and exit. Again since this is just style issue I won't nack it for
that but something to consider if needing to re-spin this series
again.

> +        self.entity_dict = {}
> +        self._def_predefineds()
> +        self._def_exprs()
> +        self.check()
> +
> +    def get_exprs(self):
> +        return [expr_elem['expr'] for expr_elem in self.exprs]
> +
> +    def _def_entity(self, ent):
> +        assert ent.name not in self.entity_dict
> +        self.entity_dict[ent.name] = ent
> +
> +    def lookup_entity(self, name, typ=None):
> +        ent = self.entity_dict.get(name)
> +        if typ and not isinstance(ent, typ):
> +            return None
> +        return ent
> +
> +    def lookup_type(self, name):
> +        return self.lookup_entity(name, QAPISchemaType)
> +
> +    def _def_builtin_type(self, name):
> +        self._def_entity(QAPISchemaBuiltinType(name))
> +        if name != '**':
> +            self._make_array_type(name) # TODO really needed?
> +
> +    def _def_predefineds(self):
> +        for t in ['str', 'number', 'int', 'int8', 'int16', 'int32', 'int64',
> +                  'uint8', 'uint16', 'uint32', 'uint64', 'size', 'bool', '**']:
> +            self._def_builtin_type(t)
> +
> +    def _make_implicit_enum_type(self, name, values):
> +        name = name + 'Kind'
> +        self._def_entity(QAPISchemaEnumType(name, None, values))
> +        return name
> +
> +    def _make_array_type(self, element_type):
> +        name = element_type + 'List'
> +        if not self.lookup_type(name):
> +            self._def_entity(QAPISchemaArrayType(name, None, element_type))
> +        return name
> +
> +    def _make_implicit_object_type(self, name, role, members):
> +        if not members:
> +            return None
> +        name = ':obj-%s-%s' % (name, role)
> +        if not self.lookup_entity(name, QAPISchemaObjectType):
> +            self._def_entity(QAPISchemaObjectType(name, None, None,
> +                                                  members, None))
> +        return name
> +
> +    def _def_enum_type(self, expr, info):
> +        name = expr['enum']
> +        data = expr['data']
> +        self._def_entity(QAPISchemaEnumType(name, info, data))
> +        self._make_array_type(name) # TODO really needed?
> +
> +    def _make_member(self, name, typ):
> +        optional = False
> +        if name.startswith('*'):
> +            name = name[1:]
> +            optional = True
> +        if isinstance(typ, list):
> +            assert len(typ) == 1
> +            typ = self._make_array_type(typ[0])
> +        return QAPISchemaObjectTypeMember(name, typ, optional)
> +
> +    def _make_members(self, data):
> +        return [self._make_member(key, value) 

Trailing whitespace at end of this line

> +                for (key, value) in data.iteritems()]
> +
> +    def _def_struct_type(self, expr, info):
> +        name = expr['struct']
> +        base = expr.get('base')
> +        data = expr['data']
> +        self._def_entity(QAPISchemaObjectType(name, info, base,
> +                                              self._make_members(data),
> +                                              None))
> +        self._make_array_type(name) # TODO really needed?
> +
> +    def _make_variant(self, case, typ):
> +        return QAPISchemaObjectTypeVariant(case, typ)
> +
> +    def _make_simple_variant(self, case, typ):
> +        if isinstance(typ, list):
> +            assert len(typ) == 1
> +            typ = self._make_array_type(typ[0])
> +        typ = self._make_implicit_object_type(typ, 'wrapper',
> +                                              [self._make_member('data', typ)])
> +        return QAPISchemaObjectTypeVariant(case, typ)
> +
> +    def _make_tag_enum(self, type_name, variants):
> +        return self._make_implicit_enum_type(type_name,
> +                                             [v.name for v in variants])
> +
> +    def _def_union_type(self, expr, info):
> +        name = expr['union']
> +        data = expr['data']
> +        base = expr.get('base')
> +        tag_name = expr.get('discriminator')
> +        tag_enum = None
> +        if tag_name:
> +            variants = [self._make_variant(key, value)
> +                        for (key, value) in data.iteritems()]
> +        else:
> +            variants = [self._make_simple_variant(key, value)
> +                        for (key, value) in data.iteritems()]
> +            tag_enum = self._make_tag_enum(name, variants)
> +        self._def_entity(QAPISchemaObjectType(name, info, base,
> +                                    self._make_members(OrderedDict()),
> +                                    QAPISchemaObjectTypeVariants(tag_name,
> +                                                                 tag_enum,
> +                                                                 variants)))
> +        self._make_array_type(name) # TODO really needed?
> +
> +    def _def_alternate_type(self, expr, info):
> +        name = expr['alternate']
> +        data = expr['data']
> +        variants = [self._make_variant(key, value)
> +                    for (key, value) in data.iteritems()]
> +        tag_enum = self._make_tag_enum(name, variants)
> +        self._def_entity(QAPISchemaAlternateType(name, info,
> +                                    QAPISchemaObjectTypeVariants(None,
> +                                                                 tag_enum,
> +                                                                 variants)))
> +        self._make_array_type(name) # TODO really needed?
> +
> +    def _def_command(self, expr, info):
> +        name = expr['command']
> +        data = expr.get('data')
> +        rets = expr.get('returns')
> +        gen = expr.get('gen', True)
> +        success_response = expr.get('success-response', True)
> +        if isinstance(data, OrderedDict):
> +            data = self._make_implicit_object_type(name, 'arg',
> +                                                   self._make_members(data))
> +        if isinstance(rets, list):
> +            assert len(rets) == 1
> +            rets = self._make_array_type(rets[0])
> +        self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
> +                                           success_response))
> +
> +    def _def_event(self, expr, info):
> +        name = expr['event']
> +        data = expr.get('data')
> +        if isinstance(data, OrderedDict):
> +            data = self._make_implicit_object_type(name, 'arg',
> +                                                   self._make_members(data))
> +        self._def_entity(QAPISchemaEvent(name, info, data))
> +
> +    def _def_exprs(self):
> +        for expr_elem in self.exprs:
> +            expr = expr_elem['expr']
> +            info = expr_elem['info']
> +            if 'enum' in expr:
> +                self._def_enum_type(expr, info)
> +            elif 'struct' in expr:
> +                self._def_struct_type(expr, info)
> +            elif 'union' in expr:
> +                self._def_union_type(expr, info)
> +            elif 'alternate' in expr:
> +                self._def_alternate_type(expr, info)
> +            elif 'command' in expr:
> +                self._def_command(expr, info)
> +            elif 'event' in expr:
> +                self._def_event(expr, info)
> +            else:
> +                assert False
> +
> +    def check(self):
> +        for ent in self.entity_dict.values():
> +            ent.check(self)
>  
>  #
>  # Code generation helpers
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index 634ef2d..461c713 100644
> --- a/tests/qapi-schema/test-qapi.py
> +++ b/tests/qapi-schema/test-qapi.py
> @@ -16,7 +16,7 @@ import os
>  import sys
>  
>  try:
> -    exprs = parse_schema(sys.argv[1])
> +    exprs = QAPISchema(sys.argv[1]).get_exprs()
>  except SystemExit:
>      

Following on earlier comment, this could have a block

  except Exception as e:
      print >>sys.stderr, e
      sys.exit(1)

instead of having the object constructor call exit itself.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 03/32] qapi: QAPISchema code generation helper methods
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 03/32] qapi: QAPISchema code generation helper methods Markus Armbruster
@ 2015-09-03 16:13   ` Daniel P. Berrange
  0 siblings, 0 replies; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 16:13 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Thu, Sep 03, 2015 at 04:29:54PM +0200, Markus Armbruster wrote:
> New methods c_name(), c_type(), c_null(), json_type(),
> alternate_qtype().
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 04/32] qapi: New QAPISchemaVisitor
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 04/32] qapi: New QAPISchemaVisitor Markus Armbruster
@ 2015-09-03 16:16   ` Daniel P. Berrange
  0 siblings, 0 replies; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 16:16 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Thu, Sep 03, 2015 at 04:29:55PM +0200, Markus Armbruster wrote:
> The visitor will help keeping the code generation code simple and
> reasonably separated from QAPISchema details.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor Markus Armbruster
@ 2015-09-03 16:18   ` Daniel P. Berrange
  2015-09-03 16:24   ` Eric Blake
  2015-09-05 13:23   ` Eric Blake
  2 siblings, 0 replies; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 16:18 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Thu, Sep 03, 2015 at 04:29:56PM +0200, Markus Armbruster wrote:
> The old code prints the result of parsing (list of expression
> dictionaries), and partial results of semantic analysis (list of enum
> dictionaries, list of struct dictionaries).
> 
> The new code prints a trace of a schema visit, i.e. what the back-ends
> are going to use.  Built-in and array types are omitted, because
> they're boring.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 06/32] qapi: Split up some typedefs to ease review
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 06/32] qapi: Split up some typedefs to ease review Markus Armbruster
@ 2015-09-03 16:19   ` Daniel P. Berrange
  0 siblings, 0 replies; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 16:19 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Thu, Sep 03, 2015 at 04:29:57PM +0200, Markus Armbruster wrote:
> One of the next patches will among other things generate a separate
> typedef for some struct types, i.e.
> 
>     typedef struct FOO FOO;
> 
>     struct FOO {
>     ...
>     };
> 
> instead of
> 
>     typedef struct FOO {
>     ...
>     } FOO;
> 
> To make the generated files easier to diff, anticipate the change.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 07/32] qapi: Generate comments to simplify splitting for review
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 07/32] qapi: Generate comments to simplify splitting for review Markus Armbruster
@ 2015-09-03 16:21   ` Daniel P. Berrange
  0 siblings, 0 replies; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 16:21 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Thu, Sep 03, 2015 at 04:29:58PM +0200, Markus Armbruster wrote:
> The effect of the next few patches on generated files is hard to
> review, because stuff gets generated in different order, which renders
> diffs of the generated files useless.
> 
> To get reviewable diffs, we need to split the generated files into
> suitable parts: put every top-level declaration in a file named like
> the thing declared, so we can diff top-level declarations regardless
> of their order in the generated files.
> 
> Since I don't feel like parsing C, simply generate a // comment
> identifying the declaration right before each top-level declaration.
> This lets us split with a simple shell loop:
> 
>     mkdir -p o
>     for i in q*-{commands,marshal,event,types,visit}.[ch]
>     do
>         csplit -n 4 -s "$i" '/^\/\//' '{*}'
>         for j in xx*
>         do
>             read h t <$j
>             [ "$h" = "//" ] || t=""
>             mv $j o/"$i-${j#xx}-${t/ /-}"
>         done
>     done
> 
> Splits each file F into F-NUMB-ID, where NUMB counts up from 0001, and
> ID comes from the // comment.
> 
> To check the split's sane, we can run
> 
>     for i in q*-{commands,marshal,event,types,visit}.[ch]
>     do cat o/$i-* | diff $i -
>     done
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor Markus Armbruster
  2015-09-03 16:18   ` Daniel P. Berrange
@ 2015-09-03 16:24   ` Eric Blake
  2015-09-05 13:23   ` Eric Blake
  2 siblings, 0 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-03 16:24 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 737 bytes --]

On 09/03/2015 08:29 AM, Markus Armbruster wrote:
> The old code prints the result of parsing (list of expression
> dictionaries), and partial results of semantic analysis (list of enum
> dictionaries, list of struct dictionaries).
> 
> The new code prints a trace of a schema visit, i.e. what the back-ends
> are going to use.  Built-in and array types are omitted, because
> they're boring.

Knowing which array types are in memory might not be boring in the
future if we address the FIXME to make them generated on an as-needed
basis instead of always - but that of course is a matter for the future
patches :)

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 29/32] qapi: Pseudo-type '**' is now unused, drop it
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 29/32] qapi: Pseudo-type '**' is now unused, drop it Markus Armbruster
@ 2015-09-03 20:42   ` Eric Blake
  2015-09-04  7:14     ` Markus Armbruster
  0 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-03 20:42 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 1267 bytes --]

On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> 'gen': false needs to stay for now, because netdev_add is still using
> it.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---

> +++ b/tests/Makefile
> @@ -228,7 +228,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
>  	bad-type-dict.json double-data.json unknown-expr-key.json \
>  	redefined-type.json redefined-command.json redefined-builtin.json \
>  	redefined-event.json command-int.json bad-data.json event-max.json \
> -	type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
> +	type-bypass-bad-gen.json \
>  	args-invalid.json \
>  	args-array-empty.json args-array-unknown.json args-int.json \
>  	args-unknown.json args-member-unknown.json args-member-array.json \

Not for this patch, but we aren't very consistent on any form of sorting
or line length in this section. It might be nicer if it were one test
per line (lots more line continuations) and/or alphabetical order.  If
that sounds nice, then it's a trivial patch to add in as one of the
followups after this series lands.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection Markus Armbruster
@ 2015-09-03 20:50   ` Eric Blake
  2015-09-04  0:04     ` Michael Roth
  2015-09-03 23:59   ` Michael Roth
  2015-09-04  2:03   ` Eric Blake
  2 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-03 20:50 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 1976 bytes --]

On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> qapi/introspect.json defines the introspection schema.  It's designed
> for QMP introspection, but should do for similar uses, such as QGA.

[review to follow in separate message; I'm using this message to focus
on one point for easier tracking of the sub-thread]

> 
> The introspection schema does not reflect all the rules and
> restrictions that apply to QAPI schemata.  A valid QAPI schema has an
> introspection value conforming to the introspection schema, but the
> converse is not true.
> 
> Introspection lowers away a number of schema details, and makes
> implicit things explicit:
> 
> * The built-in types are declared with their JSON type.
> 
>   TODO Should we map all the integer types to just int?

So, given the discussion on v3, I think we will have consensus if v5
does the following:

- Merge 30 and 31 into a single patch, and drop this TODO statement (I
think we have agreement that exposing QMP [that is, JSON number] wire
form, with no additional constraints, as a single 'int', rather than the
underlying generated C types, is the way to go), provided that...
- Document in the qapi side that the schema intentionally lacks details
on ranges, and that consulting the qapi and/or C code may be required to
see what actual values are valid anywhere introspection merely says 'int',
- leave 32 as a separate patch, as it is complex enough, and still may
have debate on whether the types '123' and 'int' should have
corresponding array types '[123]' and '[int]' rather than the v3 patch
munging of '456' and '789' (I haven't yet reviewed whether v4 changed that).

Any further arguments on whether exposing just 'int' in the
introspection for all integral types, and/or whether patches 30 and 31
should be merged, are best made in response to this mail.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection Markus Armbruster
  2015-09-03 20:50   ` Eric Blake
@ 2015-09-03 23:59   ` Michael Roth
  2015-09-04  8:54     ` Markus Armbruster
  2015-09-04  2:03   ` Eric Blake
  2 siblings, 1 reply; 75+ messages in thread
From: Michael Roth @ 2015-09-03 23:59 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel

Quoting Markus Armbruster (2015-09-03 09:30:21)
> qapi/introspect.json defines the introspection schema.  It's designed
> for QMP introspection, but should do for similar uses, such as QGA.
> 
> The introspection schema does not reflect all the rules and
> restrictions that apply to QAPI schemata.  A valid QAPI schema has an
> introspection value conforming to the introspection schema, but the
> converse is not true.
> 
> Introspection lowers away a number of schema details, and makes
> implicit things explicit:
> 
> * The built-in types are declared with their JSON type.
> 
>   TODO Should we map all the integer types to just int?
> 
> * Implicit type definitions are made explicit, and given
>   auto-generated names:
> 
>   - Array types, named by appending "List" to the name of their
>     element type, like in generated C.
> 
>   - The enumeration types implicitly defined by simple union types,
>     named by appending "Kind" to the name of their simple union type,
>     like in generated C.
> 
>   - Types that don't occur in generated C.  Their names start with ':'
>     so they don't clash with the user's names.
> 
> * All type references are by name.
> 
> * The struct and union types are generalized into an object type.
> 
> * Base types are flattened.
> 
> * Commands take a single argument and return a single result.
> 
>   Dictionary argument or list result is an implicit type definition.
> 
>   The empty object type is used when a command takes no arguments or
>   produces no results.
> 
>   The argument is always of object type, but the introspection schema
>   doesn't reflect that.
> 
>   The 'gen': false directive is omitted as implementation detail.
> 
>   The 'success-response' directive is omitted as well for now, even
>   though it's not an implementation detail, because it's not used by
>   QMP.
> 
> * Events carry a single data value.
> 
>   Implicit type definition and empty object type use, just like for
>   commands.
> 
>   The value is of object type, but the introspection schema doesn't
>   reflect that.
> 
> * Types not used by commands or events are omitted.
> 
>   Indirect use counts as use.
> 
> * Optional members have a default, which can only be null right now
> 
>   Instead of a mandatory "optional" flag, we have an optional default.
>   No default means mandatory, default null means optional without
>   default value.  Non-null is available for optional with default
>   (possible future extension).
> 
> * Clients should *not* look up types by name, because type names are
>   not ABI.  Look up the command or event you're interested in, then
>   follow the references.
> 
>   TODO Should we hide the type names to eliminate the temptation?
> 
> New generator scripts/qapi-introspect.py computes an introspection
> value for its input, and generates a C variable holding it.
> 
> It can generate awfully long lines.  Marked TODO.
> 
> A new test-qmp-input-visitor test case feeds its result for both
> tests/qapi-schema/qapi-schema-test.json and qapi-schema.json to a
> QmpInputVisitor to verify it actually conforms to the schema.
> 
> New QMP command query-schema takes its return value from that
> variable.  Its reply is some 85KiBytes for me right now.
> 
> If this turns out to be too much, we have a couple of options:
> 
> * We can use shorter names in the JSON.  Not the QMP style.
> 
> * Optionally return the sub-schema for commands and events given as
>   arguments.
> 
>   Right now qmp_query_schema() sends the string literal computed by
>   qmp-introspect.py.  To compute sub-schema at run time, we'd have to
>   duplicate parts of qapi-introspect.py in C.  Unattractive.
> 
> * Let clients cache the output of query-schema.
> 
>   It changes only on QEMU upgrades, i.e. rarely.  Provide a command
>   query-schema-hash.  Clients can have a cache indexed by hash, and
>   re-query the schema only when they don't have it cached.  Even
>   simpler: put the hash in the QMP greeting.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  .gitignore                                      |   1 +
>  Makefile                                        |   9 +-
>  Makefile.objs                                   |   4 +-
>  docs/qapi-code-gen.txt                          | 226 +++++++++++++++++++-
>  monitor.c                                       |  15 ++
>  qapi-schema.json                                |   3 +
>  qapi/introspect.json                            | 271 ++++++++++++++++++++++++
>  qmp-commands.hx                                 |  17 ++
>  scripts/qapi-introspect.py                      | 174 +++++++++++++++
>  scripts/qapi.py                                 |  12 +-
>  tests/.gitignore                                |   1 +
>  tests/Makefile                                  |  10 +-
>  tests/qapi-schema/alternate-good.out            |   1 +
>  tests/qapi-schema/args-member-array.out         |   1 +
>  tests/qapi-schema/comments.out                  |   1 +
>  tests/qapi-schema/empty.out                     |   1 +
>  tests/qapi-schema/enum-empty.out                |   1 +
>  tests/qapi-schema/event-case.out                |   1 +
>  tests/qapi-schema/flat-union-reverse-define.out |   1 +
>  tests/qapi-schema/ident-with-escape.out         |   1 +
>  tests/qapi-schema/include-relpath.out           |   1 +
>  tests/qapi-schema/include-repetition.out        |   1 +
>  tests/qapi-schema/include-simple.out            |   1 +
>  tests/qapi-schema/indented-expr.out             |   1 +
>  tests/qapi-schema/qapi-schema-test.out          |   1 +
>  tests/qapi-schema/returns-int.out               |   1 +
>  tests/test-qmp-input-strict.c                   |  55 +++++
>  27 files changed, 801 insertions(+), 11 deletions(-)
>  create mode 100644 qapi/introspect.json
>  create mode 100644 scripts/qapi-introspect.py
> 

<snip>

> diff --git a/qapi-schema.json b/qapi-schema.json
> index e91e3b9..af19810 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -14,6 +14,9 @@
>  # Tracing commands
>  { 'include': 'qapi/trace.json' }
> 
> +# QAPI introspection
> +{ 'include': 'qapi/introspect.json' }
> +
>  ##
>  # @LostTickPolicy:
>  #
> diff --git a/qapi/introspect.json b/qapi/introspect.json
> new file mode 100644
> index 0000000..171baba
> --- /dev/null
> +++ b/qapi/introspect.json
> @@ -0,0 +1,271 @@
> +# -*- Mode: Python -*-
> +#
> +# QAPI/QMP introspection
> +#
> +# Copyright (C) 2015 Red Hat, Inc.
> +#
> +# Authors:
> +#  Markus Armbruster <armbru@redhat.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or later.
> +# See the COPYING file in the top-level directory.
> +
> +##
> +# @query-schema
> +#
> +# Command query-schema exposes the QMP wire ABI as an array of
> +# SchemaInfo.  This lets QMP clients figure out what commands and
> +# events are available in this QEMU, and their parameters and results.
> +#
> +# Returns: array of @SchemaInfo, where each element describes an
> +# entity in the ABI: command, event, type, ...
> +#
> +# Note: the QAPI schema is also used to help define *internal*
> +# interfaces, by defining QAPI types.  These are not part of the QMP
> +# wire ABI, and therefore not returned by this command.

Maybe add something like:

"These internal interfaces may place additional restrictions on the
values of individual fields, so users should reference the QAPI schema
to avoid unexpected behavior resulting from invalid field values."

<snip>

> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index b06d74c..6232ca0 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2168,6 +2168,23 @@ EQMP
>      },
> 
>  SQMP
> +query-schema
> +------------
> +
> +Return the QMP wire schema.  The returned value is a json-array of
> +named schema entities.  Entities are commands, events and various
> +types.  See docs/qapi-code-gen.txt for information on their structure
> +and intended use.

We should probably duplicate any notes from QAPI schema description here
as well, or at least direct users to reference them.

> +
> +EQMP
> +
> +    {
> +        .name       = "query-schema",
> +        .args_type  = "",
> +        .mhandler.cmd_new = qmp_query_schema,
> +    },
> +
> +SQMP
>  query-chardev
>  -------------
> 
> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
> new file mode 100644
> index 0000000..4bcffc4
> --- /dev/null
> +++ b/scripts/qapi-introspect.py
> @@ -0,0 +1,174 @@
> +#
> +# QAPI introspection generator
> +#
> +# Copyright (C) 2015 Red Hat, Inc.
> +#
> +# Authors:
> +#  Markus Armbruster <armbru@redhat.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2.
> +# See the COPYING file in the top-level directory.
> +
> +from qapi import *
> +import string
> +
> +# Caveman's json.dumps() replacement (we're stuck at 2.4)
> +# TODO try to use json.dumps() once we get unstuck
> +def to_json(obj, level=0):
> +    if obj == None:
> +        ret = 'null'
> +    elif isinstance(obj, str):
> +        ret = '"' + obj.replace('"', r'\"') + '"'
> +    elif isinstance(obj, list):
> +        elts = [to_json(elt, level + 1)
> +                for elt in obj]
> +        ret = '[' + ', '.join(elts) + ']'
> +    elif isinstance(obj, dict):
> +        elts = ['"%s": %s' % (key, to_json(obj[key], level + 1))
> +                for key in sorted(obj.keys())]
> +        ret = '{' + ', '.join(elts) + '}'
> +    else:
> +        assert False                # not implemented
> +    if level == 1:
> +        ret = '\n' + ret
> +    return ret
> +
> +def to_c_string(string):
> +    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
> +
> +class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
> +    def __init__(self):
> +        self.defn = None
> +        self.decl = None
> +        self.schema = None
> +        self.jsons = None
> +        self.used_types = None
> +
> +    def visit_begin(self, schema):
> +        self.schema = schema
> +        self.jsons = []
> +        self.used_types = []
> +        return QAPISchemaType   # don't visit types for now
> +
> +    def visit_end(self):
> +        # visit the types that are actually used
> +        for typ in self.used_types:
> +            typ.visit(self)
> +        self.jsons.sort()
> +        # generate C
> +        # TODO can generate awfully long lines

Not sure if this is planned for this series, but a multi-line
representation in the generated files would make things a bit
easier to review/check. If we went a little further and made
them pretty-printed it might make some of the docs referencing
the output a bit more clear as well. Not a huge deal, but don't
think it would take much code. Would suggest a JSON library or
something but that would probably mangle the ordering, which
wouldn't help with readability.

> +        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')
> +        c_string = '\n    '.join([to_c_string(line) for line in lines])
> +        self.defn = mcgen('''
> +const char %(c_name)s[] = %(c_string)s;
> +''',
> +                          c_name=c_name(name),
> +                          c_string=c_string)
> +        self.schema = None
> +        self.jsons = None
> +        self.used_types = None

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-03 20:50   ` Eric Blake
@ 2015-09-04  0:04     ` Michael Roth
  2015-09-04  7:19       ` Markus Armbruster
  0 siblings, 1 reply; 75+ messages in thread
From: Michael Roth @ 2015-09-04  0:04 UTC (permalink / raw)
  To: Eric Blake, Markus Armbruster, qemu-devel

Quoting Eric Blake (2015-09-03 15:50:16)
> On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> > qapi/introspect.json defines the introspection schema.  It's designed
> > for QMP introspection, but should do for similar uses, such as QGA.
> 
> [review to follow in separate message; I'm using this message to focus
> on one point for easier tracking of the sub-thread]
> 
> > 
> > The introspection schema does not reflect all the rules and
> > restrictions that apply to QAPI schemata.  A valid QAPI schema has an
> > introspection value conforming to the introspection schema, but the
> > converse is not true.
> > 
> > Introspection lowers away a number of schema details, and makes
> > implicit things explicit:
> > 
> > * The built-in types are declared with their JSON type.
> > 
> >   TODO Should we map all the integer types to just int?
> 
> So, given the discussion on v3, I think we will have consensus if v5
> does the following:
> 
> - Merge 30 and 31 into a single patch, and drop this TODO statement (I
> think we have agreement that exposing QMP [that is, JSON number] wire
> form, with no additional constraints, as a single 'int', rather than the
> underlying generated C types, is the way to go), provided that...
> - Document in the qapi side that the schema intentionally lacks details
> on ranges, and that consulting the qapi and/or C code may be required to
> see what actual values are valid anywhere introspection merely says 'int',
> - leave 32 as a separate patch, as it is complex enough, and still may
> have debate on whether the types '123' and 'int' should have
> corresponding array types '[123]' and '[int]' rather than the v3 patch
> munging of '456' and '789' (I haven't yet reviewed whether v4 changed that).

Agreed on all 3

> 
> Any further arguments on whether exposing just 'int' in the
> introspection for all integral types, and/or whether patches 30 and 31
> should be merged, are best made in response to this mail.

Given the above, I don't think I have any further objections.

> 
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
> 

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection Markus Armbruster
  2015-09-03 20:50   ` Eric Blake
  2015-09-03 23:59   ` Michael Roth
@ 2015-09-04  2:03   ` Eric Blake
  2015-09-04  3:12     ` Eric Blake
  2015-09-04  9:55     ` Markus Armbruster
  2 siblings, 2 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04  2:03 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 25337 bytes --]

On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> qapi/introspect.json defines the introspection schema.  It's designed
> for QMP introspection, but should do for similar uses, such as QGA.

[review in this sub-thread; for comments on 'int' munging or other
followups, see other subthread]

There is at least one definite bug (see multiple references below to
[1]), and several ideas for cleanups, but in general, I think that the
remaining changes are going to be small enough that I'd probably be okay
if v5 started life with:
Reviewed-by: Eric Blake <eblake@redhat.com>
(Of course, you have every right to not add the R-b, especially if you
want me to do another close review :)

> 
> The introspection schema does not reflect all the rules and
> restrictions that apply to QAPI schemata.  A valid QAPI schema has an
> introspection value conforming to the introspection schema, but the
> converse is not true.
> 
> 
>   The empty object type is used when a command takes no arguments or
>   produces no results.

We had discussed whether to expose the empty type in a separate patch,
to reduce the size of this patch; but at this point in the game, I'm
happy to keep it as-is (the faster we get v5 out for review and into the
tree, the faster we can process the backlog of followup patches).

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---


> +++ b/docs/qapi-code-gen.txt
> @@ -494,13 +494,195 @@ Resulting in this JSON object:
>    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
>  
>  
> +== Client JSON Protocol introspection ==
> +
> +Clients of a Client JSON Protocol commonly need to figure out what
> +exactly the server (QEMU) supports.
> +
> +For this purpose, QMP provides introspection via command query-schema.

Will need tweaking to the agreed-on name query-qmp-schema...

> +QGA currently doesn't support introspection.

... (and we'd obviously want qga to eventually gain a matching
counterpart, possibly 'guest-schema' or 'guest-get-schema', to match
prevailing naming in qga/qapi-schema.json).

> +
> +query-schema returns a JSON array of SchemaInfo objects.  These
> +objects together describe the wire ABI, as defined in the QAPI schema.

Maybe an additional sentence here, perhaps along the lines of:

Note that the wire format cannot express everything; it is not designed
to show semantic constraints (such as when exactly one of a pair of
mutually-exclusive optional members must be present, or when an integral
value has specific limitations on valid values).  Introspection allows
an application to know if a feature is present, but the application must
obey the qapi documentation to properly interact with that feature.

> +
> +Like any other command, query-schema is itself defined in the QAPI
> +schema, along with the SchemaInfo type.  This text attempts to give an
> +overview how things work.  For details you need to consult the QAPI
> +schema.
> +
> +SchemaInfo objects have common members "name" and "meta-type", and
> +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.

And hopefully patch 32 munges this text to match the simplification made
there (I haven't checked yet).

> +
> +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.
> +
> +QAPI schema definitions not reachable that way are omitted.
> +
> +The SchemaInfo for a command has meta-type "command", and variant
> +members "arg-type" and "ret-type".  The arguments member clients pass

I found it easier to read with:
s/arguments/arguments that/

> +with a command on the wire must conform to the type named by
> +"arg-type", which is always an object type.  The return value the

Likewise s/value/value that/

> +server passes in a success response conforms to the type named by
> +"ret-type".
> +

[not for this patch. Thinking aloud: I wonder if QGA's success-response
could be coded in by having 'ret-type':null as a way of saying the
command will have no response if it is successful; perhaps a bit nicer
than exposing an additional boolean flag member]

> +If the command takes no arguments, "arg-type" names an object type
> +without members.  Likewise, if the command returns nothing, "ret-type"
> +names an object type without members.
> +
> +Example: the SchemaInfo for command query-schema
> +
> +    { "name": "query-schema", "meta-type": "command",
> +      "arg-type": ":empty", "ret-type": "SchemaInfoList" }
> +
> +    Type ":empty" is an object type without members, and type
> +    "SchemaInfoList" is the array of SchemaInfo type.
> +
> +The SchemaInfo for an event has meta-type "event", and variant member
> +"arg-type".  The data member the server passes with an event conforms

again, reads a little better with s/member/member that/

> +to the type named by "arg-type".  It is always an object type.
> +
> +If the event carries no additional information, "arg-type" names an
> +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.
> +
> +Example: the SchemaInfo for EVENT_C from section Events
> +
> +    { "name": "EVENT_C", "meta-type": "event",
> +      "arg-type": ":obj-EVENT_C-arg" }
> +
> +    Type ":obj-EVENT_C-arg" is an implicitly defined object type with
> +    the two members from the event's definition.
> +
> +The SchemaInfo for struct and union types has meta-type "object".
> +
> +The SchemaInfo for a struct type has variant member "members".

Worth mentioning that "members" may be empty, for an empty struct?

> +
> +The SchemaInfo for a union type additionally has variant members "tag"
> +and "variants".

Worth mentioning that "members" will never be empty, because it contains
at least the member also referenced in "tag"?

> +
> +"members" is a JSON array describing the object's common members.
> +Each element is a JSON object with members "name" (the member's name),
> +"type" (the name of its type), and optionally "default".  The member
> +is optional if "default" is present.  Currently, "default" can only
> +have value null.  Other values are reserved for future extensions.
> +
> +Example: the SchemaInfo for MyType from section Struct types
> +
> +    { "name": "MyType", "meta-type": "object",
> +      "members": [
> +          { "name": "member1", "type": "str" },
> +          { "name": "member2", "type": "int" },
> +          { "name": "member3", "type": "str", "default": null } ] }
> +
> +"tag" is the name of the common member serving as type tag.
> +"variants" is a JSON array describing the object's variant members.
> +Each element is a JSON object with members "case" (the value of type
> +tag this element applies to) and "type" (the name of an object type
> +that provides the variant members for this type tag value).
> +
> +Example: the SchemaInfo for flat union BlockdevOptions from section
> +Union types

Hmm, the "Union types" section has two mentions of BlockdevOptions; and
this example matches the second. It may help readability if we rename
one of the two examples to be distinct (preferably the first, since it
doesn't match actual QMP).  I guess the phrase "flat union
BlockdevOptions" is sufficient to make it obvious that we are referring
to the second usage, but it is subtle.

> +
> +    { "name": "BlockdevOptions", "meta-type": "object",
> +      "members": [
> +          { "name": "driver", "type": "BlockdevDriver" },
> +          { "name": "readonly", "type": "bool"} ],
> +      "tag": "driver",
> +      "variants": [
> +          { "case": "file", "type": "FileOptions" },
> +          { "case": "qcow2", "type": "Qcow2Options" } ] }
> +
> +Note that base types are "flattened": its members are included in the
> +"members" array.
> +
> +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.
> +
> +Example: the SchemaInfo for simple union BlockdevOptions from section
> +Union types

Ah, and here you refer to the other BlockdevOptions. So the point about
a judicious rename may still be warranted.

> +
> +    { "name": "BlockdevOptions", "meta-type": "object",
> +      "members": [
> +          { "name": "kind", "type": "BlockdevOptionsKind" } ],

[1] Ouch.  That should be "name": "type".  I pointed out the bug in v3,
but it looks like it still hasn't been fixed.  Or rather, that our
attempt to fix it wasn't correct.

> +      "tag": "type",
> +      "variants": [
> +          { "case": "file", "type": ":obj-FileOptions-wrapper" },
> +          { "case": "qcow2", "type": ":obj-Qcow2Options-wrapper" } ] }
> +
> +    Enumeration type "BlockdevOptionsKind" and the object types
> +    ":obj-FileOptions-wrapper", ":obj-Qcow2Options-wrapper" are
> +    implicitly defined.
> +
> +The SchemaInfo for an alternate type has meta-type "alternate", and
> +variant member "members".  "members" is a JSON array.  Each element is
> +a JSON object with member "type", which names a type.  Values of the
> +alternate type conform to exactly one of its member types.
> +
> +Example: the SchemaInfo for BlockRef from section Alternate types
> +
> +    { "name": "BlockRef", "meta-type": "alternate",
> +      "members": [
> +          { "type": "BlockdevOptions" },
> +          { "type": "str" } ] }
> +
> +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.
> +
> +Example: the SchemaInfo for ['str']
> +
> +    { "name": "strList", "meta-type": "array",
> +      "element-type": "str" }
> +
> +The SchemaInfo for an enumeration type has meta-type "enum" and
> +variant member "values".
> +
> +Example: the SchemaInfo for MyEnum from section Enumeration types
> +
> +    { "name": "MyEnum", "meta-type": "enum",
> +      "values": [ "value1", "value2", "value3" ] }
> +
> +The SchemaInfo for a built-in type has the same name as the type in
> +the QAPI schema (see section Built-in Types).  It has variant member
> +"json-type" that shows how values of this type are encoded on the
> +wire.
> +
> +Example: the SchemaInfo for str
> +
> +    { "name": "str", "meta-type": "builtin", "json-type": "string" }
> +
> +As explained above, type names are not part of the wire ABI.  Not even
> +the names of built-in types.  Clients should examine member
> +"json-type" instead of hard-coding names of built-in types.

Good point (although we may still end up with clients that disobey this,
I will certainly make sure to do this in libvirt's interactions).

> +
> +
>  == Code generation ==
>  
> -Schemas are fed into 3 scripts to generate all the code/files that, paired
> -with the core QAPI libraries, comprise everything required to take JSON
> -commands read in by a Client JSON Protocol server, unmarshal the arguments into
> -the underlying C types, call into the corresponding C function, and map the
> -response back to a Client JSON Protocol response to be returned to the user.
> +Schemas are fed into four scripts to generate all the code/files that,
> +paired with the core QAPI libraries, comprise everything required to
> +take JSON commands read in by a Client JSON Protocol server, unmarshal
> +the arguments into the underlying C types, call into the corresponding
> +C function, and map the response back to a Client JSON Protocol
> +response to be returned to the user.

Doesn't mention introspection; but then again, it also doesn't mention
that it generates the C interface for emitting events.

>  
>  As an example, we'll use the following schema, which describes a single
>  complex user-defined type (which will produce a C struct, along with a list
> @@ -848,3 +1030,37 @@ Example:
>      extern const char *const example_QAPIEvent_lookup[];
>  
>      #endif
> +
> +=== scripts/qapi-introspect.py ===
> +
> +Used to generate the introspection C code for a schema. The following
> +files are created:
> +
> +$(prefix)qmp-introspect.c - Defines a string holding a JSON
> +                            description of the schema.
> +$(prefix)qmp-introspect.h - Declares the above string.
> +
> +Example:
> +
> +    $ python scripts/qapi-introspect.py --output-dir="qapi-generated"
> +    --prefix="example-" example-schema.json
> +    $ cat qapi-generated/example-qmp-introspect.c
> +[Uninteresting stuff omitted...]
> +
> +    const char example_qmp_schema_json[] = "["
> +        "{\"arg-type\": \":empty\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
> +        "{\"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\"}]";
> +    $ cat qapi-generated/example-qmp-introspect.h
> +[Uninteresting stuff omitted...]
> +
> +    #ifndef EXAMPLE_QMP_INTROSPECT_H
> +    #define EXAMPLE_QMP_INTROSPECT_H
> +
> +    extern const char example_qmp_schema_json[];
> +
> +    #endif

Overall, a nice documentation addition, and solves one of my missing
checkboxes against v3 :)

> +++ b/qapi-schema.json
> @@ -14,6 +14,9 @@
>  # Tracing commands
>  { 'include': 'qapi/trace.json' }
>  
> +# QAPI introspection
> +{ 'include': 'qapi/introspect.json' }

When we add QGA introspection, we'll want qapi/introspect.json to
contain JUST types, and move the 'command' into qapi-schema.json proper
(that way, qga/qapi-schema.json can also include the same types, then
add its variation on the command).  As it won't matter until we actually
do get to QGA, I'm okay whether you do the hoisting of the 'command' now
or save it for later.

> +++ b/qapi/introspect.json
> @@ -0,0 +1,271 @@

> +
> +##
> +# @query-schema
> +#
> +# Command query-schema exposes the QMP wire ABI as an array of
> +# SchemaInfo.  This lets QMP clients figure out what commands and
> +# events are available in this QEMU, and their parameters and results.
> +#
> +# Returns: array of @SchemaInfo, where each element describes an
> +# entity in the ABI: command, event, type, ...
> +#
> +# Note: the QAPI schema is also used to help define *internal*
> +# interfaces, by defining QAPI types.  These are not part of the QMP
> +# wire ABI, and therefore not returned by this command.

Same as for the docs above: may be worth a paragraph explicitly
mentioning that the wire ABI cannot express everything, such as integer
ranges, or such as semantics where exactly one of two mutually-exclusive
optional members must be present.

...
> +##
> +# @SchemaInfoBase
> +#
> +# Members common to any @SchemaInfo.
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'SchemaInfoBase',
> +  'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } }
> +
> +##
> +# @SchemaInfo
> +#
> +# @name: the entity's name, inherited from @base.

Interesting way to document @name (I probably would have documented
@base as part of SchemaInfoBase); but it worked out well.  No need to
change it :)

> +# 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.
> +#
> +# 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.
> +#
> +# Since: 2.5
> +##
> +{ 'union': 'SchemaInfo',
> +  'base': 'SchemaInfoBase',
> +  'discriminator': 'meta-type',
> +  'data': {
> +      'builtin': 'SchemaInfoBuiltin',
> +      'enum': 'SchemaInfoEnum',
> +      'array': 'SchemaInfoArray',
> +      'object': 'SchemaInfoObject',
> +      'alternate': 'SchemaInfoAlternate',
> +      'command': 'SchemaInfoCommand',
> +      'event': 'SchemaInfoEvent' } }
> +
> +##
> +# @SchemaInfoBuiltin
> +#
> +# Additional SchemaInfo members for meta-type 'builtin'.
> +#
> +# @json-type: the JSON type used for this type on the wire.

Might be worth repeating the caveat in the earlier docs that @name is
not guaranteed to be stable, so clients should check @json-type.

(especially true if we later decide to expose range information by
adding more builtins or user-defined subtypes of builtins to represent
those ranged integers).

...
> +
> +##
> +# @SchemaInfoEnum
> +#
> +# Additional SchemaInfo members for meta-type 'enum'.
> +#
> +# @values: the enumeration type's values.
> +#
> +# Values of this type are JSON string on the wire.

I'm assuming that if we add sorting of elements in a later patch, we can
better document that guarantee here.

> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'SchemaInfoEnum',
> +  'data': { 'values': ['str'] } }
> +
> +##
> +# @SchemaInfoArray
> +#
> +# Additional SchemaInfo members for meta-type 'array'.
> +#
> +# @element-type: the array type's element type.
> +#
> +# Values of this type are JSON array on the wire.

Is it worth adding either of these clarifications?

As required by RFC 7159, the order of individual elements with in the
array sent over the wire is assumed to be significant unless the
documented semantics in qapi state otherwise.

While RFC 7159 permits an array to have elements of different types, all
elements of a QMP array should have the same type.

> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'SchemaInfoArray',
> +  'data': { 'element-type': 'str' } }
> +
> +##
> +# @SchemaInfoObject
> +#
> +# Additional SchemaInfo members for meta-type 'object'.
> +#
> +# @members: the object type's (non-variant) members.
> +#
> +# @tag: #optional the name of the member serving as type tag.  An
> +# element of @members with this name must exist.
> +#
> +# @variants: #optional variant members, i.e. additional members that
> +# depend on the type tag's value.  Present exactly when @tag is
> +# present.
> +#
> +# Values of this type are JSON object on the wire.

Worth adding this clarification?

As documented by RFC 7159, the wire format allows members of the JSON
object to be sent in any order, and requires that members must not be
duplicated.

> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'SchemaInfoObject',
> +  'data': { 'members': [ 'SchemaInfoObjectMember' ],
> +            '*tag': 'str',
> +            '*variants': [ 'SchemaInfoObjectVariant' ] } }
> +
> +##
> +# @SchemaInfoObjectMember
> +#
> +# An object member.
> +#
> +# @name: the member's name, as defined in the QAPI schema.
> +#
> +# @type: the name of the member's type.
> +#
> +# @default: #optional default when used as command parameter.
> +#           If absent, the parameter is mandatory.
> +#           If present, the value must be null.  The parameter is

Maybe:

s/The parameter/On the wire, the parameter/

> +#           optional, and behavior when it's missing is not specified
> +#           here.
> +#           Future extension: if present and non-null, the parameter
> +#	    is optional, and defaults to this value.

TAB damage.

> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'SchemaInfoObjectMember',
> +  'data': { 'name': 'str', 'type': 'str', '*default': 'any' } }
> +# @default's type must be null or match @type

Is this line still worth keeping, given the more complete explanation
above?  But we do need to use 'any' here, as that is currently the only
way for qapi-generated code to allow for JSON null.

> +
> +##
> +# @SchemaInfoObjectVariant
> +#
> +# The variant members for a value of the type tag.
> +#
> +# @case: a value of the type tag.
> +#
> +# @type: the name of the object type that provides the variant members
> +# when the type tag has value @case.
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'SchemaInfoObjectVariant',
> +  'data': { 'case': 'str', 'type': 'str' } }

Do we want to document whether all case values of the tag are guaranteed
to be covered (happens to be true for all current qapi, but is not yet
enforced; although that's one of my planned followup patches is to start
enforcing it - so I guess I can document it at that time if we don't do
it here)

> +##
> +# @SchemaInfoCommand
> +#
> +# Additional SchemaInfo members for meta-type 'command'.
> +#
> +# @arg-type: the name of the object type that provides the command's
> +# parameters.
> +#
> +# @ret-type: the name of the command's result type.
> +#
> +# TODO @success-response (currently irrelevant, because it's QGA, not QMP)

and my idea of using 'ret-type':null may obviate the need for an actual
'success-response' parameter.

> +++ b/scripts/qapi-introspect.py
> @@ -0,0 +1,174 @@
> +#
> +# QAPI introspection generator
> +#
> +# Copyright (C) 2015 Red Hat, Inc.
> +#
> +# Authors:
> +#  Markus Armbruster <armbru@redhat.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2.

No 'or later' clause?  Oh, because your hands are tied: scripts/qapi.py
is GPLv2-only.  Yuck. I wonder if we can fix that; although it may be
hard getting Anthony to explain his original choice.  Separate cleanup,
if at all. [2]

> +# See the COPYING file in the top-level directory.
> +
> +from qapi import *
> +import string
> +
> +# Caveman's json.dumps() replacement (we're stuck at 2.4)

s/2.4/python 2.4/

> +# TODO try to use json.dumps() once we get unstuck
> +def to_json(obj, level=0):
> +    if obj == None:
> +        ret = 'null'
> +    elif isinstance(obj, str):
> +        ret = '"' + obj.replace('"', r'\"') + '"'
> +    elif isinstance(obj, list):
> +        elts = [to_json(elt, level + 1)
> +                for elt in obj]
> +        ret = '[' + ', '.join(elts) + ']'
> +    elif isinstance(obj, dict):
> +        elts = ['"%s": %s' % (key, to_json(obj[key], level + 1))

This relies on 'key' never having any embedded ".  It happens to work
given our naming conventions of what we pass through this dumper, but
might be safer if you used key.replace('"', r'\"') to handle generic
dumping.

> +
> +    def _gen_member(self, member):
> +        ret = {'name': member.name, 'type': self._use_type(member.type)}
> +        if member.optional:
> +            ret['default'] = None
> +        return ret
> +
> +    def _gen_variants(self, tag_name, variants):
> +        return {'tag': tag_name or 'type',

[1] Ouch. Why are we still printing 'kind' as the name for simple
unions?  Oh, it's because tag_name is ALWAYS provided by the visitor, so
the "or 'type'" clause never fires.  I have a pending patch to change
the C code to generate 'type' as the C code name to match the wire ABI;
but until that patch is in, I think a hack solution might be to fix the
visitor to supply None instead of a tag_name for simple unions.  I'll
respond to the appropriate earlier patch.

> +h_comment = '''
> +/*
> + * QAPI/QMP schema introspection
> + *
> + * Copyright (C) 2015 Red Hat, Inc.
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */

[2] It's interesting that you are following the pattern of
scripts/qapi-commands.py (commit c17d9908); even there, the script
itself was GPLv2-only, while the generated output was LGPLv2+.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation Markus Armbruster
  2015-09-03 16:11   ` Daniel P. Berrange
@ 2015-09-04  3:05   ` Eric Blake
  2015-09-04 12:34   ` Eric Blake
  2 siblings, 0 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04  3:05 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 1929 bytes --]

On 09/03/2015 08:29 AM, Markus Armbruster wrote:
> The QAPI code generators work with a syntax tree (nested dictionaries)
> plus a few symbol tables (also dictionaries) on the side.
> 

> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---

> +class QAPISchemaObjectTypeVariants(object):
> +    def __init__(self, tag_name, tag_enum, variants):
> +        assert tag_name == None or isinstance(tag_name, str)
> +        assert tag_enum == None or isinstance(tag_enum, str)
> +        for v in variants:
> +            assert isinstance(v, QAPISchemaObjectTypeVariant)
> +        self.tag_name = tag_name
> +        if tag_name:
> +            assert not tag_enum
> +            self.tag_member = None
> +        else:
> +            self.tag_member = QAPISchemaObjectTypeMember('kind', tag_enum,
> +                                                         False)

See my review on 30/32.  Introspection-wise, we want this generated
member to be named 'type' to match what is on the wire.  This patch,
plus two more to qapi-types.py and qapi-visit.py, are sufficient to make
no changes to the other generated files, while fixing the bug in
qmp-introspect.c:

diff --git i/scripts/qapi.py w/scripts/qapi.py
index d2b6964..b2643af 100644
--- i/scripts/qapi.py
+++ w/scripts/qapi.py
@@ -928,7 +928,7 @@ class QAPISchemaObjectTypeVariants(object):
             assert not tag_enum
             self.tag_member = None
         else:
-            self.tag_member = QAPISchemaObjectTypeMember('kind', tag_enum,
+            self.tag_member = QAPISchemaObjectTypeMember('type', tag_enum,
                                                          False)
         self.variants = variants
     def check(self, schema, members, seen):

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 10/32] qapi-types: Convert to QAPISchemaVisitor, fixing flat unions
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 10/32] qapi-types: Convert to QAPISchemaVisitor, fixing flat unions Markus Armbruster
@ 2015-09-04  3:11   ` Eric Blake
  0 siblings, 0 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04  3:11 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 2195 bytes --]

On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> Fixes flat unions to get the base's base members.  Test case is from
> commit 2fc0043, in qapi-schema-test.json:
> 

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---
>  docs/qapi-code-gen.txt                  |  51 +++---
>  scripts/qapi-types.py                   | 273 ++++++++++++++------------------
>  tests/qapi-schema/qapi-schema-test.json |   4 +-
>  3 files changed, 144 insertions(+), 184 deletions(-)

> @@ -231,39 +200,36 @@ struct %(name)s {
>      union { /* union tag is @%(c_name)s */
>          void *data;
>  ''',
> -                 c_name=c_name(discriminator or 'kind'))
> +                 c_name=c_name(variants.tag_member.name))

See my review on 30/32.  If we change tag_member to match the wire
spelling of 'type' in patch 2, then this patch needs a slight
alteration.  (Of course, I've already posted a potential followup patch,
currently for use after your series lands, that fixes the C spelling to
quit the pointless rename to 'kind'; maybe we should instead fold that
in to the early part of this series? If not, it will be one of our
earlier followups).

Here's the diff that got the bug in 30/32 fixed, along with changes to
the two other files.

diff --git i/scripts/qapi-types.py w/scripts/qapi-types.py
index 70a57c0..83fc421 100644
--- i/scripts/qapi-types.py
+++ w/scripts/qapi-types.py
@@ -139,11 +139,12 @@ struct %(c_name)s {
     # should not be any data leaks even without a data pointer.  Or, if
     # 'data' is merely added to guarantee we don't have an empty union,
     # shouldn't we enforce that at .json parse time?
+    # FIXME: Use same tag name in C as on the wire
     ret += mcgen('''
     union { /* union tag is @%(c_name)s */
         void *data;
 ''',
-                 c_name=c_name(variants.tag_member.name))
+                 c_name=c_name(variants.tag_name or 'kind'))

     for var in variants.variants:
         # TODO ugly special case for simple union


-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-04  2:03   ` Eric Blake
@ 2015-09-04  3:12     ` Eric Blake
  2015-09-04  9:55     ` Markus Armbruster
  1 sibling, 0 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04  3:12 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 2088 bytes --]

On 09/03/2015 08:03 PM, Eric Blake wrote:
> On 09/03/2015 08:30 AM, Markus Armbruster wrote:
>> qapi/introspect.json defines the introspection schema.  It's designed
>> for QMP introspection, but should do for similar uses, such as QGA.
> 
> [review in this sub-thread; for comments on 'int' munging or other
> followups, see other subthread]
> 
> There is at least one definite bug (see multiple references below to
> [1]), and several ideas for cleanups, but in general, I think that the
> remaining changes are going to be small enough that I'd probably be okay
> if v5 started life with:
> Reviewed-by: Eric Blake <eblake@redhat.com>
> (Of course, you have every right to not add the R-b, especially if you
> want me to do another close review :)
> 

> 
>> +
>> +    { "name": "BlockdevOptions", "meta-type": "object",
>> +      "members": [
>> +          { "name": "kind", "type": "BlockdevOptionsKind" } ],
> 
> [1] Ouch.  That should be "name": "type".  I pointed out the bug in v3,
> but it looks like it still hasn't been fixed.  Or rather, that our
> attempt to fix it wasn't correct.
> 

>> +    def _gen_variants(self, tag_name, variants):
>> +        return {'tag': tag_name or 'type',
> 
> [1] Ouch. Why are we still printing 'kind' as the name for simple
> unions?  Oh, it's because tag_name is ALWAYS provided by the visitor, so
> the "or 'type'" clause never fires.

Not quite the right comment.  'tag' is being output correctly (tag_name
was indeed None), what was wrong was the 'members':[] array (which was
assuming the member was named 'kind').

>   I have a pending patch to change
> the C code to generate 'type' as the C code name to match the wire ABI;
> but until that patch is in, I think a hack solution might be to fix the
> visitor to supply None instead of a tag_name for simple unions.  I'll
> respond to the appropriate earlier patch.

Rather, 3 earlier patches.  See my comments on 2, 10, and 11.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 11/32] qapi-visit: Convert to QAPISchemaVisitor, fixing bugs
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 11/32] qapi-visit: Convert to QAPISchemaVisitor, fixing bugs Markus Armbruster
@ 2015-09-04  3:14   ` Eric Blake
  0 siblings, 0 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04  3:14 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 2416 bytes --]

On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> Fixes flat unions to visit the base's base members (the previous
> commit merely added them to the struct).  Same test case.
> 

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---
>  scripts/qapi-visit.py                   | 260 +++++++++++++-------------------
>  tests/qapi-schema/qapi-schema-test.json |   3 -
>  tests/test-qmp-input-strict.c           |   4 +-
>  tests/test-qmp-input-visitor.c          |   4 +-
>  4 files changed, 107 insertions(+), 164 deletions(-)
> 

> +def gen_visit_union(name, base, variants):
> +    ret = ''
>  
>      if base:

> +    disc_key = variants.tag_member.name
> +    if not variants.tag_name:
> +        # we pointlessly use a different key for simple unions
> +        disc_key = 'type'
>      ret += mcgen('''
> -        visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
> +        visit_type_%(disc_type)s(m, &(*obj)->%(c_name)s, "%(disc_key)s", &err);
>          if (err) {
>              goto out_obj;
>          }
>          if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
>              goto out_obj;
>          }
> -        switch ((*obj)->%(c_tag)s) {
> +        switch ((*obj)->%(c_name)s) {
>  ''',
> -                 disc_type = disc_type,
> -                 c_tag=c_name(tag),
> +                 disc_type=variants.tag_member.type.c_name(),
> +                 c_name=c_name(variants.tag_member.name),
>                   disc_key = disc_key)

See my comments on 30/32, and patches in 2 and 10.  This is the final
place that cannot refer to tag_member.name until after we unify the C
and wire spelling.

diff --git i/scripts/qapi-visit.py w/scripts/qapi-visit.py
index 6819866..468080e 100644
--- i/scripts/qapi-visit.py
+++ w/scripts/qapi-visit.py
@@ -284,7 +284,7 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s
**obj, const char *name, Error
         switch ((*obj)->%(c_name)s) {
 ''',
                  c_type=variants.tag_member.type.c_name(),
-                 c_name=c_name(variants.tag_member.name),
+                 c_name=c_name(variants.tag_name or 'kind'),
                  name=tag_key)

     for var in variants.variants:

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-03 16:11   ` Daniel P. Berrange
@ 2015-09-04  7:12     ` Markus Armbruster
  2015-09-04  8:51       ` Daniel P. Berrange
  0 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04  7:12 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: qemu-devel, mdroth

"Daniel P. Berrange" <berrange@redhat.com> writes:

> On Thu, Sep 03, 2015 at 04:29:53PM +0200, Markus Armbruster wrote:
>> The QAPI code generators work with a syntax tree (nested dictionaries)
>> plus a few symbol tables (also dictionaries) on the side.
>> 
>> They have clearly outgrown these simple data structures.  There's lots
>> of rummaging around in dictionaries, and information is recomputed on
>> the fly.  For the work I'm going to do, I want more clearly defined
>> and more convenient interfaces.
>> 
>> Going forward, I also want less coupling between the back-ends and the
>> syntax tree, to make messing with the syntax easier.
>> 
>> Create a bunch of classes to represent QAPI schemata.
>> 
>> Have the QAPISchema initializer call the parser, then walk the syntax
>> tree to create the new internal representation, and finally perform
>> semantic analysis.
>> 
>> Shortcut: the semantic analysis still relies on existing check_exprs()
>> to do the actual semantic checking.  All this code needs to move into
>> the classes.  Mark as TODO.
>> 
>> Catching name collisions in generated code would be nice.  Mark as
>> TODO.
>> 
>> We generate array types eagerly, even though most of them aren't used.
>> Mark as TODO.
>> 
>> Nothing uses the new intermediate representation just yet, thus no
>> change to generated files.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> Reviewed-by: Eric Blake <eblake@redhat.com>
>
> Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
>
> A few comments inline, but nothing worth blocking on, just style.
>
>
>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> index a0165dd..c42bea1 100644
>> --- a/scripts/qapi.py
>> +++ b/scripts/qapi.py
>> @@ -747,15 +749,355 @@ def check_exprs(exprs):
>>          else:
>>              assert False, 'unexpected meta type'
>>  
>> -    return map(lambda expr_elem: expr_elem['expr'], exprs)
>> -
>> -def parse_schema(fname):
>> -    try:
>> -        schema = QAPISchemaParser(open(fname, "r"))
>> -        return check_exprs(schema.exprs)
>> -    except (QAPISchemaError, QAPIExprError), e:
>> -        print >>sys.stderr, e
>> -        exit(1)
>> +    return exprs
>> +
>> +#
>> +# Schema compiler frontend
>> +#
>> +
>> +class QAPISchemaEntity(object):
>> +    def __init__(self, name, info):
>> +        assert isinstance(name, str)
>> +        self.name = name
>> +        self.info = info
>> +    def check(self, schema):
>> +        pass
>
> From a style POV, I'd recommend having a single blank
> line before each new function def, to make things more
> readable. I won't nack the patch for that, but if you
> have need to rebase & post a new version of this series
> I think it'd be nice to add the the extra blank lines
> when defining all these classes.

I have such blank lines in the more complex stuff.  Didn't feel like it
in trivial classes like this, but if others want the blank lines, I'm
happy to add them.

> Python has a nice tool called pep8 which can apply a
> configurable bunch of  style checks, it is probably
> worth someone wiring it up to make check in QEMU, since
> we have an increasing amount of python code. An exercise
> for a motivated reader.....

I've been using pylint (and dear-oh-dear how much pre-existing lint it
finds!), wasn't aware of pep8, can throw it in.

Automatic checking would be nice, but our current code is probably too
untidy for that.  Besides, I don't have the time and energy to set it
up now.

pylint is reasonably happy with the qapi-*.py now.  I hope the follow-up
work on qapi.py will get it into the same state.

>> +
>> +class QAPISchemaType(QAPISchemaEntity):
>> +    pass
>> +
>> +class QAPISchemaBuiltinType(QAPISchemaType):
>> +    def __init__(self, name):
>> +        QAPISchemaType.__init__(self, name, None)
>> +
>> +class QAPISchemaEnumType(QAPISchemaType):
>> +    def __init__(self, name, info, values):
>> +        QAPISchemaType.__init__(self, name, info)
>> +        for v in values:
>> +            assert isinstance(v, str)
>> +        self.values = values
>> +    def check(self, schema):
>> +        assert len(set(self.values)) == len(self.values)
>> +
>> +class QAPISchemaArrayType(QAPISchemaType):
>> +    def __init__(self, name, info, element_type):
>> +        QAPISchemaType.__init__(self, name, info)
>> +        assert isinstance(element_type, str)
>> +        self.element_type_name = element_type
>> +        self.element_type = None
>> +    def check(self, schema):
>> +        self.element_type = schema.lookup_type(self.element_type_name)
>> +        assert self.element_type
>> +
>> +class QAPISchemaObjectType(QAPISchemaType):
>> +    def __init__(self, name, info, base, local_members, variants):
>> +        QAPISchemaType.__init__(self, name, info)
>> +        assert base == None or isinstance(base, str)
>> +        for m in local_members:
>> +            assert isinstance(m, QAPISchemaObjectTypeMember)
>> +        assert variants == None \
>> +            or isinstance(variants, QAPISchemaObjectTypeVariants)
>> +        self.base_name = base
>> +        self.base = None
>> +        self.local_members = local_members
>> +        self.variants = variants
>> +        self.members = None
>> +    def check(self, schema):
>> +        assert self.members != False    # not running in cycles
>> +        if self.members:
>> +            return
>> +        self.members = False            # mark as being checked
>> +        if self.base_name:
>> +            self.base = schema.lookup_type(self.base_name)
>> +            assert isinstance(self.base, QAPISchemaObjectType)
>> +            assert not self.base.variants # not implemented
>> +            self.base.check(schema)
>> +            members = list(self.base.members)
>> +        else:
>> +            members = []
>> +        seen = {}
>> +        for m in members:
>> +            seen[m.name] = m
>> +        for m in self.local_members:
>> +            m.check(schema, members, seen)
>> +        if self.variants:
>> +            self.variants.check(schema, members, seen)
>> +        self.members = members
>> +
>> +class QAPISchemaObjectTypeMember(object):
>> +    def __init__(self, name, typ, optional):
>> +        assert isinstance(name, str)
>> +        assert isinstance(typ, str)
>> +        assert isinstance(optional, bool)
>> +        self.name = name
>> +        self.type_name = typ
>> +        self.type = None
>> +        self.optional = optional
>> +    def check(self, schema, all_members, seen):
>> +        assert self.name not in seen
>> +        self.type = schema.lookup_type(self.type_name)
>> +        assert self.type
>> +        all_members.append(self)
>> +        seen[self.name] = self
>> +
>> +class QAPISchemaObjectTypeVariants(object):
>> +    def __init__(self, tag_name, tag_enum, variants):
>> +        assert tag_name == None or isinstance(tag_name, str)
>> +        assert tag_enum == None or isinstance(tag_enum, str)
>> +        for v in variants:
>> +            assert isinstance(v, QAPISchemaObjectTypeVariant)
>> +        self.tag_name = tag_name
>> +        if tag_name:
>> +            assert not tag_enum
>> +            self.tag_member = None
>> +        else:
>> +            self.tag_member = QAPISchemaObjectTypeMember('kind', tag_enum,
>> +                                                         False)
>> +        self.variants = variants
>> +    def check(self, schema, members, seen):
>> +        if self.tag_name:
>> +            self.tag_member = seen[self.tag_name]
>> +        else:
>> +            self.tag_member.check(schema, members, seen)
>> +        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
>> +        for v in self.variants:
>> +            vseen = dict(seen)
>> +            v.check(schema, self.tag_member.type, vseen)
>> +
>> +class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
>> +    def __init__(self, name, typ):
>> +        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
>> +    def check(self, schema, tag_type, seen):
>> +        QAPISchemaObjectTypeMember.check(self, schema, [], seen)
>> +        assert self.name in tag_type.values
>> +    # TODO try to get rid of .simple_union_type()
>> +    def simple_union_type(self):
>> + if isinstance(self.type, QAPISchemaObjectType) and not
>> self.type.info:
>> +            assert len(self.type.members) == 1
>> +            assert not self.type.variants
>> +            return self.type.members[0].type
>> +        return None
>> +
>> +class QAPISchemaAlternateType(QAPISchemaType):
>> +    def __init__(self, name, info, variants):
>> +        QAPISchemaType.__init__(self, name, info)
>> +        assert isinstance(variants, QAPISchemaObjectTypeVariants)
>> +        assert not variants.tag_name
>> +        self.variants = variants
>> +    def check(self, schema):
>> +        self.variants.check(schema, [], {})
>> +
>> +class QAPISchemaCommand(QAPISchemaEntity):
>> + def __init__(self, name, info, arg_type, ret_type, gen,
>> success_response):
>> +        QAPISchemaEntity.__init__(self, name, info)
>> +        assert not arg_type or isinstance(arg_type, str)
>> +        assert not ret_type or isinstance(ret_type, str)
>> +        self.arg_type_name = arg_type
>> +        self.arg_type = None
>> +        self.ret_type_name = ret_type
>> +        self.ret_type = None
>> +        self.gen = gen
>> +        self.success_response = success_response
>> +    def check(self, schema):
>> +        if self.arg_type_name:
>> +            self.arg_type = schema.lookup_type(self.arg_type_name)
>> +            assert isinstance(self.arg_type, QAPISchemaObjectType)
>> +            assert not self.arg_type.variants # not implemented
>> +        if self.ret_type_name:
>> +            self.ret_type = schema.lookup_type(self.ret_type_name)
>> +            assert isinstance(self.ret_type, QAPISchemaType)
>> +
>> +class QAPISchemaEvent(QAPISchemaEntity):
>> +    def __init__(self, name, info, arg_type):
>> +        QAPISchemaEntity.__init__(self, name, info)
>> +        assert not arg_type or isinstance(arg_type, str)
>> +        self.arg_type_name = arg_type
>> +        self.arg_type = None
>> +    def check(self, schema):
>> +        if self.arg_type_name:
>> +            self.arg_type = schema.lookup_type(self.arg_type_name)
>> +            assert isinstance(self.arg_type, QAPISchemaObjectType)
>> +            assert not self.arg_type.variants # not implemented
>> +
>> +class QAPISchema(object):
>> +    def __init__(self, fname):
>> +        try:
>> + self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
>> +        except (QAPISchemaError, QAPIExprError), err:
>
> Preferred syntax for exceptions these days is
>
>   except Foo as err
>
> instead of
>
>   except Foo, err
>
> since the former is portable to Python3, should we need to consider
> that in future. Again, not a show stopper.

Does it work with Python 2.4?  That's where we're currently stuck.

>> +            print >>sys.stderr, err
>> +            exit(1)
>
> I think it is probably better practice to not have classes print
> to stderr / exit.  I'd just let the error propagate to the caller
> and have the top level script catch exceptions, print error message
> and exit. Again since this is just style issue I won't nack it for
> that but something to consider if needing to re-spin this series
> again.

Quadruplicates the exception handling...

  I think I'll leave this question open for now.

>> +        self.entity_dict = {}
>> +        self._def_predefineds()
>> +        self._def_exprs()
>> +        self.check()
>> +
>> +    def get_exprs(self):
>> +        return [expr_elem['expr'] for expr_elem in self.exprs]
>> +
>> +    def _def_entity(self, ent):
>> +        assert ent.name not in self.entity_dict
>> +        self.entity_dict[ent.name] = ent
>> +
>> +    def lookup_entity(self, name, typ=None):
>> +        ent = self.entity_dict.get(name)
>> +        if typ and not isinstance(ent, typ):
>> +            return None
>> +        return ent
>> +
>> +    def lookup_type(self, name):
>> +        return self.lookup_entity(name, QAPISchemaType)
>> +
>> +    def _def_builtin_type(self, name):
>> +        self._def_entity(QAPISchemaBuiltinType(name))
>> +        if name != '**':
>> +            self._make_array_type(name) # TODO really needed?
>> +
>> +    def _def_predefineds(self):
>> +        for t in ['str', 'number', 'int', 'int8', 'int16', 'int32', 'int64',
>> + 'uint8', 'uint16', 'uint32', 'uint64', 'size', 'bool', '**']:
>> +            self._def_builtin_type(t)
>> +
>> +    def _make_implicit_enum_type(self, name, values):
>> +        name = name + 'Kind'
>> +        self._def_entity(QAPISchemaEnumType(name, None, values))
>> +        return name
>> +
>> +    def _make_array_type(self, element_type):
>> +        name = element_type + 'List'
>> +        if not self.lookup_type(name):
>> +            self._def_entity(QAPISchemaArrayType(name, None, element_type))
>> +        return name
>> +
>> +    def _make_implicit_object_type(self, name, role, members):
>> +        if not members:
>> +            return None
>> +        name = ':obj-%s-%s' % (name, role)
>> +        if not self.lookup_entity(name, QAPISchemaObjectType):
>> +            self._def_entity(QAPISchemaObjectType(name, None, None,
>> +                                                  members, None))
>> +        return name
>> +
>> +    def _def_enum_type(self, expr, info):
>> +        name = expr['enum']
>> +        data = expr['data']
>> +        self._def_entity(QAPISchemaEnumType(name, info, data))
>> +        self._make_array_type(name) # TODO really needed?
>> +
>> +    def _make_member(self, name, typ):
>> +        optional = False
>> +        if name.startswith('*'):
>> +            name = name[1:]
>> +            optional = True
>> +        if isinstance(typ, list):
>> +            assert len(typ) == 1
>> +            typ = self._make_array_type(typ[0])
>> +        return QAPISchemaObjectTypeMember(name, typ, optional)
>> +
>> +    def _make_members(self, data):
>> +        return [self._make_member(key, value) 
>
> Trailing whitespace at end of this line

Will fix, and search for more.

>> +                for (key, value) in data.iteritems()]
>> +
>> +    def _def_struct_type(self, expr, info):
>> +        name = expr['struct']
>> +        base = expr.get('base')
>> +        data = expr['data']
>> +        self._def_entity(QAPISchemaObjectType(name, info, base,
>> +                                              self._make_members(data),
>> +                                              None))
>> +        self._make_array_type(name) # TODO really needed?
>> +
>> +    def _make_variant(self, case, typ):
>> +        return QAPISchemaObjectTypeVariant(case, typ)
>> +
>> +    def _make_simple_variant(self, case, typ):
>> +        if isinstance(typ, list):
>> +            assert len(typ) == 1
>> +            typ = self._make_array_type(typ[0])
>> +        typ = self._make_implicit_object_type(typ, 'wrapper',
>> + [self._make_member('data', typ)])
>> +        return QAPISchemaObjectTypeVariant(case, typ)
>> +
>> +    def _make_tag_enum(self, type_name, variants):
>> +        return self._make_implicit_enum_type(type_name,
>> +                                             [v.name for v in variants])
>> +
>> +    def _def_union_type(self, expr, info):
>> +        name = expr['union']
>> +        data = expr['data']
>> +        base = expr.get('base')
>> +        tag_name = expr.get('discriminator')
>> +        tag_enum = None
>> +        if tag_name:
>> +            variants = [self._make_variant(key, value)
>> +                        for (key, value) in data.iteritems()]
>> +        else:
>> +            variants = [self._make_simple_variant(key, value)
>> +                        for (key, value) in data.iteritems()]
>> +            tag_enum = self._make_tag_enum(name, variants)
>> +        self._def_entity(QAPISchemaObjectType(name, info, base,
>> +                                    self._make_members(OrderedDict()),
>> +                                    QAPISchemaObjectTypeVariants(tag_name,
>> +                                                                 tag_enum,
>> +                                                                 variants)))
>> +        self._make_array_type(name) # TODO really needed?
>> +
>> +    def _def_alternate_type(self, expr, info):
>> +        name = expr['alternate']
>> +        data = expr['data']
>> +        variants = [self._make_variant(key, value)
>> +                    for (key, value) in data.iteritems()]
>> +        tag_enum = self._make_tag_enum(name, variants)
>> +        self._def_entity(QAPISchemaAlternateType(name, info,
>> +                                    QAPISchemaObjectTypeVariants(None,
>> +                                                                 tag_enum,
>> +                                                                 variants)))
>> +        self._make_array_type(name) # TODO really needed?
>> +
>> +    def _def_command(self, expr, info):
>> +        name = expr['command']
>> +        data = expr.get('data')
>> +        rets = expr.get('returns')
>> +        gen = expr.get('gen', True)
>> +        success_response = expr.get('success-response', True)
>> +        if isinstance(data, OrderedDict):
>> +            data = self._make_implicit_object_type(name, 'arg',
>> +                                                   self._make_members(data))
>> +        if isinstance(rets, list):
>> +            assert len(rets) == 1
>> +            rets = self._make_array_type(rets[0])
>> +        self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
>> +                                           success_response))
>> +
>> +    def _def_event(self, expr, info):
>> +        name = expr['event']
>> +        data = expr.get('data')
>> +        if isinstance(data, OrderedDict):
>> +            data = self._make_implicit_object_type(name, 'arg',
>> +                                                   self._make_members(data))
>> +        self._def_entity(QAPISchemaEvent(name, info, data))
>> +
>> +    def _def_exprs(self):
>> +        for expr_elem in self.exprs:
>> +            expr = expr_elem['expr']
>> +            info = expr_elem['info']
>> +            if 'enum' in expr:
>> +                self._def_enum_type(expr, info)
>> +            elif 'struct' in expr:
>> +                self._def_struct_type(expr, info)
>> +            elif 'union' in expr:
>> +                self._def_union_type(expr, info)
>> +            elif 'alternate' in expr:
>> +                self._def_alternate_type(expr, info)
>> +            elif 'command' in expr:
>> +                self._def_command(expr, info)
>> +            elif 'event' in expr:
>> +                self._def_event(expr, info)
>> +            else:
>> +                assert False
>> +
>> +    def check(self):
>> +        for ent in self.entity_dict.values():
>> +            ent.check(self)
>>  
>>  #
>>  # Code generation helpers
>> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
>> index 634ef2d..461c713 100644
>> --- a/tests/qapi-schema/test-qapi.py
>> +++ b/tests/qapi-schema/test-qapi.py
>> @@ -16,7 +16,7 @@ import os
>>  import sys
>>  
>>  try:
>> -    exprs = parse_schema(sys.argv[1])
>> +    exprs = QAPISchema(sys.argv[1]).get_exprs()
>>  except SystemExit:
>>      
>
> Following on earlier comment, this could have a block
>
>   except Exception as e:
>       print >>sys.stderr, e
>       sys.exit(1)
>
> instead of having the object constructor call exit itself.

Thanks!

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 29/32] qapi: Pseudo-type '**' is now unused, drop it
  2015-09-03 20:42   ` Eric Blake
@ 2015-09-04  7:14     ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04  7:14 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 09/03/2015 08:30 AM, Markus Armbruster wrote:
>> 'gen': false needs to stay for now, because netdev_add is still using
>> it.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> Reviewed-by: Eric Blake <eblake@redhat.com>
>> ---
>
>> +++ b/tests/Makefile
>> @@ -228,7 +228,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
>>  	bad-type-dict.json double-data.json unknown-expr-key.json \
>>  	redefined-type.json redefined-command.json redefined-builtin.json \
>>  	redefined-event.json command-int.json bad-data.json event-max.json \
>> -	type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
>> +	type-bypass-bad-gen.json \
>>  	args-invalid.json \
>>  	args-array-empty.json args-array-unknown.json args-int.json \
>>  	args-unknown.json args-member-unknown.json args-member-array.json \
>
> Not for this patch, but we aren't very consistent on any form of sorting
> or line length in this section. It might be nicer if it were one test
> per line (lots more line continuations) and/or alphabetical order.  If
> that sounds nice, then it's a trivial patch to add in as one of the
> followups after this series lands.

Sorting: yes, please!  One per line: meh.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-04  0:04     ` Michael Roth
@ 2015-09-04  7:19       ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04  7:19 UTC (permalink / raw)
  To: Michael Roth; +Cc: qemu-devel

Michael Roth <mdroth@linux.vnet.ibm.com> writes:

> Quoting Eric Blake (2015-09-03 15:50:16)
>> On 09/03/2015 08:30 AM, Markus Armbruster wrote:
>> > qapi/introspect.json defines the introspection schema.  It's designed
>> > for QMP introspection, but should do for similar uses, such as QGA.
>> 
>> [review to follow in separate message; I'm using this message to focus
>> on one point for easier tracking of the sub-thread]
>> 
>> > 
>> > The introspection schema does not reflect all the rules and
>> > restrictions that apply to QAPI schemata.  A valid QAPI schema has an
>> > introspection value conforming to the introspection schema, but the
>> > converse is not true.
>> > 
>> > Introspection lowers away a number of schema details, and makes
>> > implicit things explicit:
>> > 
>> > * The built-in types are declared with their JSON type.
>> > 
>> >   TODO Should we map all the integer types to just int?
>> 
>> So, given the discussion on v3, I think we will have consensus if v5
>> does the following:
>> 
>> - Merge 30 and 31 into a single patch, and drop this TODO statement (I
>> think we have agreement that exposing QMP [that is, JSON number] wire
>> form, with no additional constraints, as a single 'int', rather than the
>> underlying generated C types, is the way to go), provided that...
>> - Document in the qapi side that the schema intentionally lacks details
>> on ranges, and that consulting the qapi and/or C code may be required to
>> see what actual values are valid anywhere introspection merely says 'int',

Please review docs/qapi-code-gen.txt in this patch for this aspect.

>> - leave 32 as a separate patch, as it is complex enough, and still may
>> have debate on whether the types '123' and 'int' should have
>> corresponding array types '[123]' and '[int]' rather than the v3 patch
>> munging of '456' and '789' (I haven't yet reviewed whether v4 changed that).
>
> Agreed on all 3

Excellent.

>> Any further arguments on whether exposing just 'int' in the
>> introspection for all integral types, and/or whether patches 30 and 31
>> should be merged, are best made in response to this mail.
>
> Given the above, I don't think I have any further objections.

Thanks!

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-04  7:12     ` Markus Armbruster
@ 2015-09-04  8:51       ` Daniel P. Berrange
  2015-09-04 11:13         ` Markus Armbruster
  0 siblings, 1 reply; 75+ messages in thread
From: Daniel P. Berrange @ 2015-09-04  8:51 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

On Fri, Sep 04, 2015 at 09:12:09AM +0200, Markus Armbruster wrote:
> "Daniel P. Berrange" <berrange@redhat.com> writes:
> 
> > On Thu, Sep 03, 2015 at 04:29:53PM +0200, Markus Armbruster wrote:
> > Python has a nice tool called pep8 which can apply a
> > configurable bunch of  style checks, it is probably
> > worth someone wiring it up to make check in QEMU, since
> > we have an increasing amount of python code. An exercise
> > for a motivated reader.....
> 
> I've been using pylint (and dear-oh-dear how much pre-existing lint it
> finds!), wasn't aware of pep8, can throw it in.

The pep8 tool is design around checking the "official" python style
rules as defined by Guido, and seems to be more commonly used than
pylint IME.

> Automatic checking would be nice, but our current code is probably too
> untidy for that.  Besides, I don't have the time and energy to set it
> up now.

Yeah, no big deal.


> >> +class QAPISchema(object):
> >> +    def __init__(self, fname):
> >> +        try:
> >> + self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
> >> +        except (QAPISchemaError, QAPIExprError), err:
> >
> > Preferred syntax for exceptions these days is
> >
> >   except Foo as err
> >
> > instead of
> >
> >   except Foo, err
> >
> > since the former is portable to Python3, should we need to consider
> > that in future. Again, not a show stopper.
> 
> Does it work with Python 2.4?  That's where we're currently stuck.

Urgh, that's a shame, what distro requires such an ancient
python ? That syntax is only valid from 2.6 onwards.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-03 23:59   ` Michael Roth
@ 2015-09-04  8:54     ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04  8:54 UTC (permalink / raw)
  To: Michael Roth; +Cc: qemu-devel

Michael Roth <mdroth@linux.vnet.ibm.com> writes:

> Quoting Markus Armbruster (2015-09-03 09:30:21)
>> qapi/introspect.json defines the introspection schema.  It's designed
>> for QMP introspection, but should do for similar uses, such as QGA.
>> 
>> The introspection schema does not reflect all the rules and
>> restrictions that apply to QAPI schemata.  A valid QAPI schema has an
>> introspection value conforming to the introspection schema, but the
>> converse is not true.
>> 
>> Introspection lowers away a number of schema details, and makes
>> implicit things explicit:
>> 
>> * The built-in types are declared with their JSON type.
>> 
>>   TODO Should we map all the integer types to just int?
>> 
>> * Implicit type definitions are made explicit, and given
>>   auto-generated names:
>> 
>>   - Array types, named by appending "List" to the name of their
>>     element type, like in generated C.
>> 
>>   - The enumeration types implicitly defined by simple union types,
>>     named by appending "Kind" to the name of their simple union type,
>>     like in generated C.
>> 
>>   - Types that don't occur in generated C.  Their names start with ':'
>>     so they don't clash with the user's names.
>> 
>> * All type references are by name.
>> 
>> * The struct and union types are generalized into an object type.
>> 
>> * Base types are flattened.
>> 
>> * Commands take a single argument and return a single result.
>> 
>>   Dictionary argument or list result is an implicit type definition.
>> 
>>   The empty object type is used when a command takes no arguments or
>>   produces no results.
>> 
>>   The argument is always of object type, but the introspection schema
>>   doesn't reflect that.
>> 
>>   The 'gen': false directive is omitted as implementation detail.
>> 
>>   The 'success-response' directive is omitted as well for now, even
>>   though it's not an implementation detail, because it's not used by
>>   QMP.
>> 
>> * Events carry a single data value.
>> 
>>   Implicit type definition and empty object type use, just like for
>>   commands.
>> 
>>   The value is of object type, but the introspection schema doesn't
>>   reflect that.
>> 
>> * Types not used by commands or events are omitted.
>> 
>>   Indirect use counts as use.
>> 
>> * Optional members have a default, which can only be null right now
>> 
>>   Instead of a mandatory "optional" flag, we have an optional default.
>>   No default means mandatory, default null means optional without
>>   default value.  Non-null is available for optional with default
>>   (possible future extension).
>> 
>> * Clients should *not* look up types by name, because type names are
>>   not ABI.  Look up the command or event you're interested in, then
>>   follow the references.
>> 
>>   TODO Should we hide the type names to eliminate the temptation?
>> 
>> New generator scripts/qapi-introspect.py computes an introspection
>> value for its input, and generates a C variable holding it.
>> 
>> It can generate awfully long lines.  Marked TODO.
>> 
>> A new test-qmp-input-visitor test case feeds its result for both
>> tests/qapi-schema/qapi-schema-test.json and qapi-schema.json to a
>> QmpInputVisitor to verify it actually conforms to the schema.
>> 
>> New QMP command query-schema takes its return value from that
>> variable.  Its reply is some 85KiBytes for me right now.
>> 
>> If this turns out to be too much, we have a couple of options:
>> 
>> * We can use shorter names in the JSON.  Not the QMP style.
>> 
>> * Optionally return the sub-schema for commands and events given as
>>   arguments.
>> 
>>   Right now qmp_query_schema() sends the string literal computed by
>>   qmp-introspect.py.  To compute sub-schema at run time, we'd have to
>>   duplicate parts of qapi-introspect.py in C.  Unattractive.
>> 
>> * Let clients cache the output of query-schema.
>> 
>>   It changes only on QEMU upgrades, i.e. rarely.  Provide a command
>>   query-schema-hash.  Clients can have a cache indexed by hash, and
>>   re-query the schema only when they don't have it cached.  Even
>>   simpler: put the hash in the QMP greeting.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  .gitignore                                      |   1 +
>>  Makefile                                        |   9 +-
>>  Makefile.objs                                   |   4 +-
>>  docs/qapi-code-gen.txt                          | 226 +++++++++++++++++++-
>>  monitor.c                                       |  15 ++
>>  qapi-schema.json                                |   3 +
>>  qapi/introspect.json                            | 271 ++++++++++++++++++++++++
>>  qmp-commands.hx                                 |  17 ++
>>  scripts/qapi-introspect.py                      | 174 +++++++++++++++
>>  scripts/qapi.py                                 |  12 +-
>>  tests/.gitignore                                |   1 +
>>  tests/Makefile                                  |  10 +-
>>  tests/qapi-schema/alternate-good.out            |   1 +
>>  tests/qapi-schema/args-member-array.out         |   1 +
>>  tests/qapi-schema/comments.out                  |   1 +
>>  tests/qapi-schema/empty.out                     |   1 +
>>  tests/qapi-schema/enum-empty.out                |   1 +
>>  tests/qapi-schema/event-case.out                |   1 +
>>  tests/qapi-schema/flat-union-reverse-define.out |   1 +
>>  tests/qapi-schema/ident-with-escape.out         |   1 +
>>  tests/qapi-schema/include-relpath.out           |   1 +
>>  tests/qapi-schema/include-repetition.out        |   1 +
>>  tests/qapi-schema/include-simple.out            |   1 +
>>  tests/qapi-schema/indented-expr.out             |   1 +
>>  tests/qapi-schema/qapi-schema-test.out          |   1 +
>>  tests/qapi-schema/returns-int.out               |   1 +
>>  tests/test-qmp-input-strict.c                   |  55 +++++
>>  27 files changed, 801 insertions(+), 11 deletions(-)
>>  create mode 100644 qapi/introspect.json
>>  create mode 100644 scripts/qapi-introspect.py
>> 
>
> <snip>
>
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index e91e3b9..af19810 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -14,6 +14,9 @@
>>  # Tracing commands
>>  { 'include': 'qapi/trace.json' }
>> 
>> +# QAPI introspection
>> +{ 'include': 'qapi/introspect.json' }
>> +
>>  ##
>>  # @LostTickPolicy:
>>  #
>> diff --git a/qapi/introspect.json b/qapi/introspect.json
>> new file mode 100644
>> index 0000000..171baba
>> --- /dev/null
>> +++ b/qapi/introspect.json
>> @@ -0,0 +1,271 @@
>> +# -*- Mode: Python -*-
>> +#
>> +# QAPI/QMP introspection
>> +#
>> +# Copyright (C) 2015 Red Hat, Inc.
>> +#
>> +# Authors:
>> +#  Markus Armbruster <armbru@redhat.com>
>> +#
>> +# This work is licensed under the terms of the GNU GPL, version 2 or later.
>> +# See the COPYING file in the top-level directory.
>> +
>> +##
>> +# @query-schema
>> +#
>> +# Command query-schema exposes the QMP wire ABI as an array of
>> +# SchemaInfo.  This lets QMP clients figure out what commands and
>> +# events are available in this QEMU, and their parameters and results.
>> +#
>> +# Returns: array of @SchemaInfo, where each element describes an
>> +# entity in the ABI: command, event, type, ...
>> +#
>> +# Note: the QAPI schema is also used to help define *internal*
>> +# interfaces, by defining QAPI types.  These are not part of the QMP
>> +# wire ABI, and therefore not returned by this command.
>
> Maybe add something like:
>
> "These internal interfaces may place additional restrictions on the
> values of individual fields, so users should reference the QAPI schema
> to avoid unexpected behavior resulting from invalid field values."

Hmm, my paragraph tries to explain something else, namely that the QAPI
schema defines purely internal interfaces in addition to QMP interfaces.
The latter consist of the QMP wire interface and the internal QMP
interface.  Generated code translates between the two.

Furthermore, "additional restrictions" apply not only to "values of
individual fields", but also to structure.  For instance,
SchemaInfoObject members tag and variants must both be present or both
be absent.

What about having this somewhere:

    The SchemaInfo can't reflect all the rules and restrictions that
    apply to QMP.  It's interface introspection (figuring out what's
    there), not interface specification.  The specification is in the
    QAPI schema, and users should peruse it to understand how QMP is to
    be used.

Then continue with an example showing a restriction that's spelled out
in the QAPI schema, but can't be expressed in SchemaInfo.

> <snip>
>
>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>> index b06d74c..6232ca0 100644
>> --- a/qmp-commands.hx
>> +++ b/qmp-commands.hx
>> @@ -2168,6 +2168,23 @@ EQMP
>>      },
>> 
>>  SQMP
>> +query-schema
>> +------------
>> +
>> +Return the QMP wire schema.  The returned value is a json-array of
>> +named schema entities.  Entities are commands, events and various
>> +types.  See docs/qapi-code-gen.txt for information on their structure
>> +and intended use.
>
> We should probably duplicate any notes from QAPI schema description here
> as well, or at least direct users to reference them.

qmp-commands.hx needs to die.  

Until it's dead, some information needs to be duplicated there.  It
already points to docs/qapi-code-gen.txt, which in turn points to the
QAPI schema.  I can add a direct pointer if you think it'll help.

>> +
>> +EQMP
>> +
>> +    {
>> +        .name       = "query-schema",
>> +        .args_type  = "",
>> +        .mhandler.cmd_new = qmp_query_schema,
>> +    },
>> +
>> +SQMP
>>  query-chardev
>>  -------------
>> 
>> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
>> new file mode 100644
>> index 0000000..4bcffc4
>> --- /dev/null
>> +++ b/scripts/qapi-introspect.py
>> @@ -0,0 +1,174 @@
>> +#
>> +# QAPI introspection generator
>> +#
>> +# Copyright (C) 2015 Red Hat, Inc.
>> +#
>> +# Authors:
>> +#  Markus Armbruster <armbru@redhat.com>
>> +#
>> +# This work is licensed under the terms of the GNU GPL, version 2.
>> +# See the COPYING file in the top-level directory.
>> +
>> +from qapi import *
>> +import string
>> +
>> +# Caveman's json.dumps() replacement (we're stuck at 2.4)
>> +# TODO try to use json.dumps() once we get unstuck
>> +def to_json(obj, level=0):
>> +    if obj == None:
>> +        ret = 'null'
>> +    elif isinstance(obj, str):
>> +        ret = '"' + obj.replace('"', r'\"') + '"'
>> +    elif isinstance(obj, list):
>> +        elts = [to_json(elt, level + 1)
>> +                for elt in obj]
>> +        ret = '[' + ', '.join(elts) + ']'
>> +    elif isinstance(obj, dict):
>> +        elts = ['"%s": %s' % (key, to_json(obj[key], level + 1))
>> +                for key in sorted(obj.keys())]
>> +        ret = '{' + ', '.join(elts) + '}'
>> +    else:
>> +        assert False                # not implemented
>> +    if level == 1:
>> +        ret = '\n' + ret
>> +    return ret
>> +
>> +def to_c_string(string):
>> +    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
>> +
>> +class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
>> +    def __init__(self):
>> +        self.defn = None
>> +        self.decl = None
>> +        self.schema = None
>> +        self.jsons = None
>> +        self.used_types = None
>> +
>> +    def visit_begin(self, schema):
>> +        self.schema = schema
>> +        self.jsons = []
>> +        self.used_types = []
>> +        return QAPISchemaType   # don't visit types for now
>> +
>> +    def visit_end(self):
>> +        # visit the types that are actually used
>> +        for typ in self.used_types:
>> +            typ.visit(self)
>> +        self.jsons.sort()
>> +        # generate C
>> +        # TODO can generate awfully long lines
>
> Not sure if this is planned for this series, but a multi-line
> representation in the generated files would make things a bit
> easier to review/check. If we went a little further and made
> them pretty-printed it might make some of the docs referencing
> the output a bit more clear as well. Not a huge deal, but don't
> think it would take much code. Would suggest a JSON library or
> something but that would probably mangle the ordering, which
> wouldn't help with readability.

The obvious Python JSON library is "json — JSON encoder and decoder"[*],
which provides pretty-printing, but it's 2.6, and we're stuck in 2.4.

I tried to structure the code in a way that makes dropping in the
library relatively painless, see function to_json() above.

>> +        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')
>> +        c_string = '\n    '.join([to_c_string(line) for line in lines])
>> +        self.defn = mcgen('''
>> +const char %(c_name)s[] = %(c_string)s;
>> +''',
>> +                          c_name=c_name(name),
>> +                          c_string=c_string)
>> +        self.schema = None
>> +        self.jsons = None
>> +        self.used_types = None

[*] https://docs.python.org/2.7/library/json.html

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-04  2:03   ` Eric Blake
  2015-09-04  3:12     ` Eric Blake
@ 2015-09-04  9:55     ` Markus Armbruster
  2015-09-04 13:58       ` Eric Blake
  1 sibling, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04  9:55 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 09/03/2015 08:30 AM, Markus Armbruster wrote:
>> qapi/introspect.json defines the introspection schema.  It's designed
>> for QMP introspection, but should do for similar uses, such as QGA.
>
> [review in this sub-thread; for comments on 'int' munging or other
> followups, see other subthread]
>
> There is at least one definite bug (see multiple references below to
> [1]), and several ideas for cleanups, but in general, I think that the
> remaining changes are going to be small enough that I'd probably be okay
> if v5 started life with:
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!

> (Of course, you have every right to not add the R-b, especially if you
> want me to do another close review :)

Judgement call, as always :)

>> The introspection schema does not reflect all the rules and
>> restrictions that apply to QAPI schemata.  A valid QAPI schema has an
>> introspection value conforming to the introspection schema, but the
>> converse is not true.
>> 
>> 
>>   The empty object type is used when a command takes no arguments or
>>   produces no results.
>
> We had discussed whether to expose the empty type in a separate patch,
> to reduce the size of this patch;

I decided against it, because I feel the churn between revisions of the
series would outweigh the relatively small reduction in this patch's
size, and...

>                                   but at this point in the game, I'm
> happy to keep it as-is (the faster we get v5 out for review and into the
> tree, the faster we can process the backlog of followup patches).

... I also feel we should get this series wrapped as soon as possible.

>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>
>
>> +++ b/docs/qapi-code-gen.txt
>> @@ -494,13 +494,195 @@ Resulting in this JSON object:
>>    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
>>  
>>  
>> +== Client JSON Protocol introspection ==
>> +
>> +Clients of a Client JSON Protocol commonly need to figure out what
>> +exactly the server (QEMU) supports.
>> +
>> +For this purpose, QMP provides introspection via command query-schema.
>
> Will need tweaking to the agreed-on name query-qmp-schema...

Yup.

>> +QGA currently doesn't support introspection.
>
> ... (and we'd obviously want qga to eventually gain a matching
> counterpart, possibly 'guest-schema' or 'guest-get-schema', to match
> prevailing naming in qga/qapi-schema.json).

Think so, but I don't wish to tell the QGA maintainers what they need,
so I chose neutral language.

>> +
>> +query-schema returns a JSON array of SchemaInfo objects.  These
>> +objects together describe the wire ABI, as defined in the QAPI schema.
>
> Maybe an additional sentence here, perhaps along the lines of:
>
> Note that the wire format cannot express everything; it is not designed
> to show semantic constraints (such as when exactly one of a pair of
> mutually-exclusive optional members must be present, or when an integral
> value has specific limitations on valid values).  Introspection allows
> an application to know if a feature is present, but the application must
> obey the qapi documentation to properly interact with that feature.

Michael raised the same point, and I drafted a paragraph in my reply to
him:

    The SchemaInfo can't reflect all the rules and restrictions that
    apply to QMP.  It's interface introspection (figuring out what's
    there), not interface specification.  The specification is in the
    QAPI schema, and users should peruse it to understand how QMP is to
    be used.

Then continue with an example showing a restriction that's spelled out
in the QAPI schema, but can't be expressed in SchemaInfo.

Use mine as is?  Merge our two proposals somehow?

>> +
>> +Like any other command, query-schema is itself defined in the QAPI
>> +schema, along with the SchemaInfo type.  This text attempts to give an
>> +overview how things work.  For details you need to consult the QAPI
>> +schema.
>> +
>> +SchemaInfo objects have common members "name" and "meta-type", and
>> +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.
>
> And hopefully patch 32 munges this text to match the simplification made
> there (I haven't checked yet).

It does :)

>> +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.
>> +
>> +QAPI schema definitions not reachable that way are omitted.
>> +
>> +The SchemaInfo for a command has meta-type "command", and variant
>> +members "arg-type" and "ret-type".  The arguments member clients pass
>
> I found it easier to read with:
> s/arguments/arguments that/

Easier to read, but no longer emphasizes that the specific "arguments"
member of the { "execute" ... } form needs to conform.  I'll use yours
anyway, unless you have a better idea.

>> +with a command on the wire must conform to the type named by
>> +"arg-type", which is always an object type.  The return value the
>
> Likewise s/value/value that/

Likewise.

>> +server passes in a success response conforms to the type named by
>> +"ret-type".
>> +
>
> [not for this patch. Thinking aloud: I wonder if QGA's success-response
> could be coded in by having 'ret-type':null as a way of saying the
> command will have no response if it is successful; perhaps a bit nicer
> than exposing an additional boolean flag member]

The QAPI schema could also do it this way.  What do you think?

>> +If the command takes no arguments, "arg-type" names an object type
>> +without members.  Likewise, if the command returns nothing, "ret-type"
>> +names an object type without members.
>> +
>> +Example: the SchemaInfo for command query-schema
>> +
>> +    { "name": "query-schema", "meta-type": "command",
>> +      "arg-type": ":empty", "ret-type": "SchemaInfoList" }
>> +
>> +    Type ":empty" is an object type without members, and type
>> +    "SchemaInfoList" is the array of SchemaInfo type.
>> +
>> +The SchemaInfo for an event has meta-type "event", and variant member
>> +"arg-type".  The data member the server passes with an event conforms
>
> again, reads a little better with s/member/member that/

My remark on command arguments applies.

>> +to the type named by "arg-type".  It is always an object type.
>> +
>> +If the event carries no additional information, "arg-type" names an
>> +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.
>> +
>> +Example: the SchemaInfo for EVENT_C from section Events
>> +
>> +    { "name": "EVENT_C", "meta-type": "event",
>> +      "arg-type": ":obj-EVENT_C-arg" }
>> +
>> +    Type ":obj-EVENT_C-arg" is an implicitly defined object type with
>> +    the two members from the event's definition.
>> +
>> +The SchemaInfo for struct and union types has meta-type "object".
>> +
>> +The SchemaInfo for a struct type has variant member "members".
>
> Worth mentioning that "members" may be empty, for an empty struct?

Its value gets explained below.  I feel discussing the special case
"empty" here would be premature.

>> +
>> +The SchemaInfo for a union type additionally has variant members "tag"
>> +and "variants".
>
> Worth mentioning that "members" will never be empty, because it contains
> at least the member also referenced in "tag"?

Likewise.

>> +
>> +"members" is a JSON array describing the object's common members.

Could say "the object's common members, if any."  What do you think?

>> +Each element is a JSON object with members "name" (the member's name),
>> +"type" (the name of its type), and optionally "default".  The member
>> +is optional if "default" is present.  Currently, "default" can only
>> +have value null.  Other values are reserved for future extensions.
>> +
>> +Example: the SchemaInfo for MyType from section Struct types
>> +
>> +    { "name": "MyType", "meta-type": "object",
>> +      "members": [
>> +          { "name": "member1", "type": "str" },
>> +          { "name": "member2", "type": "int" },
>> +          { "name": "member3", "type": "str", "default": null } ] }
>> +
>> +"tag" is the name of the common member serving as type tag.

This implies that this member must exist in "members".  Want me to
repeat that explicitly?

>> +"variants" is a JSON array describing the object's variant members.
>> +Each element is a JSON object with members "case" (the value of type
>> +tag this element applies to) and "type" (the name of an object type
>> +that provides the variant members for this type tag value).
>> +
>> +Example: the SchemaInfo for flat union BlockdevOptions from section
>> +Union types
>
> Hmm, the "Union types" section has two mentions of BlockdevOptions; and
> this example matches the second. It may help readability if we rename
> one of the two examples to be distinct (preferably the first, since it
> doesn't match actual QMP).  I guess the phrase "flat union
> BlockdevOptions" is sufficient to make it obvious that we are referring
> to the second usage, but it is subtle.

Follow-up patch?

>> +
>> +    { "name": "BlockdevOptions", "meta-type": "object",
>> +      "members": [
>> +          { "name": "driver", "type": "BlockdevDriver" },
>> +          { "name": "readonly", "type": "bool"} ],
>> +      "tag": "driver",
>> +      "variants": [
>> +          { "case": "file", "type": "FileOptions" },
>> +          { "case": "qcow2", "type": "Qcow2Options" } ] }
>> +
>> +Note that base types are "flattened": its members are included in the
>> +"members" array.
>> +
>> +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.
>> +
>> +Example: the SchemaInfo for simple union BlockdevOptions from section
>> +Union types
>
> Ah, and here you refer to the other BlockdevOptions. So the point about
> a judicious rename may still be warranted.
>
>> +
>> +    { "name": "BlockdevOptions", "meta-type": "object",
>> +      "members": [
>> +          { "name": "kind", "type": "BlockdevOptionsKind" } ],
>
> [1] Ouch.  That should be "name": "type".  I pointed out the bug in v3,
> but it looks like it still hasn't been fixed.  Or rather, that our
> attempt to fix it wasn't correct.

I'll double-check.

>> +      "tag": "type",
>> +      "variants": [
>> +          { "case": "file", "type": ":obj-FileOptions-wrapper" },
>> +          { "case": "qcow2", "type": ":obj-Qcow2Options-wrapper" } ] }
>> +
>> +    Enumeration type "BlockdevOptionsKind" and the object types
>> +    ":obj-FileOptions-wrapper", ":obj-Qcow2Options-wrapper" are
>> +    implicitly defined.
>> +
>> +The SchemaInfo for an alternate type has meta-type "alternate", and
>> +variant member "members".  "members" is a JSON array.  Each element is
>> +a JSON object with member "type", which names a type.  Values of the
>> +alternate type conform to exactly one of its member types.
>> +
>> +Example: the SchemaInfo for BlockRef from section Alternate types
>> +
>> +    { "name": "BlockRef", "meta-type": "alternate",
>> +      "members": [
>> +          { "type": "BlockdevOptions" },
>> +          { "type": "str" } ] }
>> +
>> +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.
>> +
>> +Example: the SchemaInfo for ['str']
>> +
>> +    { "name": "strList", "meta-type": "array",
>> +      "element-type": "str" }
>> +
>> +The SchemaInfo for an enumeration type has meta-type "enum" and
>> +variant member "values".
>> +
>> +Example: the SchemaInfo for MyEnum from section Enumeration types
>> +
>> +    { "name": "MyEnum", "meta-type": "enum",
>> +      "values": [ "value1", "value2", "value3" ] }
>> +
>> +The SchemaInfo for a built-in type has the same name as the type in
>> +the QAPI schema (see section Built-in Types).  It has variant member
>> +"json-type" that shows how values of this type are encoded on the
>> +wire.
>> +
>> +Example: the SchemaInfo for str
>> +
>> +    { "name": "str", "meta-type": "builtin", "json-type": "string" }
>> +
>> +As explained above, type names are not part of the wire ABI.  Not even
>> +the names of built-in types.  Clients should examine member
>> +"json-type" instead of hard-coding names of built-in types.
>
> Good point (although we may still end up with clients that disobey this,
> I will certainly make sure to do this in libvirt's interactions).

We can't fully prevent misuse of our interfaces.  Where we can't, we can
still try to make correct use as easy and obvious as we can.

PATCH 32 prevents misuse of most type names by hiding them.  RFC v2 hid
all type names.  In review, we decided not to hide the names of built-in
types.  Tradeoff: makes the introspection value easier to read for
humans (good), but enables misuse by machines (bad).

Either way works for me, hide non-built-in type names (and have docs
tell users not to rely on them), or hide them all.  Got a preference?

>> +
>> +
>>  == Code generation ==
>>  
>> -Schemas are fed into 3 scripts to generate all the code/files that, paired
>> -with the core QAPI libraries, comprise everything required to take JSON
>> -commands read in by a Client JSON Protocol server, unmarshal the arguments into
>> -the underlying C types, call into the corresponding C function, and map the
>> -response back to a Client JSON Protocol response to be returned to the user.
>> +Schemas are fed into four scripts to generate all the code/files that,
>> +paired with the core QAPI libraries, comprise everything required to
>> +take JSON commands read in by a Client JSON Protocol server, unmarshal
>> +the arguments into the underlying C types, call into the corresponding
>> +C function, and map the response back to a Client JSON Protocol
>> +response to be returned to the user.
>
> Doesn't mention introspection; but then again, it also doesn't mention
> that it generates the C interface for emitting events.

Good enough for me now, but I'm of course happy to consider proposed
improvements.

>>  
>>  As an example, we'll use the following schema, which describes a single
>>  complex user-defined type (which will produce a C struct, along with a list
>> @@ -848,3 +1030,37 @@ Example:
>>      extern const char *const example_QAPIEvent_lookup[];
>>  
>>      #endif
>> +
>> +=== scripts/qapi-introspect.py ===
>> +
>> +Used to generate the introspection C code for a schema. The following
>> +files are created:
>> +
>> +$(prefix)qmp-introspect.c - Defines a string holding a JSON
>> +                            description of the schema.
>> +$(prefix)qmp-introspect.h - Declares the above string.
>> +
>> +Example:
>> +
>> +    $ python scripts/qapi-introspect.py --output-dir="qapi-generated"
>> +    --prefix="example-" example-schema.json
>> +    $ cat qapi-generated/example-qmp-introspect.c
>> +[Uninteresting stuff omitted...]
>> +
>> +    const char example_qmp_schema_json[] = "["
>> + "{\"arg-type\": \":empty\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
>> + "{\"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\"}]";
>> +    $ cat qapi-generated/example-qmp-introspect.h
>> +[Uninteresting stuff omitted...]
>> +
>> +    #ifndef EXAMPLE_QMP_INTROSPECT_H
>> +    #define EXAMPLE_QMP_INTROSPECT_H
>> +
>> +    extern const char example_qmp_schema_json[];
>> +
>> +    #endif
>
> Overall, a nice documentation addition, and solves one of my missing
> checkboxes against v3 :)

Thanks!

>> +++ b/qapi-schema.json
>> @@ -14,6 +14,9 @@
>>  # Tracing commands
>>  { 'include': 'qapi/trace.json' }
>>  
>> +# QAPI introspection
>> +{ 'include': 'qapi/introspect.json' }
>
> When we add QGA introspection, we'll want qapi/introspect.json to
> contain JUST types, and move the 'command' into qapi-schema.json proper
> (that way, qga/qapi-schema.json can also include the same types, then
> add its variation on the command).  As it won't matter until we actually
> do get to QGA, I'm okay whether you do the hoisting of the 'command' now
> or save it for later.

Yes, let's keep things simple now, and complicate them only as needed.

>> +++ b/qapi/introspect.json
>> @@ -0,0 +1,271 @@
>
>> +
>> +##
>> +# @query-schema
>> +#
>> +# Command query-schema exposes the QMP wire ABI as an array of
>> +# SchemaInfo.  This lets QMP clients figure out what commands and
>> +# events are available in this QEMU, and their parameters and results.
>> +#
>> +# Returns: array of @SchemaInfo, where each element describes an
>> +# entity in the ABI: command, event, type, ...
>> +#
>> +# Note: the QAPI schema is also used to help define *internal*
>> +# interfaces, by defining QAPI types.  These are not part of the QMP
>> +# wire ABI, and therefore not returned by this command.
>
> Same as for the docs above: may be worth a paragraph explicitly
> mentioning that the wire ABI cannot express everything, such as integer
> ranges, or such as semantics where exactly one of two mutually-exclusive
> optional members must be present.

Here's where Michael commented.

> ...
>> +##
>> +# @SchemaInfoBase
>> +#
>> +# Members common to any @SchemaInfo.
>> +#
>> +# Since: 2.5
>> +##
>> +{ 'struct': 'SchemaInfoBase',
>> +  'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } }
>> +
>> +##
>> +# @SchemaInfo
>> +#
>> +# @name: the entity's name, inherited from @base.
>
> Interesting way to document @name (I probably would have documented
> @base as part of SchemaInfoBase); but it worked out well.  No need to
> change it :)

Okay, thanks :)

>> +# 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.
>> +#
>> +# 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.
>> +#
>> +# Since: 2.5
>> +##
>> +{ 'union': 'SchemaInfo',
>> +  'base': 'SchemaInfoBase',
>> +  'discriminator': 'meta-type',
>> +  'data': {
>> +      'builtin': 'SchemaInfoBuiltin',
>> +      'enum': 'SchemaInfoEnum',
>> +      'array': 'SchemaInfoArray',
>> +      'object': 'SchemaInfoObject',
>> +      'alternate': 'SchemaInfoAlternate',
>> +      'command': 'SchemaInfoCommand',
>> +      'event': 'SchemaInfoEvent' } }
>> +
>> +##
>> +# @SchemaInfoBuiltin
>> +#
>> +# Additional SchemaInfo members for meta-type 'builtin'.
>> +#
>> +# @json-type: the JSON type used for this type on the wire.
>
> Might be worth repeating the caveat in the earlier docs that @name is
> not guaranteed to be stable, so clients should check @json-type.

Yes, the QAPI schema should contain all the important information.
Terse is okay.

"Earlier docs" = docs/qapi-code-gen.txt.  That one should be less terse,
but doesn't have to cover all the details.

> (especially true if we later decide to expose range information by
> adding more builtins or user-defined subtypes of builtins to represent
> those ranged integers).
>
> ...
>> +
>> +##
>> +# @SchemaInfoEnum
>> +#
>> +# Additional SchemaInfo members for meta-type 'enum'.
>> +#
>> +# @values: the enumeration type's values.
>> +#
>> +# Values of this type are JSON string on the wire.
>
> I'm assuming that if we add sorting of elements in a later patch, we can
> better document that guarantee here.

Yes.

>> +#
>> +# Since: 2.5
>> +##
>> +{ 'struct': 'SchemaInfoEnum',
>> +  'data': { 'values': ['str'] } }
>> +
>> +##
>> +# @SchemaInfoArray
>> +#
>> +# Additional SchemaInfo members for meta-type 'array'.
>> +#
>> +# @element-type: the array type's element type.
>> +#
>> +# Values of this type are JSON array on the wire.
>
> Is it worth adding either of these clarifications?
>
> As required by RFC 7159, the order of individual elements with in the
> array sent over the wire is assumed to be significant unless the
> documented semantics in qapi state otherwise.

Got an example where insignificance of order actually matters?

Need to check for overlap with docs/qmp/qmp-spec.txt.

> While RFC 7159 permits an array to have elements of different types, all
> elements of a QMP array should have the same type.

Implied by having an element-type, but making it explicit won't hurt.
The new 'any' type lets us do inhomogeneous arrays, though: ['any'].  If
I can think of a concise way to clarify all that, I'll add it.  If not,
we can add it later.

>> +#
>> +# Since: 2.5
>> +##
>> +{ 'struct': 'SchemaInfoArray',
>> +  'data': { 'element-type': 'str' } }
>> +
>> +##
>> +# @SchemaInfoObject
>> +#
>> +# Additional SchemaInfo members for meta-type 'object'.
>> +#
>> +# @members: the object type's (non-variant) members.
>> +#
>> +# @tag: #optional the name of the member serving as type tag.  An
>> +# element of @members with this name must exist.
>> +#
>> +# @variants: #optional variant members, i.e. additional members that
>> +# depend on the type tag's value.  Present exactly when @tag is
>> +# present.
>> +#
>> +# Values of this type are JSON object on the wire.
>
> Worth adding this clarification?
>
> As documented by RFC 7159, the wire format allows members of the JSON
> object to be sent in any order, and requires that members must not be
> duplicated.

Need to check for overlap with docs/qmp/qmp-spec.txt.

>> +#
>> +# Since: 2.5
>> +##
>> +{ 'struct': 'SchemaInfoObject',
>> +  'data': { 'members': [ 'SchemaInfoObjectMember' ],
>> +            '*tag': 'str',
>> +            '*variants': [ 'SchemaInfoObjectVariant' ] } }
>> +
>> +##
>> +# @SchemaInfoObjectMember
>> +#
>> +# An object member.
>> +#
>> +# @name: the member's name, as defined in the QAPI schema.
>> +#
>> +# @type: the name of the member's type.
>> +#
>> +# @default: #optional default when used as command parameter.
>> +#           If absent, the parameter is mandatory.
>> +#           If present, the value must be null.  The parameter is
>
> Maybe:
>
> s/The parameter/On the wire, the parameter/

Okay.

>> +#           optional, and behavior when it's missing is not specified
>> +#           here.
>> +#           Future extension: if present and non-null, the parameter
>> +#	    is optional, and defaults to this value.
>
> TAB damage.

Will fix, and search for more.

>> +#
>> +# Since: 2.5
>> +##
>> +{ 'struct': 'SchemaInfoObjectMember',
>> +  'data': { 'name': 'str', 'type': 'str', '*default': 'any' } }
>> +# @default's type must be null or match @type
>
> Is this line still worth keeping, given the more complete explanation
> above?  But we do need to use 'any' here, as that is currently the only
> way for qapi-generated code to allow for JSON null.

I'll drop it.  We'll add it back with support for non-null values
(assuming we get around to that).

>> +
>> +##
>> +# @SchemaInfoObjectVariant
>> +#
>> +# The variant members for a value of the type tag.
>> +#
>> +# @case: a value of the type tag.
>> +#
>> +# @type: the name of the object type that provides the variant members
>> +# when the type tag has value @case.
>> +#
>> +# Since: 2.5
>> +##
>> +{ 'struct': 'SchemaInfoObjectVariant',
>> +  'data': { 'case': 'str', 'type': 'str' } }
>
> Do we want to document whether all case values of the tag are guaranteed
> to be covered (happens to be true for all current qapi, but is not yet
> enforced; although that's one of my planned followup patches is to start
> enforcing it - so I guess I can document it at that time if we don't do
> it here)

Requiring all cases to be covered explicitly may have value in the QAPI
schema (more verbose, but you get a reminder to update the union after
you extended the tag enum), but it's pretty useless in introspection,
isn't it?

>> +##
>> +# @SchemaInfoCommand
>> +#
>> +# Additional SchemaInfo members for meta-type 'command'.
>> +#
>> +# @arg-type: the name of the object type that provides the command's
>> +# parameters.
>> +#
>> +# @ret-type: the name of the command's result type.
>> +#
>> +# TODO @success-response (currently irrelevant, because it's QGA, not QMP)
>
> and my idea of using 'ret-type':null may obviate the need for an actual
> 'success-response' parameter.

So let's read the TODO as "need to cover @success-response somehow for
QGA" :)

>> +++ b/scripts/qapi-introspect.py
>> @@ -0,0 +1,174 @@
>> +#
>> +# QAPI introspection generator
>> +#
>> +# Copyright (C) 2015 Red Hat, Inc.
>> +#
>> +# Authors:
>> +#  Markus Armbruster <armbru@redhat.com>
>> +#
>> +# This work is licensed under the terms of the GNU GPL, version 2.
>
> No 'or later' clause?  Oh, because your hands are tied: scripts/qapi.py
> is GPLv2-only.  Yuck.

Since I wrote this file from scratch, and even the qapi.py interface it
uses is pretty much mine, I guess I could use "or later" here.

>                       I wonder if we can fix that; although it may be
> hard getting Anthony to explain his original choice.  Separate cleanup,
> if at all. [2]

I truly wish we'd simply told all contributors from the start "this is
our license, take it or leave it".

>> +# See the COPYING file in the top-level directory.
>> +
>> +from qapi import *
>> +import string
>> +
>> +# Caveman's json.dumps() replacement (we're stuck at 2.4)
>
> s/2.4/python 2.4/

Okay.

>> +# TODO try to use json.dumps() once we get unstuck
>> +def to_json(obj, level=0):
>> +    if obj == None:
>> +        ret = 'null'
>> +    elif isinstance(obj, str):
>> +        ret = '"' + obj.replace('"', r'\"') + '"'
>> +    elif isinstance(obj, list):
>> +        elts = [to_json(elt, level + 1)
>> +                for elt in obj]
>> +        ret = '[' + ', '.join(elts) + ']'
>> +    elif isinstance(obj, dict):
>> +        elts = ['"%s": %s' % (key, to_json(obj[key], level + 1))
>
> This relies on 'key' never having any embedded ".  It happens to work
> given our naming conventions of what we pass through this dumper, but
> might be safer if you used key.replace('"', r'\"') to handle generic
> dumping.

Either that, or assert.

>> +
>> +    def _gen_member(self, member):
>> +        ret = {'name': member.name, 'type': self._use_type(member.type)}
>> +        if member.optional:
>> +            ret['default'] = None
>> +        return ret
>> +
>> +    def _gen_variants(self, tag_name, variants):
>> +        return {'tag': tag_name or 'type',
>
> [1] Ouch. Why are we still printing 'kind' as the name for simple
> unions?  Oh, it's because tag_name is ALWAYS provided by the visitor, so
> the "or 'type'" clause never fires.  I have a pending patch to change
> the C code to generate 'type' as the C code name to match the wire ABI;
> but until that patch is in, I think a hack solution might be to fix the
> visitor to supply None instead of a tag_name for simple unions.  I'll
> respond to the appropriate earlier patch.

You followed up with patches, and I'll peruse them after lunch.

>> +h_comment = '''
>> +/*
>> + * QAPI/QMP schema introspection
>> + *
>> + * Copyright (C) 2015 Red Hat, Inc.
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version
> 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + *
>> + */
>
> [2] It's interesting that you are following the pattern of
> scripts/qapi-commands.py (commit c17d9908); even there, the script
> itself was GPLv2-only, while the generated output was LGPLv2+.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-04  8:51       ` Daniel P. Berrange
@ 2015-09-04 11:13         ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04 11:13 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: qemu-devel, mdroth

"Daniel P. Berrange" <berrange@redhat.com> writes:

> On Fri, Sep 04, 2015 at 09:12:09AM +0200, Markus Armbruster wrote:
>> "Daniel P. Berrange" <berrange@redhat.com> writes:
>> 
>> > On Thu, Sep 03, 2015 at 04:29:53PM +0200, Markus Armbruster wrote:
>> > Python has a nice tool called pep8 which can apply a
>> > configurable bunch of  style checks, it is probably
>> > worth someone wiring it up to make check in QEMU, since
>> > we have an increasing amount of python code. An exercise
>> > for a motivated reader.....
>> 
>> I've been using pylint (and dear-oh-dear how much pre-existing lint it
>> finds!), wasn't aware of pep8, can throw it in.
>
> The pep8 tool is design around checking the "official" python style
> rules as defined by Guido, and seems to be more commonly used than
> pylint IME.

I'll give the pep8 tool a try.

>> Automatic checking would be nice, but our current code is probably too
>> untidy for that.  Besides, I don't have the time and energy to set it
>> up now.
>
> Yeah, no big deal.
>
>
>> >> +class QAPISchema(object):
>> >> +    def __init__(self, fname):
>> >> +        try:
>> >> + self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
>> >> +        except (QAPISchemaError, QAPIExprError), err:
>> >
>> > Preferred syntax for exceptions these days is
>> >
>> >   except Foo as err
>> >
>> > instead of
>> >
>> >   except Foo, err
>> >
>> > since the former is portable to Python3, should we need to consider
>> > that in future. Again, not a show stopper.
>> 
>> Does it work with Python 2.4?  That's where we're currently stuck.
>
> Urgh, that's a shame, what distro requires such an ancient
> python ? That syntax is only valid from 2.6 onwards.

We bumped our minimum GLib version to 2.22 in May, which *might* have
killed off ancient Python indirectly: GLib 2.22 is from 2009, Python 2.4
is from 2004, and 2.6 if from 2008.

I vaguely remember we went only to 2.22 due to some version of SLES.
Anybody knows the oldest version of SLES still at 2.22, and what version
of Python it provides?

RHEL-5 is out: nominally glib2-2.12 (and python 2.4.3).

RHEL-6 has glib2-2.28 and python 2.6.6.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation Markus Armbruster
  2015-09-03 16:11   ` Daniel P. Berrange
  2015-09-04  3:05   ` Eric Blake
@ 2015-09-04 12:34   ` Eric Blake
  2015-09-04 14:40     ` Markus Armbruster
  2 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-04 12:34 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 4186 bytes --]

On 09/03/2015 08:29 AM, Markus Armbruster wrote:
> The QAPI code generators work with a syntax tree (nested dictionaries)
> plus a few symbol tables (also dictionaries) on the side.
> 

> +
> +class QAPISchemaArrayType(QAPISchemaType):
> +    def __init__(self, name, info, element_type):
> +        QAPISchemaType.__init__(self, name, info)
> +        assert isinstance(element_type, str)
> +        self.element_type_name = element_type
> +        self.element_type = None

Would it make sense to have:

self._element_type_name = element_type
self.element_type = None

> +    def check(self, schema):
> +        self.element_type = schema.lookup_type(self.element_type_name)
> +        assert self.element_type
> +

and the corresponding
self.element_type = schema.lookup_type(self._element_type_name)

to make it obvious that ._element_type_name is for internal use only,
while .element_type is the preferred member for queries/manipulation by
qapi-*.py, which should only be using this object after check() has been
run?

> +class QAPISchemaObjectType(QAPISchemaType):
> +    def __init__(self, name, info, base, local_members, variants):
> +        QAPISchemaType.__init__(self, name, info)
> +        assert base == None or isinstance(base, str)
> +        for m in local_members:
> +            assert isinstance(m, QAPISchemaObjectTypeMember)
> +        assert variants == None \
> +            or isinstance(variants, QAPISchemaObjectTypeVariants)
> +        self.base_name = base
> +        self.base = None
> +        self.local_members = local_members
> +        self.variants = variants
> +        self.members = None

Similarly for self._base_name

> +
> +class QAPISchemaObjectTypeMember(object):
> +    def __init__(self, name, typ, optional):
> +        assert isinstance(name, str)
> +        assert isinstance(typ, str)
> +        assert isinstance(optional, bool)
> +        self.name = name
> +        self.type_name = typ
> +        self.type = None
> +        self.optional = optional

and for self._type_name

> +class QAPISchemaObjectTypeVariants(object):
> +    def __init__(self, tag_name, tag_enum, variants):
> +        assert tag_name == None or isinstance(tag_name, str)
> +        assert tag_enum == None or isinstance(tag_enum, str)
> +        for v in variants:
> +            assert isinstance(v, QAPISchemaObjectTypeVariant)
> +        self.tag_name = tag_name
> +        if tag_name:
> +            assert not tag_enum
> +            self.tag_member = None
> +        else:
> +            self.tag_member = QAPISchemaObjectTypeMember('kind', tag_enum,
> +                                                         False)

and eventually for self._tag_name (except that we MUST expose
self._tag_name until after we fix the C struct to use 'type' instead of
'kind').


> +class QAPISchemaCommand(QAPISchemaEntity):
> +    def __init__(self, name, info, arg_type, ret_type, gen, success_response):
> +        QAPISchemaEntity.__init__(self, name, info)
> +        assert not arg_type or isinstance(arg_type, str)
> +        assert not ret_type or isinstance(ret_type, str)
> +        self.arg_type_name = arg_type
> +        self.arg_type = None
> +        self.ret_type_name = ret_type
> +        self.ret_type = None
> +        self.gen = gen
> +        self.success_response = success_response

and for self._art_type_name and self._ret_type_name

> +
> +class QAPISchemaEvent(QAPISchemaEntity):
> +    def __init__(self, name, info, arg_type):
> +        QAPISchemaEntity.__init__(self, name, info)
> +        assert not arg_type or isinstance(arg_type, str)
> +        self.arg_type_name = arg_type
> +        self.arg_type = None

and for self._arg_type_name

Basically, if I'm understanding the python conventions correctly, naming
an instance member with leading underscore implies that it is for
private use only; and if nothing else, the rename proves whether any of
the other python files are relying on a leaky abstraction.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection
  2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
                   ` (31 preceding siblings ...)
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 32/32] qapi-introspect: Hide type names Markus Armbruster
@ 2015-09-04 12:57 ` Marc-André Lureau
  2015-09-04 14:14   ` Eric Blake
  2015-09-04 15:55   ` Markus Armbruster
  32 siblings, 2 replies; 75+ messages in thread
From: Marc-André Lureau @ 2015-09-04 12:57 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

Hi Markus

On Thu, Sep 3, 2015 at 4:29 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Series is based on "[PATCH 00/26] qapi: Another round of fixes and
> cleanups" (which actually has 33 patches now).  It also needs my
> "[PATCH for-2.4 0/2] qom: Fix misuse of Error API" to pass tests.
>
> You can fetch it from my git://repo.or.cz/qemu/armbru.git branch
> qapi-introspect.

For the records, I have also rebased my qapi-related patches on top of
your branch. It adds: varargs support, doc generation, use of
qmp_dispatch() & async support.

https://github.com/elmarco/qemu branch qapi

cheers
-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-04  9:55     ` Markus Armbruster
@ 2015-09-04 13:58       ` Eric Blake
  2015-09-04 15:08         ` Markus Armbruster
  0 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-04 13:58 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, mdroth

[-- Attachment #1: Type: text/plain, Size: 9789 bytes --]

On 09/04/2015 03:55 AM, Markus Armbruster wrote:

>>> +
>>> +query-schema returns a JSON array of SchemaInfo objects.  These
>>> +objects together describe the wire ABI, as defined in the QAPI schema.
>>
>> Maybe an additional sentence here, perhaps along the lines of:
>>
>> Note that the wire format cannot express everything; it is not designed
>> to show semantic constraints (such as when exactly one of a pair of
>> mutually-exclusive optional members must be present, or when an integral
>> value has specific limitations on valid values).  Introspection allows
>> an application to know if a feature is present, but the application must
>> obey the qapi documentation to properly interact with that feature.
> 
> Michael raised the same point, and I drafted a paragraph in my reply to
> him:
> 
>     The SchemaInfo can't reflect all the rules and restrictions that
>     apply to QMP.  It's interface introspection (figuring out what's
>     there), not interface specification.  The specification is in the
>     QAPI schema, and users should peruse it to understand how QMP is to
>     be used.
> 
> Then continue with an example showing a restriction that's spelled out
> in the QAPI schema, but can't be expressed in SchemaInfo.
> 
> Use mine as is?  Merge our two proposals somehow?

Your wording works okay for me, but if you want to merge any elements of
my wording in, go for it.


>>> +The SchemaInfo for a command has meta-type "command", and variant
>>> +members "arg-type" and "ret-type".  The arguments member clients pass
>>
>> I found it easier to read with:
>> s/arguments/arguments that/
> 
> Easier to read, but no longer emphasizes that the specific "arguments"
> member of the { "execute" ... } form needs to conform.  I'll use yours
> anyway, unless you have a better idea.
> 
>>> +with a command on the wire must conform to the type named by
>>> +"arg-type", which is always an object type.  The return value the
>>
>> Likewise s/value/value that/
> 
> Likewise.
> 
>>> +server passes in a success response conforms to the type named by
>>> +"ret-type".

Maybe:

On the wire, the "arguments" key of a client's "execute" command must
conform to the type named by "arg-type", which is always an object type.
The "return" key that the server passes in a success response will
always conform to the type named by "ret-type".

Up to you; we'll see how it looks in v5, and at that point, any further
word-smithing can be done in followup patches rather than delaying this
series further.

>> [not for this patch. Thinking aloud: I wonder if QGA's success-response
>> could be coded in by having 'ret-type':null as a way of saying the
>> command will have no response if it is successful; perhaps a bit nicer
>> than exposing an additional boolean flag member]
> 
> The QAPI schema could also do it this way.  What do you think?

Sounds like I have another trivial followup patch added to my work queue :)


>>> +The SchemaInfo for an event has meta-type "event", and variant member
>>> +"arg-type".  The data member the server passes with an event conforms
>>
>> again, reads a little better with s/member/member that/
> 
> My remark on command arguments applies.
> 
>>> +to the type named by "arg-type".  It is always an object type.

Maybe:

On the wire, the "data" member included in any event passed by the
server will conform to the type named by "arg-type".

I'm not sure if we need to document whether it is always an object type;
command arguments are user-supplied, while event data is
server-supplied.  And there's the question of any possible duplication
with the QMP spec file.  I guess I'm fine as long as we aren't stating
conflicting things between the two places.


>>> +
>>> +"members" is a JSON array describing the object's common members.
> 
> Could say "the object's common members, if any."  What do you think?

Yes, adding the 'if any' helps.


>>
>> Hmm, the "Union types" section has two mentions of BlockdevOptions; and
>> this example matches the second. It may help readability if we rename
>> one of the two examples to be distinct (preferably the first, since it
>> doesn't match actual QMP).  I guess the phrase "flat union
>> BlockdevOptions" is sufficient to make it obvious that we are referring
>> to the second usage, but it is subtle.
> 
> Follow-up patch?

Yes.

>>
>> [1] Ouch.  That should be "name": "type".  I pointed out the bug in v3,
>> but it looks like it still hasn't been fixed.  Or rather, that our
>> attempt to fix it wasn't correct.
> 
> I'll double-check.

As you noted later, I've already done that legwork.


>>> +
>>> +As explained above, type names are not part of the wire ABI.  Not even
>>> +the names of built-in types.  Clients should examine member
>>> +"json-type" instead of hard-coding names of built-in types.
>>
>> Good point (although we may still end up with clients that disobey this,
>> I will certainly make sure to do this in libvirt's interactions).
> 
> We can't fully prevent misuse of our interfaces.  Where we can't, we can
> still try to make correct use as easy and obvious as we can.
> 
> PATCH 32 prevents misuse of most type names by hiding them.  RFC v2 hid
> all type names.  In review, we decided not to hide the names of built-in
> types.  Tradeoff: makes the introspection value easier to read for
> humans (good), but enables misuse by machines (bad).
> 
> Either way works for me, hide non-built-in type names (and have docs
> tell users not to rely on them), or hide them all.  Got a preference?
> 

I'm still leaning towards exposing built-in type names directly, and
furthermore exposing arrays via '[123]' naming. As you say, correct
clients shouldn't be abusing this, but it IS easier to see, and I'm
having a hard time seeing how it could paint us into corners in the
future for having exposed too much information up front (while we DO
rename object types and don't want that to affect ABI, I don't see us
trying to rename builtin types - at most, we might add subtypes with
range or other restrictions, but those would be new types and still
leave the basic 'int' and 'str' unchanged).  So leaving it as just the
documentation on what proper clients should do is sufficient; don't go
back to the v2 munging of builtin names.

>>> +++ b/qapi/introspect.json
>>> @@ -0,0 +1,271 @@
>>
>>> +
>>> +##
>>> +# @query-schema
>>> +#
>>> +# Command query-schema exposes the QMP wire ABI as an array of
>>> +# SchemaInfo.  This lets QMP clients figure out what commands and
>>> +# events are available in this QEMU, and their parameters and results.
>>> +#
>>> +# Returns: array of @SchemaInfo, where each element describes an
>>> +# entity in the ABI: command, event, type, ...
>>> +#
>>> +# Note: the QAPI schema is also used to help define *internal*
>>> +# interfaces, by defining QAPI types.  These are not part of the QMP
>>> +# wire ABI, and therefore not returned by this command.
>>
>> Same as for the docs above: may be worth a paragraph explicitly
>> mentioning that the wire ABI cannot express everything, such as integer
>> ranges, or such as semantics where exactly one of two mutually-exclusive
>> optional members must be present.
> 
> Here's where Michael commented.

Actually, he mentioned the .hx file, which I didn't. I don't know how
many duplicate places need to refer to the same information, or just
abbreviate by pointing to other places. But I'll be happy as long as the
information is present at least somewhere, and that somewhere is easy to
find (perhaps by good references rather than duplications in the other
places).


>>
>> As required by RFC 7159, the order of individual elements with in the
>> array sent over the wire is assumed to be significant unless the
>> documented semantics in qapi state otherwise.
> 
> Got an example where insignificance of order actually matters?

migrate-set-capabilities does not care what order the 'capabilities'
array is in, for example.


>>> +{ 'struct': 'SchemaInfoObjectVariant',
>>> +  'data': { 'case': 'str', 'type': 'str' } }
>>
>> Do we want to document whether all case values of the tag are guaranteed
>> to be covered (happens to be true for all current qapi, but is not yet
>> enforced; although that's one of my planned followup patches is to start
>> enforcing it - so I guess I can document it at that time if we don't do
>> it here)
> 
> Requiring all cases to be covered explicitly may have value in the QAPI
> schema (more verbose, but you get a reminder to update the union after
> you extended the tag enum), but it's pretty useless in introspection,
> isn't it?

At first glance, I wouldn't call it useless.  But thinking about it
more: Right now, if not all members of the enum are listed, the
generated code abort()s when passing the uncovered enum value.  But
ideally we want semantics where an omitted case in the qapi (or maybe
explicit 'case':null or 'case':{} notation) results in adding no further
variant members to the overall object.  In that case, knowing that the
tag enum value is valid and maps to ':empty' on the wire is worth
advertising, even if we choose to allow omitting a case in the qapi
.json as the syntax for that action.

So we can touch up the wording at the same time we fix code to not abort().


>>> +
>>> +# Caveman's json.dumps() replacement (we're stuck at 2.4)
>>
>> s/2.4/python 2.4/
> 
> Okay.

Of course, if Dan's query proves we can bump our minimum to python 2.6
anyways, this may go away soon.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 31/32] qapi-introspect: Map all integer types to 'int'
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 31/32] qapi-introspect: Map all integer types to 'int' Markus Armbruster
@ 2015-09-04 14:00   ` Eric Blake
  0 siblings, 0 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04 14:00 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 820 bytes --]

On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> How many bits we use internally is an implementation detail.  It could
> be pressed into external interface service as very approximate range
> information, but that's probably a bad idea.  If we need range
> information, we better do it properly.
> 
> Reduces output of query-schema by a negligible 0.5 out of 85KiB.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  docs/qapi-code-gen.txt     | 11 ++++++++---
>  scripts/qapi-introspect.py |  7 +++++++
>  2 files changed, 15 insertions(+), 3 deletions(-)

Wording is nice; and as discussed this will be squashed into 30 for v5.
Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 32/32] qapi-introspect: Hide type names
  2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 32/32] qapi-introspect: Hide type names Markus Armbruster
@ 2015-09-04 14:07   ` Eric Blake
  2015-09-04 15:52     ` Markus Armbruster
  0 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-04 14:07 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 3085 bytes --]

On 09/03/2015 08:30 AM, Markus Armbruster wrote:
> 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>
> ---
>  docs/qapi-code-gen.txt     | 36 +++++++++++++++++-------------------
>  qapi/introspect.json       | 11 +++--------
>  scripts/qapi-introspect.py | 41 +++++++++++++++++++++++++++++++++++------
>  3 files changed, 55 insertions(+), 33 deletions(-)
> 
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index a079b51..94fc296 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -516,13 +516,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

Missing a trailing '.'

>  
>  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 document use
> +meaningful type names instead.
> +
> +To examine a type, start with a the command or event using it, then

s/a the/the/

> +follow references by name.
>  

> @@ -1053,13 +1051,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\"}, "

I thought you said that this document would use the meaningful type
names?  Oh, I see - the earlier examples (not touched by this commit) DO
use meaningful names; while _this_ example uses the munged names. I
don't know if any additional wording would help.

Would it be worth documenting -u as a usage argument for
qapi-introspect.py?  [For that matter, should we document what -m for
middle-mode does for qapi-commands.py? But as a separate patch]

I still think that masking arrays as "[123]" is nicer, but that can be a
followup patch (especially since I've already posted it in one of my
numerous RFC followups, which I'll have to rebase anyways)

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection
  2015-09-04 12:57 ` [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Marc-André Lureau
@ 2015-09-04 14:14   ` Eric Blake
  2015-09-04 15:55   ` Markus Armbruster
  1 sibling, 0 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04 14:14 UTC (permalink / raw)
  To: Marc-André Lureau, Markus Armbruster; +Cc: QEMU, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1164 bytes --]

On 09/04/2015 06:57 AM, Marc-André Lureau wrote:
> Hi Markus
> 
> On Thu, Sep 3, 2015 at 4:29 PM, Markus Armbruster <armbru@redhat.com> wrote:
>> Series is based on "[PATCH 00/26] qapi: Another round of fixes and
>> cleanups" (which actually has 33 patches now).  It also needs my
>> "[PATCH for-2.4 0/2] qom: Fix misuse of Error API" to pass tests.
>>
>> You can fetch it from my git://repo.or.cz/qemu/armbru.git branch
>> qapi-introspect.
> 
> For the records, I have also rebased my qapi-related patches on top of
> your branch. It adds: varargs support, doc generation, use of
> qmp_dispatch() & async support.
> 
> https://github.com/elmarco/qemu branch qapi
> 

I don't think we want varargs; you added it for 'netdev_add', but the
right fix for that is to use a flat union type and fix 'command' to
allow a boxed union argument, rather than adding an untyped varargs.

I guess I need to rebase my pending patches, and get them posted, since
it touches some of the same things.  We'll get netdev_add fixed soon
enough :)

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation
  2015-09-04 12:34   ` Eric Blake
@ 2015-09-04 14:40     ` Markus Armbruster
  2015-09-04 23:51       ` [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects Eric Blake
  0 siblings, 1 reply; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04 14:40 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 09/03/2015 08:29 AM, Markus Armbruster wrote:
>> The QAPI code generators work with a syntax tree (nested dictionaries)
>> plus a few symbol tables (also dictionaries) on the side.
>> 
>
>> +
>> +class QAPISchemaArrayType(QAPISchemaType):
>> +    def __init__(self, name, info, element_type):
>> +        QAPISchemaType.__init__(self, name, info)
>> +        assert isinstance(element_type, str)
>> +        self.element_type_name = element_type
>> +        self.element_type = None
>
> Would it make sense to have:
>
> self._element_type_name = element_type
> self.element_type = None
>
>> +    def check(self, schema):
>> +        self.element_type = schema.lookup_type(self.element_type_name)
>> +        assert self.element_type
>> +
>
> and the corresponding
> self.element_type = schema.lookup_type(self._element_type_name)
>
> to make it obvious that ._element_type_name is for internal use only,
> while .element_type is the preferred member for queries/manipulation by
> qapi-*.py, which should only be using this object after check() has been
> run?
>
>> +class QAPISchemaObjectType(QAPISchemaType):
>> +    def __init__(self, name, info, base, local_members, variants):
>> +        QAPISchemaType.__init__(self, name, info)
>> +        assert base == None or isinstance(base, str)
>> +        for m in local_members:
>> +            assert isinstance(m, QAPISchemaObjectTypeMember)
>> +        assert variants == None \
>> +            or isinstance(variants, QAPISchemaObjectTypeVariants)
>> +        self.base_name = base
>> +        self.base = None
>> +        self.local_members = local_members
>> +        self.variants = variants
>> +        self.members = None
>
> Similarly for self._base_name
>
>> +
>> +class QAPISchemaObjectTypeMember(object):
>> +    def __init__(self, name, typ, optional):
>> +        assert isinstance(name, str)
>> +        assert isinstance(typ, str)
>> +        assert isinstance(optional, bool)
>> +        self.name = name
>> +        self.type_name = typ
>> +        self.type = None
>> +        self.optional = optional
>
> and for self._type_name
>
>> +class QAPISchemaObjectTypeVariants(object):
>> +    def __init__(self, tag_name, tag_enum, variants):
>> +        assert tag_name == None or isinstance(tag_name, str)
>> +        assert tag_enum == None or isinstance(tag_enum, str)
>> +        for v in variants:
>> +            assert isinstance(v, QAPISchemaObjectTypeVariant)
>> +        self.tag_name = tag_name
>> +        if tag_name:
>> +            assert not tag_enum
>> +            self.tag_member = None
>> +        else:
>> +            self.tag_member = QAPISchemaObjectTypeMember('kind', tag_enum,
>> +                                                         False)
>
> and eventually for self._tag_name (except that we MUST expose
> self._tag_name until after we fix the C struct to use 'type' instead of
> 'kind').

Python lets you use variants._tag_name.  The leading underscore is
advisory (except for wildcard imports, which don't apply here).

>> +class QAPISchemaCommand(QAPISchemaEntity):
>> + def __init__(self, name, info, arg_type, ret_type, gen,
>> success_response):
>> +        QAPISchemaEntity.__init__(self, name, info)
>> +        assert not arg_type or isinstance(arg_type, str)
>> +        assert not ret_type or isinstance(ret_type, str)
>> +        self.arg_type_name = arg_type
>> +        self.arg_type = None
>> +        self.ret_type_name = ret_type
>> +        self.ret_type = None
>> +        self.gen = gen
>> +        self.success_response = success_response
>
> and for self._art_type_name and self._ret_type_name
>
>> +
>> +class QAPISchemaEvent(QAPISchemaEntity):
>> +    def __init__(self, name, info, arg_type):
>> +        QAPISchemaEntity.__init__(self, name, info)
>> +        assert not arg_type or isinstance(arg_type, str)
>> +        self.arg_type_name = arg_type
>> +        self.arg_type = None
>
> and for self._arg_type_name
>
> Basically, if I'm understanding the python conventions correctly, naming
> an instance member with leading underscore implies that it is for
> private use only; and if nothing else, the rename proves whether any of
> the other python files are relying on a leaky abstraction.

I followed that convention for methods, but didn't bother for instance
variables.  Perhaps I should.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection
  2015-09-04 13:58       ` Eric Blake
@ 2015-09-04 15:08         ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04 15:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 09/04/2015 03:55 AM, Markus Armbruster wrote:
>
>>>> +
>>>> +query-schema returns a JSON array of SchemaInfo objects.  These
>>>> +objects together describe the wire ABI, as defined in the QAPI schema.
>>>
>>> Maybe an additional sentence here, perhaps along the lines of:
>>>
>>> Note that the wire format cannot express everything; it is not designed
>>> to show semantic constraints (such as when exactly one of a pair of
>>> mutually-exclusive optional members must be present, or when an integral
>>> value has specific limitations on valid values).  Introspection allows
>>> an application to know if a feature is present, but the application must
>>> obey the qapi documentation to properly interact with that feature.
>> 
>> Michael raised the same point, and I drafted a paragraph in my reply to
>> him:
>> 
>>     The SchemaInfo can't reflect all the rules and restrictions that
>>     apply to QMP.  It's interface introspection (figuring out what's
>>     there), not interface specification.  The specification is in the
>>     QAPI schema, and users should peruse it to understand how QMP is to
>>     be used.
>> 
>> Then continue with an example showing a restriction that's spelled out
>> in the QAPI schema, but can't be expressed in SchemaInfo.
>> 
>> Use mine as is?  Merge our two proposals somehow?
>
> Your wording works okay for me, but if you want to merge any elements of
> my wording in, go for it.

I'll probably use mine, but I'll look at it once more when I respin this
patch.

>>>> +The SchemaInfo for a command has meta-type "command", and variant
>>>> +members "arg-type" and "ret-type".  The arguments member clients pass
>>>
>>> I found it easier to read with:
>>> s/arguments/arguments that/
>> 
>> Easier to read, but no longer emphasizes that the specific "arguments"
>> member of the { "execute" ... } form needs to conform.  I'll use yours
>> anyway, unless you have a better idea.
>> 
>>>> +with a command on the wire must conform to the type named by
>>>> +"arg-type", which is always an object type.  The return value the
>>>
>>> Likewise s/value/value that/
>> 
>> Likewise.
>> 
>>>> +server passes in a success response conforms to the type named by
>>>> +"ret-type".
>
> Maybe:
>
> On the wire, the "arguments" key of a client's "execute" command must
> conform to the type named by "arg-type", which is always an object type.
> The "return" key that the server passes in a success response will
> always conform to the type named by "ret-type".
>
> Up to you; we'll see how it looks in v5, and at that point, any further
> word-smithing can be done in followup patches rather than delaying this
> series further.

Sounds nice, I'll probably adopt it.

>>> [not for this patch. Thinking aloud: I wonder if QGA's success-response
>>> could be coded in by having 'ret-type':null as a way of saying the
>>> command will have no response if it is successful; perhaps a bit nicer
>>> than exposing an additional boolean flag member]
>> 
>> The QAPI schema could also do it this way.  What do you think?
>
> Sounds like I have another trivial followup patch added to my work queue :)
>
>
>>>> +The SchemaInfo for an event has meta-type "event", and variant member
>>>> +"arg-type".  The data member the server passes with an event conforms
>>>
>>> again, reads a little better with s/member/member that/
>> 
>> My remark on command arguments applies.
>> 
>>>> +to the type named by "arg-type".  It is always an object type.
>
> Maybe:
>
> On the wire, the "data" member included in any event passed by the
> server will conform to the type named by "arg-type".
>
> I'm not sure if we need to document whether it is always an object type;
> command arguments are user-supplied, while event data is
> server-supplied.  And there's the question of any possible duplication
> with the QMP spec file.  I guess I'm fine as long as we aren't stating
> conflicting things between the two places.

I feel duplication is okay when it makes qapi-code-gen.txt easier to
understand.

>>>> +
>>>> +"members" is a JSON array describing the object's common members.
>> 
>> Could say "the object's common members, if any."  What do you think?
>
> Yes, adding the 'if any' helps.

Sold.

>>>
>>> Hmm, the "Union types" section has two mentions of BlockdevOptions; and
>>> this example matches the second. It may help readability if we rename
>>> one of the two examples to be distinct (preferably the first, since it
>>> doesn't match actual QMP).  I guess the phrase "flat union
>>> BlockdevOptions" is sufficient to make it obvious that we are referring
>>> to the second usage, but it is subtle.
>> 
>> Follow-up patch?
>
> Yes.
>
>>>
>>> [1] Ouch.  That should be "name": "type".  I pointed out the bug in v3,
>>> but it looks like it still hasn't been fixed.  Or rather, that our
>>> attempt to fix it wasn't correct.
>> 
>> I'll double-check.
>
> As you noted later, I've already done that legwork.

Thanks a lot for that!

>>>> +
>>>> +As explained above, type names are not part of the wire ABI.  Not even
>>>> +the names of built-in types.  Clients should examine member
>>>> +"json-type" instead of hard-coding names of built-in types.
>>>
>>> Good point (although we may still end up with clients that disobey this,
>>> I will certainly make sure to do this in libvirt's interactions).
>> 
>> We can't fully prevent misuse of our interfaces.  Where we can't, we can
>> still try to make correct use as easy and obvious as we can.
>> 
>> PATCH 32 prevents misuse of most type names by hiding them.  RFC v2 hid
>> all type names.  In review, we decided not to hide the names of built-in
>> types.  Tradeoff: makes the introspection value easier to read for
>> humans (good), but enables misuse by machines (bad).
>> 
>> Either way works for me, hide non-built-in type names (and have docs
>> tell users not to rely on them), or hide them all.  Got a preference?
>> 
>
> I'm still leaning towards exposing built-in type names directly, and
> furthermore exposing arrays via '[123]' naming. As you say, correct
> clients shouldn't be abusing this, but it IS easier to see, and I'm
> having a hard time seeing how it could paint us into corners in the
> future for having exposed too much information up front (while we DO
> rename object types and don't want that to affect ABI, I don't see us
> trying to rename builtin types - at most, we might add subtypes with
> range or other restrictions, but those would be new types and still
> leave the basic 'int' and 'str' unchanged).  So leaving it as just the
> documentation on what proper clients should do is sufficient; don't go
> back to the v2 munging of builtin names.

Okay, no change then.  We can explore arrays in follow-up patches.

>>>> +++ b/qapi/introspect.json
>>>> @@ -0,0 +1,271 @@
>>>
>>>> +
>>>> +##
>>>> +# @query-schema
>>>> +#
>>>> +# Command query-schema exposes the QMP wire ABI as an array of
>>>> +# SchemaInfo.  This lets QMP clients figure out what commands and
>>>> +# events are available in this QEMU, and their parameters and results.
>>>> +#
>>>> +# Returns: array of @SchemaInfo, where each element describes an
>>>> +# entity in the ABI: command, event, type, ...
>>>> +#
>>>> +# Note: the QAPI schema is also used to help define *internal*
>>>> +# interfaces, by defining QAPI types.  These are not part of the QMP
>>>> +# wire ABI, and therefore not returned by this command.
>>>
>>> Same as for the docs above: may be worth a paragraph explicitly
>>> mentioning that the wire ABI cannot express everything, such as integer
>>> ranges, or such as semantics where exactly one of two mutually-exclusive
>>> optional members must be present.
>> 
>> Here's where Michael commented.
>
> Actually, he mentioned the .hx file, which I didn't. I don't know how
> many duplicate places need to refer to the same information, or just
> abbreviate by pointing to other places. But I'll be happy as long as the
> information is present at least somewhere, and that somewhere is easy to
> find (perhaps by good references rather than duplications in the other
> places).

Judging from how I look for information, the QAPI schema should have the
complete reference information, while pointers suffice for
qmp-commands.hx.  docs/qapi-code-gen.txt is introduction / big picture,
so it should *explain* things, but it doesn't have to be exhaustively
complete.

>>>
>>> As required by RFC 7159, the order of individual elements with in the
>>> array sent over the wire is assumed to be significant unless the
>>> documented semantics in qapi state otherwise.
>> 
>> Got an example where insignificance of order actually matters?
>
> migrate-set-capabilities does not care what order the 'capabilities'
> array is in, for example.

Correct, but I'm not sure it matters enough to justify repeating basic
JSON semantics here once more, so let's leave things as they are.  Could
be just me getting worn out by this series (I really, really want it
finished).  Perhaps I'll be more amenable to a follow-up patch than to
changing it now.

>>>> +{ 'struct': 'SchemaInfoObjectVariant',
>>>> +  'data': { 'case': 'str', 'type': 'str' } }
>>>
>>> Do we want to document whether all case values of the tag are guaranteed
>>> to be covered (happens to be true for all current qapi, but is not yet
>>> enforced; although that's one of my planned followup patches is to start
>>> enforcing it - so I guess I can document it at that time if we don't do
>>> it here)
>> 
>> Requiring all cases to be covered explicitly may have value in the QAPI
>> schema (more verbose, but you get a reminder to update the union after
>> you extended the tag enum), but it's pretty useless in introspection,
>> isn't it?
>
> At first glance, I wouldn't call it useless.  But thinking about it
> more: Right now, if not all members of the enum are listed, the
> generated code abort()s when passing the uncovered enum value.  But
> ideally we want semantics where an omitted case in the qapi (or maybe
> explicit 'case':null or 'case':{} notation) results in adding no further
> variant members to the overall object.  In that case, knowing that the
> tag enum value is valid and maps to ':empty' on the wire is worth
> advertising, even if we choose to allow omitting a case in the qapi
> .json as the syntax for that action.
>
> So we can touch up the wording at the same time we fix code to not abort().

SchemaInfoObject needs to show the complete set of accepted type tag
values, and for each value, the variant members.

Currently, SchemaInfoObject.tag always names a member of enumeration
type (I guess I should add that to the docs).  That type is the set of
accepted type tag values.

SchemaInfoObject.variants neither adds nor removes from that set.  All
it does is show variant members.

If the object has none for a certain type tag value, then a natural and
compact way to encode that is not to have it in .variants.

Another way is to have a rather pointless .variants member that
explicitly states "no members", basically {"case": "FOO", "type":
":empty"}.

Only if we permitted the type tags type to have more values than object
actually accepts as type tags, the presence of a type tag value in
.variants becomes meaningful even when it adds no variant members.

Why would we ever want that?

>>>> +
>>>> +# Caveman's json.dumps() replacement (we're stuck at 2.4)
>>>
>>> s/2.4/python 2.4/
>> 
>> Okay.
>
> Of course, if Dan's query proves we can bump our minimum to python 2.6
> anyways, this may go away soon.

I'll start a thread.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 32/32] qapi-introspect: Hide type names
  2015-09-04 14:07   ` Eric Blake
@ 2015-09-04 15:52     ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04 15:52 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 09/03/2015 08:30 AM, Markus Armbruster wrote:
>> 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>
>> ---
>>  docs/qapi-code-gen.txt     | 36 +++++++++++++++++-------------------
>>  qapi/introspect.json       | 11 +++--------
>>  scripts/qapi-introspect.py | 41 +++++++++++++++++++++++++++++++++++------
>>  3 files changed, 55 insertions(+), 33 deletions(-)
>> 
>> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
>> index a079b51..94fc296 100644
>> --- a/docs/qapi-code-gen.txt
>> +++ b/docs/qapi-code-gen.txt
>> @@ -516,13 +516,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
>
> Missing a trailing '.'

Will fix.

>>  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 document use
>> +meaningful type names instead.
>> +
>> +To examine a type, start with a the command or event using it, then
>
> s/a the/the/

Will fix.

>> +follow references by name.
>>  
>
>> @@ -1053,13 +1051,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\"}, "
>
> I thought you said that this document would use the meaningful type
> names?  Oh, I see - the earlier examples (not touched by this commit) DO
> use meaningful names; while _this_ example uses the munged names. I
> don't know if any additional wording would help.

Different section.  I'll change the wording there

    meaningless names.  For readability, the examples in this document use
    meaningful type names instead.

to say "this section" instead of "this document".

> Would it be worth documenting -u as a usage argument for
> qapi-introspect.py?  [For that matter, should we document what -m for
> middle-mode does for qapi-commands.py? But as a separate patch]

None of the options are documented outside the source, and even the
source doesn't really explain.  It's just a development tool.  I guess
adding --help would be nice.  Beyond that, returns diminish rapidly.

> I still think that masking arrays as "[123]" is nicer, but that can be a
> followup patch (especially since I've already posted it in one of my
> numerous RFC followups, which I'll have to rebase anyways)
>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection
  2015-09-04 12:57 ` [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Marc-André Lureau
  2015-09-04 14:14   ` Eric Blake
@ 2015-09-04 15:55   ` Markus Armbruster
  1 sibling, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-04 15:55 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Michael Roth

Marc-André Lureau <marcandre.lureau@gmail.com> writes:

> Hi Markus
>
> On Thu, Sep 3, 2015 at 4:29 PM, Markus Armbruster <armbru@redhat.com> wrote:
>> Series is based on "[PATCH 00/26] qapi: Another round of fixes and
>> cleanups" (which actually has 33 patches now).  It also needs my
>> "[PATCH for-2.4 0/2] qom: Fix misuse of Error API" to pass tests.
>>
>> You can fetch it from my git://repo.or.cz/qemu/armbru.git branch
>> qapi-introspect.
>
> For the records, I have also rebased my qapi-related patches on top of
> your branch. It adds: varargs support, doc generation, use of
> qmp_dispatch() & async support.
>
> https://github.com/elmarco/qemu branch qapi
>
> cheers

By now we have quite a line of patches queuing up behind this series,
most of them in need of review.  Let's remember "perfect is the enemy of
good", wrap up my series, and get the queue moving.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects.
  2015-09-04 14:40     ` Markus Armbruster
@ 2015-09-04 23:51       ` Eric Blake
  2015-09-05 13:19         ` Eric Blake
  2015-09-07  6:58         ` Markus Armbruster
  0 siblings, 2 replies; 75+ messages in thread
From: Eric Blake @ 2015-09-04 23:51 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, mdroth

We have a few fields that exist mainly to hold information from
__init__() until check() (matching the fact that parsing is
two-pass; the first to find type names, the second to associate
types together while honoring forward references), or which should
only be used through accessor methods.  We should not use these
fields directly in other files after check() has run, so use the
python convention of naming these fields with leading underscore
to mark their internal usage, and to check that no one else was
using them.

Exception: our crazy handling of simple unions (with a C member
'kind' matching the QMP wire 'type') requires peeking through
the hidden field.  This leaky abstraction will be cleaned up in
a later patch.

Signed-off-by: Eric Blake <eblake@redhat.com>
---

Technically, I wrote this patch after 32; if you decide to rebase
it into the series, you'll have to split it among 2, 10, 11, and 30.
Up to you if you want to squash this in during your spin of v5, or
if you want me to keep it as a separate patch for inclusion after
your series.

 scripts/qapi-introspect.py |  2 +-
 scripts/qapi-types.py      |  2 +-
 scripts/qapi-visit.py      |  4 ++--
 scripts/qapi.py            | 54 +++++++++++++++++++++++-----------------------
 4 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 9886b65..c7a2dfb 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -138,7 +138,7 @@ const char %(c_name)s[] = %(c_string)s;
     def visit_object_type_flat(self, name, info, members, variants):
         obj = {'members': [self._gen_member(m) for m in members]}
         if variants:
-            obj.update(self._gen_variants(variants.tag_name,
+            obj.update(self._gen_variants(variants._tag_name,
                                           variants.variants))
         self._gen_json(name, 'object', obj)

diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 83fc421..65c5ed4 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -144,7 +144,7 @@ struct %(c_name)s {
     union { /* union tag is @%(c_name)s */
         void *data;
 ''',
-                 c_name=c_name(variants.tag_name or 'kind'))
+                 c_name=c_name(variants._tag_name or 'kind'))

     for var in variants.variants:
         # TODO ugly special case for simple union
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 468080e..196f389 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -270,7 +270,7 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
                      c_name=c_name(name))

     tag_key = variants.tag_member.name
-    if not variants.tag_name:
+    if not variants._tag_name:
         # we pointlessly use a different key for simple unions
         tag_key = 'type'
     ret += mcgen('''
@@ -284,7 +284,7 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
         switch ((*obj)->%(c_name)s) {
 ''',
                  c_type=variants.tag_member.type.c_name(),
-                 c_name=c_name(variants.tag_name or 'kind'),
+                 c_name=c_name(variants._tag_name or 'kind'),
                  name=tag_key)

     for var in variants.variants:
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 1058f9e..17e81f5 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -806,19 +806,19 @@ class QAPISchemaBuiltinType(QAPISchemaType):
         assert not c_type or isinstance(c_type, str)
         assert json_type in ('string', 'number', 'int', 'boolean', 'null',
                              'value')
-        self.json_type_name = json_type
-        self.c_type_name = c_type
-        self.c_null_val = c_null
+        self._json_type_name = json_type
+        self._c_type_name = c_type
+        self._c_null_val = c_null
     def c_name(self):
         return self.name
     def c_type(self, is_param=False):
         if is_param and self.name == 'str':
-            return 'const ' + self.c_type_name
-        return self.c_type_name
+            return 'const ' + self._c_type_name
+        return self._c_type_name
     def c_null(self):
-        return self.c_null_val
+        return self._c_null_val
     def json_type(self):
-        return self.json_type_name
+        return self._json_type_name
     def visit(self, visitor):
         visitor.visit_builtin_type(self.name, self.info, self.json_type())

@@ -843,10 +843,10 @@ class QAPISchemaArrayType(QAPISchemaType):
     def __init__(self, name, info, element_type):
         QAPISchemaType.__init__(self, name, info)
         assert isinstance(element_type, str)
-        self.element_type_name = element_type
+        self._element_type_name = element_type
         self.element_type = None
     def check(self, schema):
-        self.element_type = schema.lookup_type(self.element_type_name)
+        self.element_type = schema.lookup_type(self._element_type_name)
         assert self.element_type
     def json_type(self):
         return 'array'
@@ -861,7 +861,7 @@ class QAPISchemaObjectType(QAPISchemaType):
             assert isinstance(m, QAPISchemaObjectTypeMember)
         assert variants == None \
             or isinstance(variants, QAPISchemaObjectTypeVariants)
-        self.base_name = base
+        self._base_name = base
         self.base = None
         self.local_members = local_members
         self.variants = variants
@@ -871,8 +871,8 @@ class QAPISchemaObjectType(QAPISchemaType):
         if self.members:
             return
         self.members = False            # mark as being checked
-        if self.base_name:
-            self.base = schema.lookup_type(self.base_name)
+        if self._base_name:
+            self.base = schema.lookup_type(self._base_name)
             assert isinstance(self.base, QAPISchemaObjectType)
             assert not self.base.variants # not implemented
             self.base.check(schema)
@@ -907,12 +907,12 @@ class QAPISchemaObjectTypeMember(object):
         assert isinstance(typ, str)
         assert isinstance(optional, bool)
         self.name = name
-        self.type_name = typ
+        self._type_name = typ
         self.type = None
         self.optional = optional
     def check(self, schema, all_members, seen):
         assert self.name not in seen
-        self.type = schema.lookup_type(self.type_name)
+        self.type = schema.lookup_type(self._type_name)
         assert self.type
         all_members.append(self)
         seen[self.name] = self
@@ -923,7 +923,7 @@ class QAPISchemaObjectTypeVariants(object):
         assert tag_enum == None or isinstance(tag_enum, str)
         for v in variants:
             assert isinstance(v, QAPISchemaObjectTypeVariant)
-        self.tag_name = tag_name
+        self._tag_name = tag_name
         if tag_name:
             assert not tag_enum
             self.tag_member = None
@@ -932,8 +932,8 @@ class QAPISchemaObjectTypeVariants(object):
                                                          False)
         self.variants = variants
     def check(self, schema, members, seen):
-        if self.tag_name:
-            self.tag_member = seen[self.tag_name]
+        if self._tag_name:
+            self.tag_member = seen[self._tag_name]
         else:
             self.tag_member.check(schema, members, seen)
         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
@@ -959,7 +959,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
     def __init__(self, name, info, variants):
         QAPISchemaType.__init__(self, name, info)
         assert isinstance(variants, QAPISchemaObjectTypeVariants)
-        assert not variants.tag_name
+        assert not variants._tag_name
         self.variants = variants
     def check(self, schema):
         self.variants.check(schema, [], {})
@@ -973,19 +973,19 @@ class QAPISchemaCommand(QAPISchemaEntity):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
         assert not ret_type or isinstance(ret_type, str)
-        self.arg_type_name = arg_type
+        self._arg_type_name = arg_type
         self.arg_type = None
-        self.ret_type_name = ret_type
+        self._ret_type_name = ret_type
         self.ret_type = None
         self.gen = gen
         self.success_response = success_response
     def check(self, schema):
-        if self.arg_type_name:
-            self.arg_type = schema.lookup_type(self.arg_type_name)
+        if self._arg_type_name:
+            self.arg_type = schema.lookup_type(self._arg_type_name)
             assert isinstance(self.arg_type, QAPISchemaObjectType)
             assert not self.arg_type.variants # not implemented
-        if self.ret_type_name:
-            self.ret_type = schema.lookup_type(self.ret_type_name)
+        if self._ret_type_name:
+            self.ret_type = schema.lookup_type(self._ret_type_name)
             assert isinstance(self.ret_type, QAPISchemaType)
     def visit(self, visitor):
         visitor.visit_command(self.name, self.info,
@@ -996,11 +996,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
     def __init__(self, name, info, arg_type):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
-        self.arg_type_name = arg_type
+        self._arg_type_name = arg_type
         self.arg_type = None
     def check(self, schema):
-        if self.arg_type_name:
-            self.arg_type = schema.lookup_type(self.arg_type_name)
+        if self._arg_type_name:
+            self.arg_type = schema.lookup_type(self._arg_type_name)
             assert isinstance(self.arg_type, QAPISchemaObjectType)
             assert not self.arg_type.variants # not implemented
     def visit(self, visitor):
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects.
  2015-09-04 23:51       ` [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects Eric Blake
@ 2015-09-05 13:19         ` Eric Blake
  2015-09-05 13:25           ` Eric Blake
  2015-09-07  6:58         ` Markus Armbruster
  1 sibling, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-05 13:19 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, mdroth

[-- Attachment #1: Type: text/plain, Size: 2208 bytes --]

On 09/04/2015 05:51 PM, Eric Blake wrote:
> We have a few fields that exist mainly to hold information from
> __init__() until check() (matching the fact that parsing is
> two-pass; the first to find type names, the second to associate
> types together while honoring forward references), or which should
> only be used through accessor methods.  We should not use these
> fields directly in other files after check() has run, so use the
> python convention of naming these fields with leading underscore
> to mark their internal usage, and to check that no one else was
> using them.
> 
> Exception: our crazy handling of simple unions (with a C member
> 'kind' matching the QMP wire 'type') requires peeking through
> the hidden field.  This leaky abstraction will be cleaned up in
> a later patch.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
> 
> Technically, I wrote this patch after 32; if you decide to rebase
> it into the series, you'll have to split it among 2, 10, 11, and 30.
> Up to you if you want to squash this in during your spin of v5, or
> if you want me to keep it as a separate patch for inclusion after
> your series.

Oh, and tests/qapi-schema/test-qapi.py is annoying - even when it exits
non-zero, it does NOT cause a failure in 'make check-qapi-schema'.
You'll need to additionally consider squashing this in to 5:

diff --git i/tests/qapi-schema/test-qapi.py w/tests/qapi-schema/test-qapi.py
index 471f8e1..0fc0622 100644
--- i/tests/qapi-schema/test-qapi.py
+++ w/tests/qapi-schema/test-qapi.py
@@ -40,8 +40,9 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
     @staticmethod
     def _print_variants(variants):
         if variants:
-            if variants.tag_name:
-                print '    tag %s' % variants.tag_name
+            # TODO: fix simple unions so we don't have to look at _tag_name
+            if variants._tag_name:
+                print '    tag %s' % variants.tag_member.name
             for v in variants.variants:
                 print '    case %s: %s' % (v.name, v.type.name)


-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor
  2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor Markus Armbruster
  2015-09-03 16:18   ` Daniel P. Berrange
  2015-09-03 16:24   ` Eric Blake
@ 2015-09-05 13:23   ` Eric Blake
  2015-09-07  7:10     ` Markus Armbruster
  2 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-05 13:23 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: mdroth

[-- Attachment #1: Type: text/plain, Size: 1337 bytes --]

On 09/03/2015 08:29 AM, Markus Armbruster wrote:
> The old code prints the result of parsing (list of expression
> dictionaries), and partial results of semantic analysis (list of enum
> dictionaries, list of struct dictionaries).
> 
> The new code prints a trace of a schema visit, i.e. what the back-ends
> are going to use.  Built-in and array types are omitted, because
> they're boring.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---

> +++ b/tests/qapi-schema/test-qapi.py
> @@ -15,11 +15,35 @@ from pprint import pprint
>  import os
>  import sys
>  
> -try:
> -    exprs = QAPISchema(sys.argv[1]).get_exprs()
> -except SystemExit:
> -    raise

Don't we still need a try wrapped around...

> +schema = QAPISchema(sys.argv[1])
> +schema.visit(QAPISchemaTestVisitor())

...this, to guarantee non-zero exit status if an exception caused by
refactoring breaks the pretty-printing?  (It happened to me when I tried
to rename to _tag_name, and my only indication that something was wrong
was that qapi-tests/qapi-schema-test.out was truncated due to this
script aborting early after trying to access the old spelling tag_name).

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects.
  2015-09-05 13:19         ` Eric Blake
@ 2015-09-05 13:25           ` Eric Blake
  2015-09-05 13:45             ` Eric Blake
  0 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-05 13:25 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, mdroth

[-- Attachment #1: Type: text/plain, Size: 1796 bytes --]

On 09/05/2015 07:19 AM, Eric Blake wrote:
> On 09/04/2015 05:51 PM, Eric Blake wrote:
>> We have a few fields that exist mainly to hold information from
>> __init__() until check() (matching the fact that parsing is
>> two-pass; the first to find type names, the second to associate
>> types together while honoring forward references), or which should
>> only be used through accessor methods.  We should not use these
>> fields directly in other files after check() has run, so use the
>> python convention of naming these fields with leading underscore
>> to mark their internal usage, and to check that no one else was
>> using them.
>>
>> Exception: our crazy handling of simple unions (with a C member
>> 'kind' matching the QMP wire 'type') requires peeking through
>> the hidden field.  This leaky abstraction will be cleaned up in
>> a later patch.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> ---
>>
>> Technically, I wrote this patch after 32; if you decide to rebase
>> it into the series, you'll have to split it among 2, 10, 11, and 30.
>> Up to you if you want to squash this in during your spin of v5, or
>> if you want me to keep it as a separate patch for inclusion after
>> your series.
> 
> Oh, and tests/qapi-schema/test-qapi.py is annoying - even when it exits
> non-zero, it does NOT cause a failure in 'make check-qapi-schema'.

Rather, it does not leave an obvious stack trace or immediate non-zero
status; and 'make check-qapi-schema' only fails as a side effect if
later output happens to differ from expectations.  I would have had a
much easier time chasing the problem if I'd had an obvious python stack
trace.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects.
  2015-09-05 13:25           ` Eric Blake
@ 2015-09-05 13:45             ` Eric Blake
  2015-09-07  6:57               ` Markus Armbruster
  0 siblings, 1 reply; 75+ messages in thread
From: Eric Blake @ 2015-09-05 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, mdroth

[-- Attachment #1: Type: text/plain, Size: 789 bytes --]

On 09/05/2015 07:25 AM, Eric Blake wrote:
>>
>> Oh, and tests/qapi-schema/test-qapi.py is annoying - even when it exits
>> non-zero, it does NOT cause a failure in 'make check-qapi-schema'.
> 
> Rather, it does not leave an obvious stack trace or immediate non-zero
> status; and 'make check-qapi-schema' only fails as a side effect if
> later output happens to differ from expectations.  I would have had a
> much easier time chasing the problem if I'd had an obvious python stack
> trace.

Oh, I see what happened. It did capture a trace in .err; but since the
code checks for differences in .out before .err, I didn't even realize
there was a trace to look at.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects.
  2015-09-05 13:45             ` Eric Blake
@ 2015-09-07  6:57               ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-07  6:57 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 09/05/2015 07:25 AM, Eric Blake wrote:
>>>
>>> Oh, and tests/qapi-schema/test-qapi.py is annoying - even when it exits
>>> non-zero, it does NOT cause a failure in 'make check-qapi-schema'.
>> 
>> Rather, it does not leave an obvious stack trace or immediate non-zero
>> status; and 'make check-qapi-schema' only fails as a side effect if
>> later output happens to differ from expectations.  I would have had a
>> much easier time chasing the problem if I'd had an obvious python stack
>> trace.
>
> Oh, I see what happened. It did capture a trace in .err; but since the
> code checks for differences in .out before .err, I didn't even realize
> there was a trace to look at.

Yes, and it has annoyed me, too.  Perhaps we should do something like
this (entirely untested):

diff --git a/tests/Makefile b/tests/Makefile
index 92d682f..d622356 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -519,13 +519,12 @@ check-tests/test-qapi.py: tests/test-qapi.py
 $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
 	$(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts \
 		$(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \
-		$^ >$*.test.out 2>$*.test.err; \
+		$^ >$*.test.out 2>$*.test.raw-err; \
 		echo $$? >$*.test.exit, \
 		"  TEST  $*.out")
-	@diff -q $(SRC_PATH)/$*.out $*.test.out
 	@# Sanitize error messages (make them independent of build directory)
-	@perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff -q $(SRC_PATH)/$*.err -
-	@diff -q $(SRC_PATH)/$*.exit $*.test.exit
+	@perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.raw-err >$*.test.err
+	@diff -q $(SRC_PATH)/$*.out $*.test.out && diff -q $(SRC_PATH)/$*.err $*.test.err && diff -q $(SRC_PATH)/$*.exit $*.test.exit
 
 # Consolidated targets
 

^ permalink raw reply related	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects.
  2015-09-04 23:51       ` [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects Eric Blake
  2015-09-05 13:19         ` Eric Blake
@ 2015-09-07  6:58         ` Markus Armbruster
  1 sibling, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-07  6:58 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> We have a few fields that exist mainly to hold information from
> __init__() until check() (matching the fact that parsing is
> two-pass; the first to find type names, the second to associate
> types together while honoring forward references), or which should
> only be used through accessor methods.  We should not use these
> fields directly in other files after check() has run, so use the
> python convention of naming these fields with leading underscore
> to mark their internal usage, and to check that no one else was
> using them.
>
> Exception: our crazy handling of simple unions (with a C member
> 'kind' matching the QMP wire 'type') requires peeking through
> the hidden field.  This leaky abstraction will be cleaned up in
> a later patch.

There are two more peeks: QAPISchemaAlternateType peeks when it asserts
it got simple variants, and test-qapi.py peeks when it prints a flat
union's tag.  You missed the latter, and neglected to mention the
former.

> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
>
> Technically, I wrote this patch after 32; if you decide to rebase
> it into the series, you'll have to split it among 2, 10, 11, and 30.
> Up to you if you want to squash this in during your spin of v5, or
> if you want me to keep it as a separate patch for inclusion after
> your series.

I doubt the improvement worth the churn as a follow-up patch.  Instead,
I'll squash it in, less the rename of .tag_name, plus a few more renames
elsewhere.

Thanks!

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor
  2015-09-05 13:23   ` Eric Blake
@ 2015-09-07  7:10     ` Markus Armbruster
  0 siblings, 0 replies; 75+ messages in thread
From: Markus Armbruster @ 2015-09-07  7:10 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 09/03/2015 08:29 AM, Markus Armbruster wrote:
>> The old code prints the result of parsing (list of expression
>> dictionaries), and partial results of semantic analysis (list of enum
>> dictionaries, list of struct dictionaries).
>> 
>> The new code prints a trace of a schema visit, i.e. what the back-ends
>> are going to use.  Built-in and array types are omitted, because
>> they're boring.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> Reviewed-by: Eric Blake <eblake@redhat.com>
>> ---
>
>> +++ b/tests/qapi-schema/test-qapi.py
>> @@ -15,11 +15,35 @@ from pprint import pprint
>>  import os
>>  import sys
>>  
>> -try:
>> -    exprs = QAPISchema(sys.argv[1]).get_exprs()
>> -except SystemExit:
>> -    raise
>
> Don't we still need a try wrapped around...
>
>> +schema = QAPISchema(sys.argv[1])
>> +schema.visit(QAPISchemaTestVisitor())
>
> ...this, to guarantee non-zero exit status if an exception caused by
> refactoring breaks the pretty-printing?  (It happened to me when I tried
> to rename to _tag_name, and my only indication that something was wrong
> was that qapi-tests/qapi-schema-test.out was truncated due to this
> script aborting early after trying to access the old spelling tag_name).

The new version doesn't catch exceptions, and uncaught exceptions
terminate the program unsucsessfully.  Anything wrong with that?

The old version hides exceptions other than SystemExit.  Why?  I believe
it's mostly historical accident.  Accidents mostly cause by me.

I created the file in commit 9862657.  I wrapped parse_schema() in a
misguided attempt to keep the .err files concise:

    try:
        exprs = parse_schema(sys.stdin)
    except:
        print >>sys.stderr, "Crashed:", sys.exc_info()[0]
        exit(1)

This misreported errors as crashes, so I amended it in commit 2caba36:

    try:
        exprs = parse_schema(sys.stdin)
    except SystemExit:
        raise
    except:
        print >>sys.stderr, "Crashed:", sys.exc_info()[0]
        exit(1)

Lluís then ripped out the original misguided blanket exception catching,
but left in the amend (commit 98c1200):

    try:
        exprs = parse_schema(sys.stdin)
    except SystemExit:
        raise

^ permalink raw reply	[flat|nested] 75+ messages in thread

end of thread, other threads:[~2015-09-07  7:10 UTC | newest]

Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-03 14:29 [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Markus Armbruster
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 01/32] qapi: Rename class QAPISchema to QAPISchemaParser Markus Armbruster
2015-09-03 15:55   ` Daniel P. Berrange
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 02/32] qapi: New QAPISchema intermediate reperesentation Markus Armbruster
2015-09-03 16:11   ` Daniel P. Berrange
2015-09-04  7:12     ` Markus Armbruster
2015-09-04  8:51       ` Daniel P. Berrange
2015-09-04 11:13         ` Markus Armbruster
2015-09-04  3:05   ` Eric Blake
2015-09-04 12:34   ` Eric Blake
2015-09-04 14:40     ` Markus Armbruster
2015-09-04 23:51       ` [Qemu-devel] [PATCH RFC v4 02.5/32] qapi: Hide internal data members of schema objects Eric Blake
2015-09-05 13:19         ` Eric Blake
2015-09-05 13:25           ` Eric Blake
2015-09-05 13:45             ` Eric Blake
2015-09-07  6:57               ` Markus Armbruster
2015-09-07  6:58         ` Markus Armbruster
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 03/32] qapi: QAPISchema code generation helper methods Markus Armbruster
2015-09-03 16:13   ` Daniel P. Berrange
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 04/32] qapi: New QAPISchemaVisitor Markus Armbruster
2015-09-03 16:16   ` Daniel P. Berrange
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 05/32] tests/qapi-schema: Convert test harness to QAPISchemaVisitor Markus Armbruster
2015-09-03 16:18   ` Daniel P. Berrange
2015-09-03 16:24   ` Eric Blake
2015-09-05 13:23   ` Eric Blake
2015-09-07  7:10     ` Markus Armbruster
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 06/32] qapi: Split up some typedefs to ease review Markus Armbruster
2015-09-03 16:19   ` Daniel P. Berrange
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 07/32] qapi: Generate comments to simplify splitting for review Markus Armbruster
2015-09-03 16:21   ` Daniel P. Berrange
2015-09-03 14:29 ` [Qemu-devel] [PATCH RFC v4 08/32] Revert "qapi: Generate comments to simplify splitting for review" Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 09/32] Revert "qapi: Split up some typedefs to ease review" Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 10/32] qapi-types: Convert to QAPISchemaVisitor, fixing flat unions Markus Armbruster
2015-09-04  3:11   ` Eric Blake
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 11/32] qapi-visit: Convert to QAPISchemaVisitor, fixing bugs Markus Armbruster
2015-09-04  3:14   ` Eric Blake
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 12/32] qapi-commands: Convert to QAPISchemaVisitor Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 13/32] qapi: De-duplicate enum code generation Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 14/32] qapi-event: Eliminate global variable event_enum_value Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 15/32] qapi-event: Convert to QAPISchemaVisitor, fixing data with base Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 16/32] qapi: Generate comments to simplify splitting for review Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 17/32] Revert "qapi: Generate comments to simplify splitting for review" Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 18/32] qapi: Replace dirty is_c_ptr() by method c_null() Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 19/32] qapi: Clean up after recent conversions to QAPISchemaVisitor Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 20/32] qapi-visit: Rearrange code a bit Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 21/32] qapi-commands: Rearrange code Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 22/32] qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO() Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 23/32] qapi: De-duplicate parameter list generation Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 24/32] qapi-commands: De-duplicate output marshaling functions Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 25/32] qapi: Improve built-in type documentation Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 26/32] qapi: Introduce a first class 'any' type Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 27/32] qom: Don't use 'gen': false for qom-get, qom-set, object-add Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 28/32] qapi-schema: Fix up misleading specification of netdev_add Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 29/32] qapi: Pseudo-type '**' is now unused, drop it Markus Armbruster
2015-09-03 20:42   ` Eric Blake
2015-09-04  7:14     ` Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 30/32] qapi: New QMP command query-schema for QMP schema introspection Markus Armbruster
2015-09-03 20:50   ` Eric Blake
2015-09-04  0:04     ` Michael Roth
2015-09-04  7:19       ` Markus Armbruster
2015-09-03 23:59   ` Michael Roth
2015-09-04  8:54     ` Markus Armbruster
2015-09-04  2:03   ` Eric Blake
2015-09-04  3:12     ` Eric Blake
2015-09-04  9:55     ` Markus Armbruster
2015-09-04 13:58       ` Eric Blake
2015-09-04 15:08         ` Markus Armbruster
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 31/32] qapi-introspect: Map all integer types to 'int' Markus Armbruster
2015-09-04 14:00   ` Eric Blake
2015-09-03 14:30 ` [Qemu-devel] [PATCH RFC v4 32/32] qapi-introspect: Hide type names Markus Armbruster
2015-09-04 14:07   ` Eric Blake
2015-09-04 15:52     ` Markus Armbruster
2015-09-04 12:57 ` [Qemu-devel] [PATCH RFC v4 00/32] qapi: QMP introspection Marc-André Lureau
2015-09-04 14:14   ` Eric Blake
2015-09-04 15:55   ` Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.