From patchwork Mon Mar 12 18:35:55 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Eric Blake X-Patchwork-Id: 10277067 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 30EC6603B5 for ; Mon, 12 Mar 2018 18:57:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 20D4328DAA for ; Mon, 12 Mar 2018 18:57:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 153A128DAE; Mon, 12 Mar 2018 18:57:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 4EA4E28DAA for ; Mon, 12 Mar 2018 18:57:28 +0000 (UTC) Received: from localhost ([::1]:34007 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1evSdT-0005Dl-Fb for patchwork-qemu-devel@patchwork.kernel.org; Mon, 12 Mar 2018 14:57:27 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45108) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1evSJQ-0000ci-8O for qemu-devel@nongnu.org; Mon, 12 Mar 2018 14:36:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1evSJN-0005s5-4Y for qemu-devel@nongnu.org; Mon, 12 Mar 2018 14:36:44 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:33590 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1evSJM-0005rL-Uz for qemu-devel@nongnu.org; Mon, 12 Mar 2018 14:36:41 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2DF42814DF58; Mon, 12 Mar 2018 18:36:37 +0000 (UTC) Received: from red.redhat.com (ovpn-121-135.rdu2.redhat.com [10.10.121.135]) by smtp.corp.redhat.com (Postfix) with ESMTP id 957C5215CDAC; Mon, 12 Mar 2018 18:36:36 +0000 (UTC) From: Eric Blake To: qemu-devel@nongnu.org Date: Mon, 12 Mar 2018 13:35:55 -0500 Message-Id: <20180312183628.394722-5-eblake@redhat.com> In-Reply-To: <20180312183628.394722-1-eblake@redhat.com> References: <20180312183628.394722-1-eblake@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Mon, 12 Mar 2018 18:36:37 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Mon, 12 Mar 2018 18:36:37 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'eblake@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PULL 04/36] qapi: generate a literal qobject for introspection X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Markus Armbruster , "Dr. David Alan Gilbert" , Michael Roth Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Marc-André Lureau Replace the generated json string with a literal qobject. The later is easier to deal with, at run time as well as compile time: adding #if conditionals will be easier than in a json string. The output of query-qmp-schema is not changed. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20180305172951.2150-5-marcandre.lureau@redhat.com> [eblake: fix python 3 failure] Signed-off-by: Eric Blake --- docs/devel/qapi-code-gen.txt | 29 ++++++++++----- scripts/qapi/introspect.py | 76 +++++++++++++++++++++----------------- monitor.c | 2 +- tests/test-qobject-input-visitor.c | 11 ++++-- 4 files changed, 70 insertions(+), 48 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 25b7180a189..99d81230e26 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -1319,18 +1319,27 @@ Example: #ifndef EXAMPLE_QMP_INTROSPECT_H #define EXAMPLE_QMP_INTROSPECT_H - extern const char example_qmp_schema_json[]; + extern const QLitObject qmp_schema_qlit; #endif $ cat qapi-generated/example-qapi-introspect.c [Uninteresting stuff omitted...] - const char example_qmp_schema_json[] = "[" - "{\"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\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, " - "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, " - "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, " - "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]"; + const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) { + QLIT_QDICT(((QLitDictEntry[]) { + { "arg-type", QLIT_QSTR("0") }, + { "meta-type", QLIT_QSTR("event") }, + { "name", QLIT_QSTR("Event") }, + { } + })), + QLIT_QDICT(((QLitDictEntry[]) { + { "members", QLIT_QLIST(((QLitObject[]) { + { } + })) }, + { "meta-type", QLIT_QSTR("object") }, + { "name", QLIT_QSTR("0") }, + { } + })), + ... + { } + })); diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f66c397fb0e..1d46a6d6b6f 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -13,26 +13,36 @@ See the COPYING file in the top-level directory. from qapi.common import * -# Caveman's json.dumps() replacement (we're stuck at Python 2.4) -# TODO try to use json.dumps() once we get unstuck -def to_json(obj, level=0): +def to_qlit(obj, level=0, suppress_first_indent=False): + + def indent(level): + return level * 4 * ' ' + + ret = '' + if not suppress_first_indent: + ret += indent(level) if obj is None: - ret = 'null' + ret += 'QLIT_QNULL' elif isinstance(obj, str): - ret = '"' + obj.replace('"', r'\"') + '"' + ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' elif isinstance(obj, list): - elts = [to_json(elt, level + 1) + elts = [to_qlit(elt, level + 1) for elt in obj] - ret = '[' + ', '.join(elts) + ']' + elts.append(indent(level + 1) + "{}") + ret += 'QLIT_QLIST(((QLitObject[]) {\n' + ret += ',\n'.join(elts) + '\n' + ret += indent(level) + '}))' elif isinstance(obj, dict): - elts = ['"%s": %s' % (key.replace('"', r'\"'), - to_json(obj[key], level + 1)) - for key in sorted(obj.keys())] - ret = '{' + ', '.join(elts) + '}' + elts = [] + for key, value in sorted(obj.items()): + elts.append(indent(level + 1) + '{ %s, %s }' % + (to_c_string(key), to_qlit(value, level + 1, True))) + elts.append(indent(level + 1) + '{}') + ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n' + ret += ',\n'.join(elts) + '\n' + ret += indent(level) + '}))' else: assert False # not implemented - if level == 1: - ret = '\n' + ret return ret @@ -48,7 +58,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask self._schema = None - self._jsons = [] + self._qlits = [] self._used_types = [] self._name_map = {} self._genc.add(mcgen(''' @@ -63,27 +73,27 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): def visit_end(self): # visit the types that are actually used - jsons = self._jsons - self._jsons = [] + qlits = self._qlits + self._qlits = [] for typ in self._used_types: typ.visit(self) # generate C # TODO can generate awfully long lines - jsons.extend(self._jsons) - name = c_name(self._prefix, protect=False) + 'qmp_schema_json' + qlits.extend(self._qlits) + name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit' self._genh.add(mcgen(''' -extern const char %(c_name)s[]; +#include "qapi/qmp/qlit.h" + +extern const QLitObject %(c_name)s; ''', c_name=c_name(name))) - lines = to_json(jsons).split('\n') - c_string = '\n '.join([to_c_string(line) for line in lines]) self._genc.add(mcgen(''' -const char %(c_name)s[] = %(c_string)s; +const QLitObject %(c_name)s = %(c_string)s; ''', c_name=c_name(name), - c_string=c_string)) + c_string=to_qlit(qlits))) self._schema = None - self._jsons = [] + self._qlits = [] self._used_types = [] self._name_map = {} @@ -117,12 +127,12 @@ const char %(c_name)s[] = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_json(self, name, mtype, obj): + def _gen_qlit(self, name, mtype, obj): if mtype not in ('command', 'event', 'builtin', 'array'): name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - self._jsons.append(obj) + self._qlits.append(obj) def _gen_member(self, member): ret = {'name': member.name, 'type': self._use_type(member.type)} @@ -138,24 +148,24 @@ const char %(c_name)s[] = %(c_string)s; 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}) + self._gen_qlit(name, 'builtin', {'json-type': json_type}) def visit_enum_type(self, name, info, values, prefix): - self._gen_json(name, 'enum', {'values': values}) + self._gen_qlit(name, 'enum', {'values': values}) def visit_array_type(self, name, info, element_type): element = self._use_type(element_type) - self._gen_json('[' + element + ']', 'array', {'element-type': element}) + self._gen_qlit('[' + element + ']', 'array', {'element-type': element}) 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_member.name, variants.variants)) - self._gen_json(name, 'object', obj) + self._gen_qlit(name, 'object', obj) def visit_alternate_type(self, name, info, variants): - self._gen_json(name, 'alternate', + self._gen_qlit(name, 'alternate', {'members': [{'type': self._use_type(m.type)} for m in variants.variants]}) @@ -163,13 +173,13 @@ const char %(c_name)s[] = %(c_string)s; gen, success_response, boxed): 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', + self._gen_qlit(name, 'command', {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type)}) def visit_event(self, name, info, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type - self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) + self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}) def gen_introspect(schema, output_dir, prefix, opt_unmask): diff --git a/monitor.c b/monitor.c index a4417f26cde..db166fab52f 100644 --- a/monitor.c +++ b/monitor.c @@ -956,7 +956,7 @@ EventInfoList *qmp_query_events(Error **errp) static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, Error **errp) { - *ret_data = qobject_from_json(qmp_schema_json, &error_abort); + *ret_data = qobject_from_qlit(&qmp_schema_qlit); } /* diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c index 79b1a8cb175..55db6bdef13 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -1250,24 +1250,27 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data, } static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data, - const char *schema_json) + const QLitObject *qlit) { SchemaInfoList *schema = NULL; + QObject *obj = qobject_from_qlit(qlit); Visitor *v; - v = visitor_input_test_init_raw(data, schema_json); + v = qobject_input_visitor_new(obj); visit_type_SchemaInfoList(v, NULL, &schema, &error_abort); g_assert(schema); qapi_free_SchemaInfoList(schema); + qobject_decref(obj); + visit_free(v); } static void test_visitor_in_qmp_introspect(TestInputVisitorData *data, const void *unused) { - do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json); - do_test_visitor_in_qmp_introspect(data, qmp_schema_json); + do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit); + do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit); } int main(int argc, char **argv)