On 22/07/21 16:02, Markus Armbruster wrote:
Double-checking: the other fields are not accessible via this visitor.
Correct?

Correct.


Signed-off-by: Paolo Bonzini <pbonz...@redhat.com>
---
  include/qapi/forward-visitor.h    |  27 +++
  qapi/meson.build                  |   1 +
  qapi/qapi-forward-visitor.c       | 307 ++++++++++++++++++++++++++++++
  tests/unit/meson.build            |   1 +
  tests/unit/test-forward-visitor.c | 165 ++++++++++++++++
  5 files changed, 501 insertions(+)
  create mode 100644 include/qapi/forward-visitor.h
  create mode 100644 qapi/qapi-forward-visitor.c
  create mode 100644 tests/unit/test-forward-visitor.c

Missing: update of the big comment in include/qapi/visitor.h.  Can be
done on top.

Also because I'm not sure what to add. :)

This is not a fifth type of visitor, it's a wrapper for the existing types (two 
of them, input and output; the other two don't break horribly but make no sense 
either).

+static bool forward_field_translate_name(ForwardFieldVisitor *v, const char 
**name,
+                                         Error **errp)
+{
+    if (v->depth) {
+        return true;
+    }

Succeed when we're in a sub-struct.

+    if (g_str_equal(*name, v->from)) {
+        *name = v->to;
+        return true;
+    }

Succeed when we're in the root struct and @name is the alias name.
Replace the alias name by the real one.

+    error_setg(errp, QERR_MISSING_PARAMETER, *name);
+    return false;

Fail when we're in the root struct and @name is not the alias name.

+}

Can you explain why you treat names in sub-structs differently than
names other than the alias name in the root struct?

Taking the example of QOM alias properties, if the QOM property you're
aliasing is a struct, its field names are irrelevant.  The caller may
not even know what they are, as they are not part of the namespace (e.g.
the toplevel QDict returned by keyval_parse) that is being modified.

There are no aliased compound QOM properties that I can make a proper example 
with, unfortunately.

+    /*
+     * The name of alternates is reused when accessing the content,
+     * so do not increase depth here.
+     */

I understand why you don't increase @depth here (same reason
qobject-input-visitor.c doesn't qobject_input_push() here).  I don't
understand the comment :)

See above: the alternate is not a struct; the names that are passed
between start_alternate and end_alternate are within the same namespace
as the toplevel field.

As to the comment, the idea is: if those calls used e.g. name == NULL,
the depth would need to be increased, but the name will be the same one
that was received by start_alternate.  Change to "The name passed to
start_alternate is also used when accessing the content"?

+Visitor *visitor_forward_field(Visitor *target, const char *from, const char 
*to)
+{
+    ForwardFieldVisitor *v = g_new0(ForwardFieldVisitor, 1);
+
+    v->visitor.type = target->type;

Do arbitrary types work?  Or is this limited to input and output
visitors?

They don't crash, but they don't make sense because 1) they should not
live outside qapi_clone and visit_free_* 2) they use NULL for the
outermost name.

Not forwarded: method .type_size().  Impact: visit_type_size() will call
the wrapped visitor's .type_uint64() instead of its .type_size().  The
two differ for the opts visitor, the keyval input visitor, the string
input visitor, and the string output visitor.

Fixed, of course.  Incremental diff after my sig.

Paolo

