From patchwork Thu Feb 27 11:09:30 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wenchao Xia X-Patchwork-Id: 324771 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 0A4092C00A0 for ; Thu, 27 Feb 2014 22:12:50 +1100 (EST) Received: from localhost ([::1]:45393 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WIytT-0005t9-MH for incoming@patchwork.ozlabs.org; Thu, 27 Feb 2014 06:12:47 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42682) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WIyrL-0002m8-8H for qemu-devel@nongnu.org; Thu, 27 Feb 2014 06:10:41 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WIyrE-00065S-Gj for qemu-devel@nongnu.org; Thu, 27 Feb 2014 06:10:35 -0500 Received: from mail-pb0-x229.google.com ([2607:f8b0:400e:c01::229]:62778) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WIyrE-000659-50 for qemu-devel@nongnu.org; Thu, 27 Feb 2014 06:10:28 -0500 Received: by mail-pb0-f41.google.com with SMTP id jt11so2389136pbb.14 for ; Thu, 27 Feb 2014 03:10:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=/jJpBjYYJmi4nN/ecBZyyF7teOSkcEfZRDm3l63zxxY=; b=XIUe1rgR139/WfHhO3Y1dFPFy/4ZySBKfHMhImNmJIL6cY+HptnhpSiqp8czlSUTzg XaXom2UDy2uDkvn9sBJZb1GR7i+kdL7EhTPmq83LfUB+ADTvIv3QubeX2GpAbyiQvutN 4GdIC1byt4iTKMpk5Wr8QYOJhWLFY4Cuuy1gdL3Kk4DYhu4EPMw02k4GGDKtNXQ9Hpor KytgFIcYPq/TmVhrDMBnE5EucrwD9x6xOhKajHZHu4KbL5t3i11PA3Bd3qReD7AsHyQJ SV9E3AsHyEH7S8I+SGcLptIdu6+V99Tefc5hXa6997oc2RgX8SvT/jCjdmMkOMEygZlE NsrQ== X-Received: by 10.68.254.103 with SMTP id ah7mr12465111pbd.159.1393499427319; Thu, 27 Feb 2014 03:10:27 -0800 (PST) Received: from localhost.localdomain.localdomain ([113.247.8.2]) by mx.google.com with ESMTPSA id my6sm7286267pbc.36.2014.02.27.03.10.18 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 27 Feb 2014 03:10:26 -0800 (PST) From: Wenchao Xia To: qemu-devel@nongnu.org Date: Thu, 27 Feb 2014 03:09:30 -0800 Message-Id: <1393499376-4374-5-git-send-email-wenchaoqemu@gmail.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1393499376-4374-1-git-send-email-wenchaoqemu@gmail.com> References: <1393499376-4374-1-git-send-email-wenchaoqemu@gmail.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:400e:c01::229 Cc: kwolf@redhat.com, Wenchao Xia , mdroth@linux.vnet.ibm.com, armbru@redhat.com, lcapitulino@redhat.com, Wenchao Xia Subject: [Qemu-devel] [PATCH V8 04/10] qapi script: check correctness of union X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Wenchao Xia Signed-off-by: Wenchao Xia Signed-off-by: Wenchao Xia --- scripts/qapi.py | 106 +++++++++++++++++++- tests/Makefile | 4 +- .../qapi-schema/flat-union-invalid-branch-key.err | 1 + .../qapi-schema/flat-union-invalid-branch-key.exit | 1 + .../qapi-schema/flat-union-invalid-branch-key.json | 17 +++ .../flat-union-invalid-discriminator.err | 1 + .../flat-union-invalid-discriminator.exit | 1 + .../flat-union-invalid-discriminator.json | 17 +++ tests/qapi-schema/flat-union-no-base.err | 1 + tests/qapi-schema/flat-union-no-base.exit | 1 + tests/qapi-schema/flat-union-no-base.json | 16 +++ tests/qapi-schema/union-invalid-base.err | 1 + tests/qapi-schema/union-invalid-base.exit | 1 + tests/qapi-schema/union-invalid-base.json | 10 ++ 14 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 tests/qapi-schema/flat-union-invalid-branch-key.err create mode 100644 tests/qapi-schema/flat-union-invalid-branch-key.exit create mode 100644 tests/qapi-schema/flat-union-invalid-branch-key.json create mode 100644 tests/qapi-schema/flat-union-invalid-branch-key.out create mode 100644 tests/qapi-schema/flat-union-invalid-discriminator.err create mode 100644 tests/qapi-schema/flat-union-invalid-discriminator.exit create mode 100644 tests/qapi-schema/flat-union-invalid-discriminator.json create mode 100644 tests/qapi-schema/flat-union-invalid-discriminator.out create mode 100644 tests/qapi-schema/flat-union-no-base.err create mode 100644 tests/qapi-schema/flat-union-no-base.exit create mode 100644 tests/qapi-schema/flat-union-no-base.json create mode 100644 tests/qapi-schema/flat-union-no-base.out create mode 100644 tests/qapi-schema/union-invalid-base.err create mode 100644 tests/qapi-schema/union-invalid-base.exit create mode 100644 tests/qapi-schema/union-invalid-base.json create mode 100644 tests/qapi-schema/union-invalid-base.out diff --git a/tests/qapi-schema/union-invalid-base.out b/tests/qapi-schema/union-invalid-base.out new file mode 100644 index 0000000..e69de29 diff --git a/scripts/qapi.py b/scripts/qapi.py index 1954292..cea346f 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -50,6 +50,15 @@ class QAPISchemaError(Exception): def __str__(self): return "%s:%s:%s: %s" % (self.fp.name, self.line, self.col, self.msg) +class QAPIExprError(Exception): + def __init__(self, expr_info, msg): + self.fp = expr_info['fp'] + self.line = expr_info['line'] + self.msg = msg + + def __str__(self): + return "%s:%s: %s" % (self.fp.name, self.line, self.msg) + class QAPISchema: def __init__(self, fp): @@ -64,7 +73,10 @@ class QAPISchema: self.accept() while self.tok != None: - self.exprs.append(self.get_expr(False)) + expr_info = {'fp': fp, 'line': self.line} + expr_elem = {'expr': self.get_expr(False), + 'info': expr_info} + self.exprs.append(expr_elem) def accept(self): while True: @@ -162,6 +174,89 @@ class QAPISchema: raise QAPISchemaError(self, 'Expected "{", "[" or string') return expr +def find_base_fields(base): + base_struct_define = find_struct(base) + if not base_struct_define: + return None + return base_struct_define['data'] + +# Return the discriminator enum define if discrminator is specified as an +# enum type, otherwise return None. +def discriminator_find_enum_define(expr): + base = expr.get('base') + discriminator = expr.get('discriminator') + + if not (discriminator and base): + return None + + base_fields = find_base_fields(base) + if not base_fields: + return None + + discriminator_type = base_fields.get(discriminator) + if not discriminator_type: + return None + + return find_enum(discriminator_type) + +def check_union(expr, expr_info): + name = expr['union'] + base = expr.get('base') + discriminator = expr.get('discriminator') + members = expr['data'] + + # If the object has a member 'base', its value must name a complex type. + if base: + base_fields = find_base_fields(base) + if not base_fields: + raise QAPIExprError(expr_info, + "Base '%s' is not a valid type" + % base) + + # If the union object has no member 'discriminator', it's an + # ordinary union. + if not discriminator: + enum_define = None + + # Else if the value of member 'discriminator' is {}, it's an + # anonymous union. + elif discriminator == {}: + enum_define = None + + # Else, it's a flat union. + else: + # The object must have a member 'base'. + if not base: + raise QAPIExprError(expr_info, + "Flat union '%s' must have a base field" + % name) + # The value of member 'discriminator' must name a member of the + # base type. + if not base_fields.get(discriminator): + raise QAPIExprError(expr_info, + "Discriminator '%s' is not a member of base " + "type '%s'" + % (discriminator, base)) + enum_define = discriminator_find_enum_define(expr) + + # Check every branch + for (key, value) in members.items(): + # If this named member's value names an enum type, then all members + # of 'data' must also be members of the enum type. + if enum_define and not key in enum_define['enum_values']: + raise QAPIExprError(expr_info, + "Discriminator value '%s' is not found in " + "enum '%s'" % + (key, enum_define["enum_name"])) + # Todo: put more check such as whether each value is valid, but it may + # need more functions to handle array case, so leave it now. + +def check_exprs(schema): + for expr_elem in schema.exprs: + expr = expr_elem['expr'] + if expr.has_key('union'): + check_union(expr, expr_elem['info']) + def parse_schema(fp): try: schema = QAPISchema(fp) @@ -171,7 +266,8 @@ def parse_schema(fp): exprs = [] - for expr in schema.exprs: + for expr_elem in schema.exprs: + expr = expr_elem['expr'] if expr.has_key('enum'): add_enum(expr['enum'], expr['data']) elif expr.has_key('union'): @@ -181,6 +277,12 @@ def parse_schema(fp): add_struct(expr) exprs.append(expr) + try: + check_exprs(schema) + except QAPIExprError, e: + print >>sys.stderr, e + exit(1) + return exprs def parse_args(typeinfo): diff --git a/tests/Makefile b/tests/Makefile index dfe06eb..6ac9889 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -143,7 +143,9 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ qapi-schema-test.json quoted-structural-chars.json \ trailing-comma-list.json trailing-comma-object.json \ unclosed-list.json unclosed-object.json unclosed-string.json \ - duplicate-key.json) + duplicate-key.json union-invalid-base.json flat-union-no-base.json \ + flat-union-invalid-discriminator.json \ + flat-union-invalid-branch-key.json) GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.err b/tests/qapi-schema/flat-union-invalid-branch-key.err new file mode 100644 index 0000000..1125caf --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-branch-key.err @@ -0,0 +1 @@ +:13: Discriminator value 'value_wrong' is not found in enum 'TestEnum' diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.exit b/tests/qapi-schema/flat-union-invalid-branch-key.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-branch-key.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.json b/tests/qapi-schema/flat-union-invalid-branch-key.json new file mode 100644 index 0000000..a624282 --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-branch-key.json @@ -0,0 +1,17 @@ +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } + +{ 'type': 'TestBase', + 'data': { 'enum1': 'TestEnum' } } + +{ 'type': 'TestTypeA', + 'data': { 'string': 'str' } } + +{ 'type': 'TestTypeB', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestUnion', + 'base': 'TestBase', + 'discriminator': 'enum1', + 'data': { 'value_wrong': 'TestTypeA', + 'value2': 'TestTypeB' } } diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.out b/tests/qapi-schema/flat-union-invalid-branch-key.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.err b/tests/qapi-schema/flat-union-invalid-discriminator.err new file mode 100644 index 0000000..cad9dbf --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-discriminator.err @@ -0,0 +1 @@ +:13: Discriminator 'enum_wrong' is not a member of base type 'TestBase' diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.exit b/tests/qapi-schema/flat-union-invalid-discriminator.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-discriminator.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.json b/tests/qapi-schema/flat-union-invalid-discriminator.json new file mode 100644 index 0000000..887157e --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-discriminator.json @@ -0,0 +1,17 @@ +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } + +{ 'type': 'TestBase', + 'data': { 'enum1': 'TestEnum' } } + +{ 'type': 'TestTypeA', + 'data': { 'string': 'str' } } + +{ 'type': 'TestTypeB', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestUnion', + 'base': 'TestBase', + 'discriminator': 'enum_wrong', + 'data': { 'value1': 'TestTypeA', + 'value2': 'TestTypeB' } } diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.out b/tests/qapi-schema/flat-union-invalid-discriminator.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err new file mode 100644 index 0000000..e695315 --- /dev/null +++ b/tests/qapi-schema/flat-union-no-base.err @@ -0,0 +1 @@ +:13: Flat union 'TestUnion' must have a base field diff --git a/tests/qapi-schema/flat-union-no-base.exit b/tests/qapi-schema/flat-union-no-base.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/flat-union-no-base.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-no-base.json b/tests/qapi-schema/flat-union-no-base.json new file mode 100644 index 0000000..e0900d4 --- /dev/null +++ b/tests/qapi-schema/flat-union-no-base.json @@ -0,0 +1,16 @@ +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } + +{ 'type': 'TestBase', + 'data': { 'enum1': 'TestEnum' } } + +{ 'type': 'TestTypeA', + 'data': { 'string': 'str' } } + +{ 'type': 'TestTypeB', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestUnion', + 'discriminator': 'enum1', + 'data': { 'value1': 'TestTypeA', + 'value2': 'TestTypeB' } } diff --git a/tests/qapi-schema/flat-union-no-base.out b/tests/qapi-schema/flat-union-no-base.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err new file mode 100644 index 0000000..dd8e3d1 --- /dev/null +++ b/tests/qapi-schema/union-invalid-base.err @@ -0,0 +1 @@ +:7: Base 'TestBaseWrong' is not a valid type diff --git a/tests/qapi-schema/union-invalid-base.exit b/tests/qapi-schema/union-invalid-base.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/qapi-schema/union-invalid-base.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/union-invalid-base.json b/tests/qapi-schema/union-invalid-base.json new file mode 100644 index 0000000..1fa4930 --- /dev/null +++ b/tests/qapi-schema/union-invalid-base.json @@ -0,0 +1,10 @@ +{ 'type': 'TestTypeA', + 'data': { 'string': 'str' } } + +{ 'type': 'TestTypeB', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestUnion', + 'base': 'TestBaseWrong', + 'data': { 'value1': 'TestTypeA', + 'value2': 'TestTypeB' } }