[PATCH 08/16] rpcgen: add a C code generator for XDR protocol specs

Daniel P. Berrangé berrange at redhat.com
Wed Mar 8 16:39:05 UTC 2023


This implements a C code generator that emits code that is
(almost) identical to the classic 'rpcgen' program. The
key differences are:

 - Skip inlining of calls for struct fields
 - Skip K&R style function prototypes in headers
 - Use int64_t instead of quad_t for OS portability
 - Saner whitespace / indentation

The tests/demo.c and tests/demo.h files were created using
the traditional 'rpcgen' program, and then editted to cut
out the leading boilerplate, and the differences mentioned
above.

Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
---
 build-aux/syntax-check.mk              |   4 +-
 scripts/rpcgen/rpcgen/generator.py     | 467 +++++++++++++++++++++++++
 scripts/rpcgen/tests/demo.c            | 351 +++++++++++++++++++
 scripts/rpcgen/tests/demo.h            | 216 ++++++++++++
 scripts/rpcgen/tests/demo.x            | 128 +++++++
 scripts/rpcgen/tests/meson.build       |   1 +
 scripts/rpcgen/tests/test_generator.py |  55 +++
 7 files changed, 1221 insertions(+), 1 deletion(-)
 create mode 100644 scripts/rpcgen/rpcgen/generator.py
 create mode 100644 scripts/rpcgen/tests/demo.c
 create mode 100644 scripts/rpcgen/tests/demo.h
 create mode 100644 scripts/rpcgen/tests/demo.x
 create mode 100644 scripts/rpcgen/tests/test_generator.py

diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk
index 6d82a4301a..375fad188b 100644
--- a/build-aux/syntax-check.mk
+++ b/build-aux/syntax-check.mk
@@ -1429,7 +1429,7 @@ exclude_file_name_regexp--sc_prohibit_xmlURI = ^src/util/viruri\.c$$
 exclude_file_name_regexp--sc_prohibit_return_as_function = \.py$$
 
 exclude_file_name_regexp--sc_require_config_h = \
-	^(examples/|tools/virsh-edit\.c$$|tests/virmockstathelpers.c)
+	^(examples/c/.*/.*\.c|tools/virsh-edit\.c|tests/virmockstathelpers\.c|scripts/rpcgen/tests/demo\.c)$$
 
 exclude_file_name_regexp--sc_require_config_h_first = \
 	^(examples/|tools/virsh-edit\.c$$|tests/virmockstathelpers.c)
@@ -1493,6 +1493,8 @@ exclude_file_name_regexp--sc_prohibit_strcmp = \
 exclude_file_name_regexp--sc_prohibit_select = \
   ^build-aux/syntax-check\.mk|src/util/vireventglibwatch\.c|tests/meson\.build$$
 
+exclude_file_name_regexp--sc_header-ifdef = \
+  ^scripts/rpcgen/tests/demo\.[ch]$$
 
 exclude_file_name_regexp--sc_black = \
   ^tools/|src/|tests/|ci/|run\.in|scripts/[^/]*\.py