diff --git a/qapi/qapi-forward-visitor.c b/qapi/qapi-forward-visitor.c
index f04f72f66d..a4b111e22a 100644
--- a/qapi/qapi-forward-visitor.c
+++ b/qapi/qapi-forward-visitor.c
@@ -57,6 +57,7 @@ static bool forward_field_translate_name(ForwardFieldVisitor 
*v, const char **na
 static bool forward_field_check_struct(Visitor *v, Error **errp)
 {
     ForwardFieldVisitor *ffv = to_ffv(v);
+
     return visit_check_struct(ffv->target, errp);
 }
@@ -78,6 +79,7 @@ static bool forward_field_start_struct(Visitor *v, const char *name, void **obj,
 static void forward_field_end_struct(Visitor *v, void **obj)
 {
     ForwardFieldVisitor *ffv = to_ffv(v);
+
     assert(ffv->depth);
     ffv->depth--;
     visit_end_struct(ffv->target, obj);
@@ -132,8 +134,8 @@ static bool forward_field_start_alternate(Visitor *v, const 
char *name,
         return false;
     }
     /*
-     * The name of alternates is reused when accessing the content,
-     * so do not increase depth here.
+     * The name passed to start_alternate is used also in the visit_type_* 
calls
+     * that retrieve the alternate's content; so, do not increase depth here.
      */
     return visit_start_alternate(ffv->target, name, obj, size, errp);
 }
@@ -189,6 +191,17 @@ static bool forward_field_type_str(Visitor *v, const char 
*name, char **obj,
     return visit_type_str(ffv->target, name, obj, errp);
 }
+static bool forward_field_type_size(Visitor *v, const char *name, uint64_t *obj,
+                                    Error **errp)
+{
+    ForwardFieldVisitor *ffv = to_ffv(v);
+
+    if (!forward_field_translate_name(ffv, &name, errp)) {
+        return false;
+    }
+    return visit_type_size(ffv->target, name, obj, errp);
+}
+
 static bool forward_field_type_number(Visitor *v, const char *name, double 
*obj,
                                       Error **errp)
 {
@@ -275,6 +288,12 @@ Visitor *visitor_forward_field(Visitor *target, const char 
*from, const char *to
 {
     ForwardFieldVisitor *v = g_new0(ForwardFieldVisitor, 1);
+ /*
+     * Clone and dealloc visitors don't use a name for the toplevel
+     * visit, so they make no sense here.
+     */
+    assert(target->type == VISITOR_OUTPUT || target->type == VISITOR_INPUT);
+
     v->visitor.type = target->type;
     v->visitor.start_struct = forward_field_start_struct;
     v->visitor.check_struct = forward_field_check_struct;
@@ -285,18 +304,19 @@ Visitor *visitor_forward_field(Visitor *target, const 
char *from, const char *to
     v->visitor.end_list = forward_field_end_list;
     v->visitor.start_alternate = forward_field_start_alternate;
     v->visitor.end_alternate = forward_field_end_alternate;
-    v->visitor.optional = forward_field_optional;
-    v->visitor.deprecated_accept = forward_field_deprecated_accept;
-    v->visitor.deprecated = forward_field_deprecated;
-    v->visitor.free = forward_field_free;
     v->visitor.type_int64 = forward_field_type_int64;
     v->visitor.type_uint64 = forward_field_type_uint64;
+    v->visitor.type_size = forward_field_type_size;
     v->visitor.type_bool = forward_field_type_bool;
     v->visitor.type_str = forward_field_type_str;
     v->visitor.type_number = forward_field_type_number;
     v->visitor.type_any = forward_field_type_any;
     v->visitor.type_null = forward_field_type_null;
+    v->visitor.optional = forward_field_optional;
+    v->visitor.deprecated_accept = forward_field_deprecated_accept;
+    v->visitor.deprecated = forward_field_deprecated;
     v->visitor.complete = forward_field_complete;
+    v->visitor.free = forward_field_free;
v->target = target;
     v->from = g_strdup(from);
diff --git a/tests/unit/test-forward-visitor.c 
b/tests/unit/test-forward-visitor.c
index 32ba359977..0de43964d2 100644
--- a/tests/unit/test-forward-visitor.c
+++ b/tests/unit/test-forward-visitor.c
@@ -69,6 +69,33 @@ static void test_forward_any(void)
     qapi_free_UserDefOne(dst);
 }
+static void test_forward_size(void)
+{
+    /*
+     * visit_type_size does not return a pointer, so visit_with_forward
+     * cannot be used.
+     */
+    bool help = false;
+    QDict *src = keyval_parse("src=1.5M", NULL, &help, &error_abort);
+    Visitor *v, *alias_v;
+    Error *err = NULL;
+    uint64_t result = 0;
+
+    v = qobject_input_visitor_new_keyval(QOBJECT(src));
+    visit_start_struct(v, NULL, NULL, 0, &error_abort);
+
+    alias_v = visitor_forward_field(v, "dst", "src");
+    visit_type_size(alias_v, "src", &result, &err);
+    error_free_or_abort(&err);
+    visit_type_size(alias_v, "dst", &result, &err);
+    assert(result == 3 << 19);
+    assert(err == NULL);
+    visit_free(alias_v);
+
+    visit_end_struct(v, NULL);
+    visit_free(v);
+}
+
 static void test_forward_number(void)
 {
     /*
@@ -157,6 +184,7 @@ int main(int argc, char **argv)
     g_test_add_func("/visitor/forward/struct", test_forward_struct);
     g_test_add_func("/visitor/forward/alternate", test_forward_alternate);
     g_test_add_func("/visitor/forward/string", test_forward_string);
+    g_test_add_func("/visitor/forward/size", test_forward_size);
     g_test_add_func("/visitor/forward/number", test_forward_number);
     g_test_add_func("/visitor/forward/any", test_forward_any);
     g_test_add_func("/visitor/forward/list", test_forward_list);


Reply via email to