qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PULL 40/40] qapi: Detect base class loops
Date: Thu, 17 Dec 2015 09:33:45 +0100	[thread overview]
Message-ID: <1450341225-2735-41-git-send-email-armbru@redhat.com> (raw)
In-Reply-To: <1450341225-2735-1-git-send-email-armbru@redhat.com>

From: Eric Blake <eblake@redhat.com>

It should be fairly obvious that qapi base classes need to
form an acyclic graph, since QMP cannot specify the same
key more than once, while base classes are included as flat
members alongside other members added by the child.  But the
old check_member_clash() parser function was not prepared to
check for this, and entered an infinite recursion (at least
until Python gives up, complaining about nesting too deep).

Now that check_member_clash() has been recently removed,
attempts at self-inheritance trigger an assertion failure
introduced by commit ac88219a.  The obvious fix is to turn
the assertion into a conditional.

This patch includes both the tests (base-cycle-direct and
base-cycle-indirect) and the fix, since the .err file output
for the unfixed case is not useful (particularly when it was
warning about unbounded recursion, as that limit may be
platform-specific).

We don't need to worry about cycles in flat unions (neither
the base type nor the type of a variant can be a union) nor
in alternates (alternate branches cannot themselves be an
alternate).  But if we later allow a union type as a variant,
we will still be okay, as QAPISchemaObjectTypeVariants.check()
triggers the same QAPISchemaObjectType.check() that will
detect any loops.

Likewise, we need not worry about the case of diamond
inheritance where the same class is used for a flat union base
class and one of its variants; either both uses will introduce
a collision in trying to insert the same member name twice, or
the shared type is empty and changes nothing.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1449033659-25497-16-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi.py                            | 4 +++-
 tests/Makefile                             | 2 ++
 tests/qapi-schema/base-cycle-direct.err    | 1 +
 tests/qapi-schema/base-cycle-direct.exit   | 1 +
 tests/qapi-schema/base-cycle-direct.json   | 2 ++
 tests/qapi-schema/base-cycle-direct.out    | 0
 tests/qapi-schema/base-cycle-indirect.err  | 1 +
 tests/qapi-schema/base-cycle-indirect.exit | 1 +
 tests/qapi-schema/base-cycle-indirect.json | 3 +++
 tests/qapi-schema/base-cycle-indirect.out  | 0
 10 files changed, 14 insertions(+), 1 deletion(-)
 create mode 100644 tests/qapi-schema/base-cycle-direct.err
 create mode 100644 tests/qapi-schema/base-cycle-direct.exit
 create mode 100644 tests/qapi-schema/base-cycle-direct.json
 create mode 100644 tests/qapi-schema/base-cycle-direct.out
 create mode 100644 tests/qapi-schema/base-cycle-indirect.err
 create mode 100644 tests/qapi-schema/base-cycle-indirect.exit
 create mode 100644 tests/qapi-schema/base-cycle-indirect.json
 create mode 100644 tests/qapi-schema/base-cycle-indirect.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 8edfd79..7dec611 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -940,7 +940,9 @@ class QAPISchemaObjectType(QAPISchemaType):
         self.members = None
 
     def check(self, schema):
-        assert self.members is not False        # not running in cycles
+        if self.members is False:               # check for cycles
+            raise QAPIExprError(self.info,
+                                "Object %s contains itself" % self.name)
         if self.members:
             return
         self.members = False                    # mark as being checked
diff --git a/tests/Makefile b/tests/Makefile
index 69cef77..053c1ae 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -257,6 +257,8 @@ qapi-schema += bad-ident.json
 qapi-schema += bad-type-bool.json
 qapi-schema += bad-type-dict.json
 qapi-schema += bad-type-int.json
+qapi-schema += base-cycle-direct.json
+qapi-schema += base-cycle-indirect.json
 qapi-schema += command-int.json
 qapi-schema += comments.json
 qapi-schema += double-data.json