diff --git a/scripts/rpcgen/rpcgen/generator.py b/scripts/rpcgen/rpcgen/generator.py
new file mode 100644
index 0000000000..110cd12c5e
--- /dev/null
+++ b/scripts/rpcgen/rpcgen/generator.py
@@ -0,0 +1,467 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+from .visitor import XDRVisitor
+from .parser import (
+    XDRTypeString,
+    XDRTypeVoid,
+    XDRTypeOpaque,
+    XDRTypeCustom,
+    XDRDefinitionTypedef,
+    XDRDeclarationFixedArray,
+    XDRDeclarationVariableArray,
+    XDRDeclarationPointer,
+)
+
+
+class XDRTypeDeclarationGenerator(XDRVisitor):
+    def visit_definition_cescape(self, obj, indent, context):
+        return obj.code + "\n"
+
+    def visit_definition_constant(self, obj, indent, context):
+        return "#%sdefine %s %s\n" % (indent, obj.name, obj.value)
+
+    def visit_definition_enum(self, obj, indent, context):
+        code = "%senum %s %s;\n" % (
+            indent,
+            obj.name,
+            self.visit_object(obj.body, indent),
+        ) + "%stypedef enum %s %s;\n" % (indent, obj.name, obj.name)
+        return code
+
+    def visit_definition_struct(self, obj, indent, context):
+        code = "%sstruct %s %s;\n" % (
+            indent,
+            obj.name,
+            self.visit_object(obj.body, indent),
+        ) + "%stypedef struct %s %s;\n" % (indent, obj.name, obj.name)
+        return code
+
+    def visit_definition_union(self, obj, indent, context):
+        code = "%sstruct %s %s;\n" % (
+            indent,
+            obj.name,
+            self.visit_object(obj.body, indent, obj.name),
+        ) + "%stypedef struct %s %s;\n" % (indent, obj.name, obj.name)
+        return code
+
+    def visit_definition_typedef(self, obj, indent, context):
+        return "%stypedef %s;\n" % (indent, self.visit_object(obj.decl, indent))
+
+    def visit_declaration_scalar(self, obj, indent, context):
+        return "%s %s" % (self.visit_object(obj.typ, indent), obj.identifier)
+
+    def visit_declaration_pointer(self, obj, indent, context):
+        return "%s *%s" % (self.visit_object(obj.typ, indent), obj.identifier)
+
+    def visit_declaration_fixedarray(self, obj, indent, context):
+        return "%s %s[%s]" % (
+            self.visit_object(obj.typ, indent),
+            obj.identifier,
+            obj.length,
+        )
+
+    def visit_declaration_variablearray(self, obj, indent, context):
+        if type(obj.typ) == XDRTypeString:
+            return "%schar *%s" % (indent, obj.identifier)
+        else:
+            code = (
+                "%sstruct {\n" % indent
+                + "%s    u_int %s_len;\n" % (indent, obj.identifier)
+                + "%s    %s *%s_val;\n"
+                % (indent, self.visit_object(obj.typ, ""), obj.identifier)
+                + "%s} %s" % (indent, obj.identifier)
+            )
+            return code
+
+    def visit_type_custom(self, obj, indent, context):
+        return "%s%s" % (indent, obj.identifier)
+
+    def visit_type_opaque(self, obj, indent, context):
+        return "%schar" % indent
+
+    def visit_type_string(self, obj, indent, context):
+        return "%sstring" % indent
+
+    def visit_type_void(self, obj, indent, context):
+        return "%svoid" % indent
+
+    def visit_type_char(self, obj, indent, context):
+        return "%schar" % indent
+
+    def visit_type_unsignedchar(self, obj, indent, context):
+        return "%su_char" % indent
+
+    def visit_type_short(self, obj, indent, context):
+        return "%sshort" % indent
+
+    def visit_type_unsignedshort(self, obj, indent, context):
+        return "%su_short" % indent
+
+    def visit_type_int(self, obj, indent, context):
+        return "%sint" % indent
+
+    def visit_type_unsignedint(self, obj, indent, context):
+        return "%su_int" % indent
+
+    def visit_type_hyper(self, obj, indent, context):
+        return "%sint64_t" % indent
+
+    def visit_type_unsignedhyper(self, obj, indent, context):
+        return "%suint64_t" % indent
+
+    def visit_type_bool(self, obj, indent, context):
+        return "%sbool_t" % indent
+
+    def visit_type_float(self, obj, indent, context):
+        return "%sfloat" % indent
+
+    def visit_type_double(self, obj, indent, context):
+        return "%sdouble" % indent
+
+    def visit_type_enum(self, obj, indent, context):
+        return "%senum %s" % (indent, self.visit_object(obj.body.body, indent))
+
+    def visit_type_struct(self, obj, indent, context):
+        return "%sstruct %s" % (indent, self.visit_object(obj.body, indent))
+
+    def visit_type_union(self, obj, indent, context):
+        return "%sstruct %s" % (indent, self.visit_object(obj.body, indent))
+
+    def visit_enum_value(self, obj, indent, context):
+        return "%s%s = %s" % (indent, obj.name, obj.value)
+
+    def visit_enum_body(self, obj, indent, context):
+        code = "{\n"
+        for value in obj.values:
+            code = code + self.visit_object(value, indent + "    ") + ",\n"
+        code = code + "%s}" % indent
+        return code
+
+    def visit_struct_body(self, obj, indent, context):
+        code = "{\n"
+        for value in obj.fields:
+            code = code + self.visit_object(value, indent + "    ") + ";\n"
+        code = code + "%s}" % indent
+        return code
+
+    def visit_union_case(self, obj, indent, context):
+        return self.visit_object(obj.decl, indent)
+
+    def visit_union_body(self, obj, indent, context):
+        prefix = context
+        if prefix != "":
+            prefix = prefix + "_"
+
+        code = (
+            "%s{\n" % indent
+            + "%s    %s;\n" % (indent, self.visit_object(obj.discriminator))
+            + "%s    union {\n" % indent
+        )
+        for value in obj.cases:
+            if type(value.decl.typ) == XDRTypeVoid:
+                continue
+            code = code + self.visit_object(value, indent + "        ") + ";\n"
+        if obj.default is not None and type(obj.default.typ) != XDRTypeVoid:
+            code = code + self.visit_object(obj.default, indent + "        ") + ";\n"
+        code = code + "%s    } %su;\n" % (indent, prefix) + "%s}" % indent
+        return code
+
+
+class XDRMarshallDeclarationGenerator(XDRVisitor):
+    def visit_definition_enum(self, obj, indent, context):
+        return "%sextern  bool_t xdr_%s(XDR *, %s*);\n" % (indent, obj.name, obj.name)
+
+    def visit_definition_union(self, obj, indent, context):
+        return "%sextern  bool_t xdr_%s(XDR *, %s*);\n" % (indent, obj.name, obj.name)
+
+    def visit_definition_struct(self, obj, indent, context):
+        return "%sextern  bool_t xdr_%s(XDR *, %s*);\n" % (indent, obj.name, obj.name)
+
+    def visit_definition_typedef(self, obj, indent, context):
+        if isinstance(obj.decl, XDRDeclarationFixedArray):
+            return "%sextern  bool_t xdr_%s(XDR *, %s);\n" % (
+                indent,
+                obj.decl.identifier,
+                obj.decl.identifier,
+            )
+        else:
+            return "%sextern  bool_t xdr_%s(XDR *, %s*);\n" % (
+                indent,
+                obj.decl.identifier,
+                obj.decl.identifier,
+            )
+
+
+class XDRMarshallImplementationGenerator(XDRVisitor):
+    def visit_definition_enum(self, obj, indent, context):
+        code = (
+            "%sbool_t\n" % indent
+            + "%sxdr_%s(XDR *xdrs, %s *objp)\n" % (indent, obj.name, obj.name)
+            + "%s{\n" % indent
+            + "%s    if (!xdr_enum(xdrs, (enum_t *)objp))\n" % indent
+            + "%s        return FALSE;\n" % indent
+            + "%s    return TRUE;\n" % indent
+            + "%s}\n" % indent
+        )
+        return code
+
+    def generate_type_call(self, decl, field, typename, embedded=False, indent=""):
+        if type(decl.typ) == XDRTypeVoid:
+            return ""
+        if type(decl) == XDRDeclarationFixedArray:
+            if type(decl.typ) == XDRTypeOpaque:
+                code = "%s    if (!xdr_%s(xdrs, %s, %s))\n" % (
+                    indent,
+                    self.visit_object(decl.typ, context="func"),
+                    field,
+                    decl.length,
+                )
+            else:
+                code = "%s    if (!xdr_vector(xdrs, (char *)%s, %s,\n" % (
+                    indent,
+                    field,
+                    decl.length,
+                ) + "%s        sizeof(%s), (xdrproc_t)xdr_%s))\n" % (
+                    indent,
+                    self.visit_object(decl.typ),
+                    self.visit_object(decl.typ, context="func"),
+                )
+        elif type(decl) == XDRDeclarationVariableArray:
+            fieldRef = "."
+            pointerStr = ""
+            if embedded:
+                pointerStr = "&"
+            else:
+                fieldRef = "->"
+
+            if type(decl.typ) == XDRTypeString:
+                code = "%s    if (!xdr_%s(xdrs, %s%s, %s))\n" % (
+                    indent,
+                    self.visit_object(decl.typ, context="func"),
+                    pointerStr,
+                    field,
+                    decl.maxlength,
+                )
+            elif type(decl.typ) == XDRTypeOpaque:
+                code = "%s    if (!xdr_bytes(xdrs, (char **)&%s%s%s_val, " % (
+                    indent,
+                    field,
+                    fieldRef,
+                    typename,
+                ) + "(u_int *) &%s%s%s_len, %s))\n" % (
+                    field,
+                    fieldRef,
+                    typename,
+                    decl.maxlength,
+                )
+            else:
+                code = (
+                    "%s    if (!xdr_array(xdrs, (char **)&%s%s%s_val, "
+                    % (indent, field, fieldRef, typename)
+                    + "(u_int *) &%s%s%s_len, %s,\n"
+                    % (field, fieldRef, typename, decl.maxlength)
+                    + "%s        sizeof(%s), (xdrproc_t)xdr_%s))\n"
+                    % (
+                        indent,
+                        self.visit_object(decl.typ),
+                        self.visit_object(decl.typ, context="func"),
+                    )
+                )
+        elif type(decl) == XDRDeclarationPointer:
+            pointerStr = ""
+            if embedded:
+                pointerStr = "&"
+
+            code = "%s    if (!xdr_pointer(xdrs, (char **)%s%s, " % (
+                indent,
+                pointerStr,
+                field,
+            ) + "sizeof(%s), (xdrproc_t)xdr_%s))\n" % (
+                self.visit_object(decl.typ, context="func"),
+                self.visit_object(decl.typ, context="func"),
+            )
+        else:
+            pointerStr = ""
+            isFixedArray = (
+                type(decl.typ) == XDRTypeCustom
+                and type(decl.typ.definition) == XDRDefinitionTypedef
+                and type(decl.typ.definition.decl) == XDRDeclarationFixedArray
+            )
+
+            if embedded and not isFixedArray:
+                pointerStr = "&"
+
+            code = "%s    if (!xdr_%s(xdrs, %s%s))\n" % (
+                indent,
+                self.visit_object(decl.typ, context="func"),
+                pointerStr,
+                field,
+            )
+
+        code = code + "%s        return FALSE;\n" % indent
+        return code
+
+    def visit_definition_union(self, obj, indent, context):
+        code = (
+            "%sbool_t\n" % indent
+            + "%sxdr_%s(XDR *xdrs, %s *objp)\n" % (indent, obj.name, obj.name)
+            + "%s{\n" % indent
+            + self.generate_type_call(
+                obj.body.discriminator,
+                "objp->%s" % obj.body.discriminator.identifier,
+                obj.body.discriminator.identifier,
+                embedded=True,
+                indent=indent,
+            )
+            + "%s    switch (objp->%s) {\n"
+            % (indent, obj.body.discriminator.identifier)
+        )
+
+        for case in obj.body.cases:
+            code = (
+                code
+                + "%s    case %s:\n" % (indent, case.value)
+                + self.generate_type_call(
+                    case.decl,
+                    "objp->%s_u.%s" % (obj.name, case.decl.identifier),
+                    obj.name,
+                    embedded=True,
+                    indent=indent + "    ",
+                )
+                + "%s        break;\n" % indent
+            )
+
+        code = code + "%s    default:\n" % indent
+
+        if obj.body.default is not None:
+            code = (
+                code
+                + self.generate_type_call(
+                    obj.body.default,
+                    "objp->%s_u.%s" % (obj.name, obj.body.default.identifier),
+                    obj.name,
+                    embedded=True,
+                    indent=indent + "    ",
+                )
+                + "%s        break;\n" % indent
+            )
+        else:
+            code = code + "%s        return FALSE;\n" % indent
+
+        code = (
+            code
+            + "%s    }\n" % indent
+            + "%s    return TRUE;\n" % indent
+            + "%s}\n" % indent
+        )
+        return code
+
+    def visit_definition_struct(self, obj, indent, context):
+        code = (
+            "%sbool_t\n" % indent
+            + "%sxdr_%s(XDR *xdrs, %s *objp)\n" % (indent, obj.name, obj.name)
+            + "%s{\n" % indent
+        )
+        for field in obj.body.fields:
+            code = code + self.generate_type_call(
+                field,
+                "objp->%s" % field.identifier,
+                field.identifier,
+                embedded=True,
+                indent=indent,
+            )
+        code = code + "%s    return TRUE;\n" % indent + "%s}\n" % indent
+        return code
+
+    def visit_definition_typedef(self, obj, indent, context):
+        code = "%sbool_t\n" % indent
+        if isinstance(obj.decl, XDRDeclarationFixedArray):
+            code = code + "%sxdr_%s(XDR *xdrs, %s objp)\n" % (
+                indent,
+                obj.decl.identifier,
+                obj.decl.identifier,
+            )
+        else:
+            code = code + "%sxdr_%s(XDR *xdrs, %s *objp)\n" % (
+                indent,
+                obj.decl.identifier,
+                obj.decl.identifier,
+            )
+        code = (
+            code
+            + "%s{\n" % indent
+            + self.generate_type_call(
+                obj.decl, "objp", obj.decl.identifier, embedded=False, indent=indent
+            )
+            + "%s    return TRUE;\n" % indent
+            + "%s}\n" % indent
+        )
+        return code
+
+    def visit_declaration_pointer(self, obj, indent, context):
+        return "%s%s *%s" % (indent, self.visit_object(obj.typ), obj.identifier)
+
+    def visit_declaration_fixedarray(self, obj, indent, context):
+        return "%s%s %s[%s]" % (
+            indent,
+            self.visit_object(obj.typ),
+            obj.identifier,
+            obj.length,
+        )
+
+    def visit_declaration_variablearray(self, obj, indent, context):
+        return "%s%s *%s" % (indent, self.visit_object(obj.typ), obj.identifier)
+
+    def visit_type_custom(self, obj, indent, context):
+        return "%s%s" % (indent, obj.identifier)
+
+    def visit_type_opaque(self, obj, indent, context):
+        return "%sopaque" % indent
+
+    def visit_type_string(self, obj, indent, context):
+        return "%sstring" % indent
+
+    def visit_type_char(self, obj, indent, context):
+        return "%schar" % indent
+
+    def visit_type_unsignedchar(self, obj, indent, context):
+        return "%su_char" % indent
+
+    def visit_type_short(self, obj, indent, context):
+        return "%sshort" % indent
+
+    def visit_type_unsignedshort(self, obj, indent, context):
+        return "%su_short" % indent
+
+    def visit_type_int(self, obj, indent, context):
+        return "%sint" % indent
+
+    def visit_type_unsignedint(self, obj, indent, context):
+        return "%su_int" % indent
+
+    def visit_type_hyper(self, obj, indent, context):
+        return "%sint64_t" % indent
+
+    def visit_type_unsignedhyper(self, obj, indent, context):
+        if context == "func":
+            return "%su_int64_t" % indent
+        else:
+            return "%suint64_t" % indent
+
+    def visit_type_bool(self, obj, indent, context):
+        if context == "func":
+            return "%sbool" % indent
+        else:
+            return "%sbool_t" % indent
+
+    def visit_type_float(self, obj, indent, context):
+        return "%sfloat" % indent
+
+    def visit_type_double(self, obj, indent, context):
+        return "%sdouble" % indent
+
+    def visit_enum_value(self, obj, indent, context):
+        return "%s%s = %s" % (indent, obj.name, obj.value)
+
+    def visit_union_case(self, obj, indent, context):
+        return self.visit_object(obj.value, indent)
diff --git a/scripts/rpcgen/tests/demo.c b/scripts/rpcgen/tests/demo.c
new file mode 100644
index 0000000000..a261b4fe22
--- /dev/null
+++ b/scripts/rpcgen/tests/demo.c
@@ -0,0 +1,351 @@
+bool_t
+xdr_TestEnum(XDR *xdrs, TestEnum *objp)
+{
+    if (!xdr_enum(xdrs, (enum_t *)objp))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestStruct(XDR *xdrs, TestStruct *objp)
+{
+    if (!xdr_char(xdrs, &objp->c1))
+        return FALSE;
+    if (!xdr_char(xdrs, &objp->c2))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestUnion(XDR *xdrs, TestUnion *objp)
+{
+    if (!xdr_int(xdrs, &objp->type))
+        return FALSE;
+    switch (objp->type) {
+    case 20:
+        if (!xdr_int(xdrs, &objp->TestUnion_u.i1))
+            return FALSE;
+        break;
+    case 30:
+        if (!xdr_int(xdrs, &objp->TestUnion_u.i2))
+            return FALSE;
+        break;
+    default:
+        if (!xdr_int(xdrs, &objp->TestUnion_u.i3))
+            return FALSE;
+        break;
+    }
+    return TRUE;
+}
+
+bool_t
+xdr_TestUnionVoidDefault(XDR *xdrs, TestUnionVoidDefault *objp)
+{
+    if (!xdr_int(xdrs, &objp->type))
+        return FALSE;
+    switch (objp->type) {
+    case 21:
+        if (!xdr_int(xdrs, &objp->TestUnionVoidDefault_u.i1))
+            return FALSE;
+        break;
+    case 31:
+        if (!xdr_int(xdrs, &objp->TestUnionVoidDefault_u.i2))
+            return FALSE;
+        break;
+    default:
+        break;
+    }
+    return TRUE;
+}
+
+bool_t
+xdr_TestUnionNoDefault(XDR *xdrs, TestUnionNoDefault *objp)
+{
+    if (!xdr_int(xdrs, &objp->type))
+        return FALSE;
+    switch (objp->type) {
+    case 22:
+        if (!xdr_int(xdrs, &objp->TestUnionNoDefault_u.i1))
+            return FALSE;
+        break;
+    case 32:
+        if (!xdr_int(xdrs, &objp->TestUnionNoDefault_u.i2))
+            return FALSE;
+        break;
+    default:
+        return FALSE;
+    }
+    return TRUE;
+}
+
+bool_t
+xdr_TestIntScalar(XDR *xdrs, TestIntScalar *objp)
+{
+    if (!xdr_int(xdrs, objp))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestIntPointer(XDR *xdrs, TestIntPointer *objp)
+{
+    if (!xdr_pointer(xdrs, (char **)objp, sizeof(int), (xdrproc_t)xdr_int))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestIntFixedArray(XDR *xdrs, TestIntFixedArray objp)
+{
+    if (!xdr_vector(xdrs, (char *)objp, 3,
+        sizeof(int), (xdrproc_t)xdr_int))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestIntVariableArray(XDR *xdrs, TestIntVariableArray *objp)
+{
+    if (!xdr_array(xdrs, (char **)&objp->TestIntVariableArray_val, (u_int *) &objp->TestIntVariableArray_len, 5,
+        sizeof(int), (xdrproc_t)xdr_int))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestStringVariableArray(XDR *xdrs, TestStringVariableArray *objp)
+{
+    if (!xdr_string(xdrs, objp, 7))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestOpaqueFixedArray(XDR *xdrs, TestOpaqueFixedArray objp)
+{
+    if (!xdr_opaque(xdrs, objp, 9))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestOpaqueVariableArray(XDR *xdrs, TestOpaqueVariableArray *objp)
+{
+    if (!xdr_bytes(xdrs, (char **)&objp->TestOpaqueVariableArray_val, (u_int *) &objp->TestOpaqueVariableArray_len, 11))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestEnumScalar(XDR *xdrs, TestEnumScalar *objp)
+{
+    if (!xdr_TestEnum(xdrs, objp))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestEnumPointer(XDR *xdrs, TestEnumPointer *objp)
+{
+    if (!xdr_pointer(xdrs, (char **)objp, sizeof(TestEnum), (xdrproc_t)xdr_TestEnum))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestEnumFixedArray(XDR *xdrs, TestEnumFixedArray objp)
+{
+    if (!xdr_vector(xdrs, (char *)objp, 13,
+        sizeof(TestEnum), (xdrproc_t)xdr_TestEnum))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestEnumVariableArray(XDR *xdrs, TestEnumVariableArray *objp)
+{
+    if (!xdr_array(xdrs, (char **)&objp->TestEnumVariableArray_val, (u_int *) &objp->TestEnumVariableArray_len, 15,
+        sizeof(TestEnum), (xdrproc_t)xdr_TestEnum))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestStructScalar(XDR *xdrs, TestStructScalar *objp)
+{
+    if (!xdr_TestStruct(xdrs, objp))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestStructPointer(XDR *xdrs, TestStructPointer *objp)
+{
+    if (!xdr_pointer(xdrs, (char **)objp, sizeof(TestStruct), (xdrproc_t)xdr_TestStruct))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestStructFixedArray(XDR *xdrs, TestStructFixedArray objp)
+{
+    if (!xdr_vector(xdrs, (char *)objp, 17,
+        sizeof(TestStruct), (xdrproc_t)xdr_TestStruct))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestStructVariableArray(XDR *xdrs, TestStructVariableArray *objp)
+{
+    if (!xdr_array(xdrs, (char **)&objp->TestStructVariableArray_val, (u_int *) &objp->TestStructVariableArray_len, 19,
+        sizeof(TestStruct), (xdrproc_t)xdr_TestStruct))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestUnionScalar(XDR *xdrs, TestUnionScalar *objp)
+{
+    if (!xdr_TestUnion(xdrs, objp))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestUnionPointer(XDR *xdrs, TestUnionPointer *objp)
+{
+    if (!xdr_pointer(xdrs, (char **)objp, sizeof(TestUnion), (xdrproc_t)xdr_TestUnion))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestUnionFixedArray(XDR *xdrs, TestUnionFixedArray objp)
+{
+    if (!xdr_vector(xdrs, (char *)objp, 21,
+        sizeof(TestUnion), (xdrproc_t)xdr_TestUnion))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestUnionVariableArray(XDR *xdrs, TestUnionVariableArray *objp)
+{
+    if (!xdr_array(xdrs, (char **)&objp->TestUnionVariableArray_val, (u_int *) &objp->TestUnionVariableArray_len, 23,
+        sizeof(TestUnion), (xdrproc_t)xdr_TestUnion))
+        return FALSE;
+    return TRUE;
+}
+
+bool_t
+xdr_TestStructAllTypes(XDR *xdrs, TestStructAllTypes *objp)
+{
+    if (!xdr_char(xdrs, &objp->sc))
+        return FALSE;
+    if (!xdr_u_char(xdrs, &objp->suc))
+        return FALSE;
+    if (!xdr_short(xdrs, &objp->ss))
+        return FALSE;
+    if (!xdr_u_short(xdrs, &objp->sus))
+        return FALSE;
+    if (!xdr_int(xdrs, &objp->si))
+        return FALSE;
+    if (!xdr_u_int(xdrs, &objp->sui))
+        return FALSE;
+    if (!xdr_int64_t(xdrs, &objp->sh))
+        return FALSE;
+    if (!xdr_u_int64_t(xdrs, &objp->suh))
+        return FALSE;
+    if (!xdr_bool(xdrs, &objp->sb))
+        return FALSE;
+    if (!xdr_float(xdrs, &objp->sf))
+        return FALSE;
+    if (!xdr_double(xdrs, &objp->sd))
+        return FALSE;
+    if (!xdr_pointer(xdrs, (char **)&objp->ip, sizeof(int), (xdrproc_t)xdr_int))
+        return FALSE;
+    if (!xdr_vector(xdrs, (char *)objp->ifa, TestConstDec,
+        sizeof(int), (xdrproc_t)xdr_int))
+        return FALSE;
+    if (!xdr_array(xdrs, (char **)&objp->iva.iva_val, (u_int *) &objp->iva.iva_len, TestConstHex,
+        sizeof(int), (xdrproc_t)xdr_int))
+        return FALSE;
+    if (!xdr_string(xdrs, &objp->stva, TestConstOct))
+        return FALSE;
+    if (!xdr_opaque(xdrs, objp->ofa, 33))
+        return FALSE;
+    if (!xdr_bytes(xdrs, (char **)&objp->ova.ova_val, (u_int *) &objp->ova.ova_len, 35))
+        return FALSE;
+    if (!xdr_TestEnum(xdrs, &objp->e1))
+        return FALSE;
+    if (!xdr_TestEnum(xdrs, &objp->e2))
+        return FALSE;
+    if (!xdr_pointer(xdrs, (char **)&objp->ep, sizeof(TestEnum), (xdrproc_t)xdr_TestEnum))
+        return FALSE;
+    if (!xdr_vector(xdrs, (char *)objp->efa, 37,
+        sizeof(TestEnum), (xdrproc_t)xdr_TestEnum))
+        return FALSE;
+    if (!xdr_array(xdrs, (char **)&objp->eva.eva_val, (u_int *) &objp->eva.eva_len, 39,
+        sizeof(TestEnum), (xdrproc_t)xdr_TestEnum))
+        return FALSE;
+    if (!xdr_TestStruct(xdrs, &objp->s))
+        return FALSE;
+    if (!xdr_pointer(xdrs, (char **)&objp->sp, sizeof(TestStruct), (xdrproc_t)xdr_TestStruct))
+        return FALSE;
+    if (!xdr_vector(xdrs, (char *)objp->sfa, 41,
+        sizeof(TestStruct), (xdrproc_t)xdr_TestStruct))
+        return FALSE;
+    if (!xdr_array(xdrs, (char **)&objp->sva.sva_val, (u_int *) &objp->sva.sva_len, 43,
+        sizeof(TestStruct), (xdrproc_t)xdr_TestStruct))
+        return FALSE;
+    if (!xdr_TestUnion(xdrs, &objp->u))
+        return FALSE;
+    if (!xdr_pointer(xdrs, (char **)&objp->up, sizeof(TestUnion), (xdrproc_t)xdr_TestUnion))
+        return FALSE;
+    if (!xdr_vector(xdrs, (char *)objp->ufa, 45,
+        sizeof(TestUnion), (xdrproc_t)xdr_TestUnion))
+        return FALSE;
+    if (!xdr_array(xdrs, (char **)&objp->uva.uva_val, (u_int *) &objp->uva.uva_len, 47,
+        sizeof(TestUnion), (xdrproc_t)xdr_TestUnion))
+        return FALSE;
+    if (!xdr_TestIntScalar(xdrs, &objp->tis))
+        return FALSE;
+    if (!xdr_TestIntPointer(xdrs, &objp->tip))
+        return FALSE;
+    if (!xdr_TestIntFixedArray(xdrs, objp->tifa))
+        return FALSE;
+    if (!xdr_TestIntVariableArray(xdrs, &objp->tiva))
+        return FALSE;
+    if (!xdr_TestStringVariableArray(xdrs, &objp->tstva))
+        return FALSE;
+    if (!xdr_TestOpaqueFixedArray(xdrs, objp->tofa))
+        return FALSE;
+    if (!xdr_TestOpaqueVariableArray(xdrs, &objp->tova))
+        return FALSE;
+    if (!xdr_TestEnumScalar(xdrs, &objp->tes))
+        return FALSE;
+    if (!xdr_TestEnumPointer(xdrs, &objp->tep))
+        return FALSE;
+    if (!xdr_TestEnumFixedArray(xdrs, objp->tefa))
+        return FALSE;
+    if (!xdr_TestEnumVariableArray(xdrs, &objp->teva))
+        return FALSE;
+    if (!xdr_TestStructScalar(xdrs, &objp->tss))
+        return FALSE;
+    if (!xdr_TestStructPointer(xdrs, &objp->tsp))
+        return FALSE;
+    if (!xdr_TestStructFixedArray(xdrs, objp->tsfa))
+        return FALSE;
+    if (!xdr_TestStructVariableArray(xdrs, &objp->tsva))
+        return FALSE;
+    if (!xdr_TestUnionScalar(xdrs, &objp->tu))
+        return FALSE;
+    if (!xdr_TestUnionPointer(xdrs, &objp->tup))
+        return FALSE;
+    if (!xdr_TestUnionFixedArray(xdrs, objp->tufa))
+        return FALSE;
+    if (!xdr_TestUnionVariableArray(xdrs, &objp->tuva))
+        return FALSE;
+    return TRUE;
+}
diff --git a/scripts/rpcgen/tests/demo.h b/scripts/rpcgen/tests/demo.h
new file mode 100644
index 0000000000..6fac61e7e9
--- /dev/null
+++ b/scripts/rpcgen/tests/demo.h
@@ -0,0 +1,216 @@
+enum TestEnum {
+    TEST_ENUM_ONE = 1,
+    TEST_ENUM_TWO = 2,
+};
+typedef enum TestEnum TestEnum;
+
+struct TestStruct {
+    char c1;
+    char c2;
+};
+typedef struct TestStruct TestStruct;
+
+struct TestUnion {
+    int type;
+    union {
+        int i1;
+        int i2;
+        int i3;
+    } TestUnion_u;
+};
+typedef struct TestUnion TestUnion;
+
+struct TestUnionVoidDefault {
+    int type;
+    union {
+        int i1;
+        int i2;
+    } TestUnionVoidDefault_u;
+};
+typedef struct TestUnionVoidDefault TestUnionVoidDefault;
+
+struct TestUnionNoDefault {
+    int type;
+    union {
+        int i1;
+        int i2;
+    } TestUnionNoDefault_u;
+};
+typedef struct TestUnionNoDefault TestUnionNoDefault;
+
+typedef int TestIntScalar;
+
+typedef int *TestIntPointer;
+
+typedef int TestIntFixedArray[3];
+
+typedef struct {
+    u_int TestIntVariableArray_len;
+    int *TestIntVariableArray_val;
+} TestIntVariableArray;
+
+typedef char *TestStringVariableArray;
+
+typedef char TestOpaqueFixedArray[9];
+
+typedef struct {
+    u_int TestOpaqueVariableArray_len;
+    char *TestOpaqueVariableArray_val;
+} TestOpaqueVariableArray;
+
+typedef TestEnum TestEnumScalar;
+
+typedef TestEnum *TestEnumPointer;
+
+typedef TestEnum TestEnumFixedArray[13];
+
+typedef struct {
+    u_int TestEnumVariableArray_len;
+    TestEnum *TestEnumVariableArray_val;
+} TestEnumVariableArray;
+
+typedef TestStruct TestStructScalar;
+
+typedef TestStruct *TestStructPointer;
+
+typedef TestStruct TestStructFixedArray[17];
+
+typedef struct {
+    u_int TestStructVariableArray_len;
+    TestStruct *TestStructVariableArray_val;
+} TestStructVariableArray;
+
+typedef TestUnion TestUnionScalar;
+
+typedef TestUnion *TestUnionPointer;
+
+typedef TestUnion TestUnionFixedArray[21];
+
+typedef struct {
+    u_int TestUnionVariableArray_len;
+    TestUnion *TestUnionVariableArray_val;
+} TestUnionVariableArray;
+
+#define TestConstDec 25
+
+#define TestConstHex 0x27
+
+#define TestConstOct 031
+
+struct TestStructAllTypes {
+    char sc;
+    u_char suc;
+    short ss;
+    u_short sus;
+    int si;
+    u_int sui;
+    int64_t sh;
+    uint64_t suh;
+    bool_t sb;
+    float sf;
+    double sd;
+    int *ip;
+    int ifa[TestConstDec];
+    struct {
+        u_int iva_len;
+        int *iva_val;
+    } iva;
+    char *stva;
+    char ofa[33];
+    struct {
+        u_int ova_len;
+        char *ova_val;
+    } ova;
+    TestEnum e1;
+    TestEnum e2;
+    TestEnum *ep;
+    TestEnum efa[37];
+    struct {
+        u_int eva_len;
+        TestEnum *eva_val;
+    } eva;
+    TestStruct s;
+    TestStruct *sp;
+    TestStruct sfa[41];
+    struct {
+        u_int sva_len;
+        TestStruct *sva_val;
+    } sva;
+    TestUnion u;
+    TestUnion *up;
+    TestUnion ufa[45];
+    struct {
+        u_int uva_len;
+        TestUnion *uva_val;
+    } uva;
+    TestIntScalar tis;
+    TestIntPointer tip;
+    TestIntFixedArray tifa;
+    TestIntVariableArray tiva;
+    TestStringVariableArray tstva;
+    TestOpaqueFixedArray tofa;
+    TestOpaqueVariableArray tova;
+    TestEnumScalar tes;
+    TestEnumPointer tep;
+    TestEnumFixedArray tefa;
+    TestEnumVariableArray teva;
+    TestStructScalar tss;
+    TestStructPointer tsp;
+    TestStructFixedArray tsfa;
+    TestStructVariableArray tsva;
+    TestUnionScalar tu;
+    TestUnionPointer tup;
+    TestUnionFixedArray tufa;
+    TestUnionVariableArray tuva;
+};
+typedef struct TestStructAllTypes TestStructAllTypes;
+
+extern  bool_t xdr_TestEnum(XDR *, TestEnum*);
+
+extern  bool_t xdr_TestStruct(XDR *, TestStruct*);
+
+extern  bool_t xdr_TestUnion(XDR *, TestUnion*);
+
+extern  bool_t xdr_TestUnionVoidDefault(XDR *, TestUnionVoidDefault*);
+
+extern  bool_t xdr_TestUnionNoDefault(XDR *, TestUnionNoDefault*);
+
+extern  bool_t xdr_TestIntScalar(XDR *, TestIntScalar*);
+
+extern  bool_t xdr_TestIntPointer(XDR *, TestIntPointer*);
+
+extern  bool_t xdr_TestIntFixedArray(XDR *, TestIntFixedArray);
+
+extern  bool_t xdr_TestIntVariableArray(XDR *, TestIntVariableArray*);
+
+extern  bool_t xdr_TestStringVariableArray(XDR *, TestStringVariableArray*);
+
+extern  bool_t xdr_TestOpaqueFixedArray(XDR *, TestOpaqueFixedArray);
+
+extern  bool_t xdr_TestOpaqueVariableArray(XDR *, TestOpaqueVariableArray*);
+
+extern  bool_t xdr_TestEnumScalar(XDR *, TestEnumScalar*);
+
+extern  bool_t xdr_TestEnumPointer(XDR *, TestEnumPointer*);
+
+extern  bool_t xdr_TestEnumFixedArray(XDR *, TestEnumFixedArray);
+
+extern  bool_t xdr_TestEnumVariableArray(XDR *, TestEnumVariableArray*);
+
+extern  bool_t xdr_TestStructScalar(XDR *, TestStructScalar*);
+
+extern  bool_t xdr_TestStructPointer(XDR *, TestStructPointer*);
+
+extern  bool_t xdr_TestStructFixedArray(XDR *, TestStructFixedArray);
+
+extern  bool_t xdr_TestStructVariableArray(XDR *, TestStructVariableArray*);
+
+extern  bool_t xdr_TestUnionScalar(XDR *, TestUnionScalar*);
+
+extern  bool_t xdr_TestUnionPointer(XDR *, TestUnionPointer*);
+
+extern  bool_t xdr_TestUnionFixedArray(XDR *, TestUnionFixedArray);
+
+extern  bool_t xdr_TestUnionVariableArray(XDR *, TestUnionVariableArray*);
+
+extern  bool_t xdr_TestStructAllTypes(XDR *, TestStructAllTypes*);
diff --git a/scripts/rpcgen/tests/demo.x b/scripts/rpcgen/tests/demo.x
new file mode 100644
index 0000000000..ec69913f3d
--- /dev/null
+++ b/scripts/rpcgen/tests/demo.x
@@ -0,0 +1,128 @@
+enum TestEnum {
+  TEST_ENUM_ONE = 1,
+  TEST_ENUM_TWO = 2
+};
+
+struct TestStruct {
+  char c1;
+  char c2;
+};
+
+union TestUnion switch (int type) {
+ case 20:
+    int i1;
+ case 30:
+    int i2;
+ default:
+    int i3;
+};
+
+union TestUnionVoidDefault switch (int type) {
+ case 21:
+    int i1;
+ case 31:
+    int i2;
+ default:
+    void;
+};
+
+union TestUnionNoDefault switch (int type) {
+ case 22:
+    int i1;
+ case 32:
+    int i2;
+};
+
+typedef int TestIntScalar;
+typedef int *TestIntPointer;
+typedef int TestIntFixedArray[3];
+typedef int TestIntVariableArray<5>;
+
+typedef string TestStringVariableArray<7>;
+
+typedef opaque TestOpaqueFixedArray[9];
+typedef opaque TestOpaqueVariableArray<11>;
+
+typedef TestEnum TestEnumScalar;
+typedef TestEnum *TestEnumPointer;
+typedef TestEnum TestEnumFixedArray[13];
+typedef TestEnum TestEnumVariableArray<15>;
+
+typedef TestStruct TestStructScalar;
+typedef TestStruct *TestStructPointer;
+typedef TestStruct TestStructFixedArray[17];
+typedef TestStruct TestStructVariableArray<19>;
+
+typedef TestUnion TestUnionScalar;
+typedef TestUnion *TestUnionPointer;
+typedef TestUnion TestUnionFixedArray[21];
+typedef TestUnion TestUnionVariableArray<23>;
+
+const TestConstDec = 25;
+const TestConstHex = 0x27;
+const TestConstOct = 031;
+
+struct TestStructAllTypes {
+  char sc;
+  unsigned char suc;
+  short ss;
+  unsigned short sus;
+  int si;
+  unsigned int sui;
+  hyper sh;
+  unsigned hyper suh;
+  bool sb;
+  float sf;
+  double sd;
+/*  quadruple sq; */
+
+  int *ip;
+  int ifa[TestConstDec];
+  int iva<TestConstHex>;
+
+  string stva<TestConstOct>;
+
+  opaque ofa[33];
+  opaque ova<35>;
+
+  TestEnum e1;
+  TestEnum e2;
+  TestEnum *ep;
+  TestEnum efa[37];
+  TestEnum eva<39>;
+
+  TestStruct s;
+  TestStruct *sp;
+  TestStruct sfa[41];
+  TestStruct sva<43>;
+
+  TestUnion u;
+  TestUnion *up;
+  TestUnion ufa[45];
+  TestUnion uva<47>;
+
+  TestIntScalar tis;
+  TestIntPointer tip;
+  TestIntFixedArray tifa;
+  TestIntVariableArray tiva;
+
+  TestStringVariableArray tstva;
+
+  TestOpaqueFixedArray tofa;
+  TestOpaqueVariableArray tova;
+
+  TestEnumScalar tes;
+  TestEnumPointer tep;
+  TestEnumFixedArray tefa;
+  TestEnumVariableArray teva;
+
+  TestStructScalar tss;
+  TestStructPointer tsp;
+  TestStructFixedArray tsfa;
+  TestStructVariableArray tsva;
+
+  TestUnionScalar tu;
+  TestUnionPointer tup;
+  TestUnionFixedArray tufa;
+  TestUnionVariableArray tuva;
+};
diff --git a/scripts/rpcgen/tests/meson.build b/scripts/rpcgen/tests/meson.build
index 4b1ea308ce..953a3dbede 100644
--- a/scripts/rpcgen/tests/meson.build
+++ b/scripts/rpcgen/tests/meson.build
@@ -1,4 +1,5 @@
 rpcgen_tests = files([
+    'test_generator.py',
     'test_lexer.py',
     'test_parser.py',
 ])
diff --git a/scripts/rpcgen/tests/test_generator.py b/scripts/rpcgen/tests/test_generator.py
new file mode 100644
index 0000000000..bc7660a6fc
--- /dev/null
+++ b/scripts/rpcgen/tests/test_generator.py
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import os
+from pathlib import Path
+
+from rpcgen.parser import XDRParser
+from rpcgen.generator import (
+    XDRTypeDeclarationGenerator,
+    XDRMarshallDeclarationGenerator,
+    XDRMarshallImplementationGenerator,
+)
+
+
+def test_generate_header():
+    x = Path(Path(__file__).parent, "demo.x")
+    h = Path(Path(__file__).parent, "demo.h")
+    with x.open("r") as fp:
+        parser = XDRParser(fp)
+        spec = parser.parse()
+
+    got = (
+        XDRTypeDeclarationGenerator(spec).visit()
+        + "\n"
+        + XDRMarshallDeclarationGenerator(spec).visit()
+    )
+
+    with h.open("r") as fp:
+        want = fp.read()
+
+    if "VIR_TEST_REGENERATE_OUTPUT" in os.environ:
+        want = got
+        with h.open("w") as fp:
+            fp.write(want)
+
+    assert got == want
+
+
+def test_generate_source():
+    x = Path(Path(__file__).parent, "demo.x")
+    h = Path(Path(__file__).parent, "demo.c")
+    with x.open("r") as fp:
+        parser = XDRParser(fp)
+        spec = parser.parse()
+
+    got = XDRMarshallImplementationGenerator(spec).visit()
+
+    with h.open("r") as fp:
+        want = fp.read()
+
+    if "VIR_TEST_REGENERATE_OUTPUT" in os.environ:
+        want = got
+        with h.open("w") as fp:
+            fp.write(want)
+
+    assert got == want
-- 
2.39.1



More information about the libvir-list mailing list