diff --git a/tests/qapi-schema/base-cycle-direct.err b/tests/qapi-schema/base-cycle-direct.err
new file mode 100644
index 0000000..9c68f65
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-direct.err
@@ -0,0 +1 @@
+tests/qapi-schema/base-cycle-direct.json:2: Object Loopy contains itself
diff --git a/tests/qapi-schema/base-cycle-direct.exit b/tests/qapi-schema/base-cycle-direct.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-direct.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/base-cycle-direct.json b/tests/qapi-schema/base-cycle-direct.json
new file mode 100644
index 0000000..4fc66d0
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-direct.json
@@ -0,0 +1,2 @@
+# we reject a loop in base classes
+{ 'struct': 'Loopy', 'base': 'Loopy', 'data': {} }
diff --git a/tests/qapi-schema/base-cycle-direct.out b/tests/qapi-schema/base-cycle-direct.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/base-cycle-indirect.err b/tests/qapi-schema/base-cycle-indirect.err
new file mode 100644
index 0000000..fc92fe4
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-indirect.err
@@ -0,0 +1 @@
+tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 contains itself
diff --git a/tests/qapi-schema/base-cycle-indirect.exit b/tests/qapi-schema/base-cycle-indirect.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-indirect.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/base-cycle-indirect.json b/tests/qapi-schema/base-cycle-indirect.json
new file mode 100644
index 0000000..2866772
--- /dev/null
+++ b/tests/qapi-schema/base-cycle-indirect.json
@@ -0,0 +1,3 @@
+# we reject a loop in base classes
+{ 'struct': 'Base1', 'base': 'Base2', 'data': {} }
+{ 'struct': 'Base2', 'base': 'Base1', 'data': {} }
diff --git a/tests/qapi-schema/base-cycle-indirect.out b/tests/qapi-schema/base-cycle-indirect.out
new file mode 100644
index 0000000..e69de29
-- 
2.4.3

  parent reply	other threads:[~2015-12-17  8:34 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-17  8:33 [Qemu-devel] [PULL 00/40] QAPI patches for 2015-12-17 Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 01/40] qapi: Track simple union tag in object.local_members Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 02/40] qapi-types: Consolidate gen_struct() and gen_union() Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 03/40] qapi-types: Simplify gen_struct_field[s] Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 04/40] qapi: Drop obsolete tag value collision assertions Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 05/40] qapi: Simplify QAPISchemaObjectTypeMember.check() Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 06/40] qapi: Clean up after previous commit Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 07/40] qapi: Fix up commit 7618b91's clash sanity checking change Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 08/40] qapi: Eliminate QAPISchemaObjectType.check() variable members Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 09/40] qapi: Factor out QAPISchemaObjectTypeMember.check_clash() Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 10/40] qapi: Simplify QAPISchemaObjectTypeVariants.check() Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 11/40] qapi: Check for QAPI collisions involving variant members Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 12/40] qapi: Factor out QAPISchemaObjectType.check_clash() Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 13/40] qapi: Hoist tag collision check to Variants.check() Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 14/40] qapi: Remove outdated tests related to QMP/branch collisions Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 15/40] qapi: Track owner of each object member Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 16/40] qapi: Detect collisions in C member names Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 17/40] qapi: Fix c_name() munging Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 18/40] qapi: Remove dead visitor code Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 19/40] blkdebug: Merge hand-rolled and qapi BlkdebugEvent enum Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 20/40] blkdebug: Avoid '.' in enum values Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 21/40] qapi: Tighten the regex on valid names Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 22/40] qapi: Don't let implicit enum MAX member collide Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 23/40] qapi: Remove obsolete tests for MAX collision Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 24/40] cpu: Convert CpuInfo into flat union Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 25/40] qapi: Add alias for ErrorClass Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 26/40] qapi: Change munging of CamelCase enum values Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 27/40] qobject: Simplify QObject Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 28/40] qobject: Rename qtype_code to QType Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 29/40] qapi: Convert QType into QAPI built-in enum type Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 30/40] qapi: Simplify visiting of alternate types Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 31/40] qapi-types: Drop unnedeed ._fwdefn Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 32/40] qapi: Inline _make_implicit_tag() Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 33/40] qapi: Fix alternates that accept 'number' but not 'int' Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 34/40] qapi: Simplify visits of optional fields Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 35/40] qapi: Shorter " Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 36/40] qapi: Prepare new QAPISchemaMember base class Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 37/40] qapi: Track enum values by QAPISchemaMember, not string Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 38/40] qapi: Enforce (or whitelist) case conventions on qapi members Markus Armbruster
2015-12-17  8:33 ` [Qemu-devel] [PULL 39/40] qapi: Move duplicate collision checks to schema check() Markus Armbruster
2015-12-17  8:33 ` Markus Armbruster [this message]
2015-12-17 12:39 ` [Qemu-devel] [PULL 00/40] QAPI patches for 2015-12-17 Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1450341225-2735-41-git-send-email-armbru@redhat.com \
    --to=armbru@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).