<div dir="ltr"><div class="gmail_extra"><br><div class="gmail_quote">On Thu, May 23, 2013 at 3:20 PM,  <span dir="ltr"><<a href="mailto:alexl@redhat.com" target="_blank">alexl@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
From: Alexander Larsson <<a href="mailto:alexl@redhat.com">alexl@redhat.com</a>><br>
<br>
If an interface has any messages and its version is larger than 1<br>
then we emit a method counts array which lists the number of methods<br>
for each version of the interface. This can be used in addition<br>
to the normal method_count to reject requests that the<br>
server doesn't support. This allows the wayland server library<br>
to be upgraded and still safely run oler compositors that don't<br>
implement the new requests.<br>
<br>
Since there is no other space in the wm_interface we add the<br>
method count array at the end of the events array. We then<br>
add some warnings to the event sending code so that we never<br>
accidentally trigger these events.<br></blockquote><div><br></div><div>Would it possibly be better to use the upper bits of method_count (or method_count < 0) to signal that a method_counts array is appended to methods?  This keeps methods with methods and also allows us to optionally do the same thing with events client-side to protect clients from broken newer-version compositors.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Then we add code in the server to reject messages that are not<br>
supported by the server version of the object they are sent to.<br>
---<br>
 src/scanner.c        | 54 ++++++++++++++++++++++++++++++++++++++++++++++------<br>
 src/wayland-server.c | 34 +++++++++++++++++++++++++++++++--<br>
 2 files changed, 80 insertions(+), 8 deletions(-)<br>
<br>
diff --git a/src/scanner.c b/src/scanner.c<br>
index 9c14ad3..38e7909 100644<br>
--- a/src/scanner.c<br>
+++ b/src/scanner.c<br>
@@ -1019,12 +1019,13 @@ emit_types(struct protocol *protocol, struct wl_list *message_list)<br>
<br>
 static void<br>
 emit_messages(struct wl_list *message_list,<br>
-             struct interface *interface, const char *suffix)<br>
+             struct interface *interface, const char *suffix,<br>
+             int emit_method_counts)<br>
 {<br>
        struct message *m;<br>
        struct arg *a;<br>
<br>
-       if (wl_list_empty(message_list))<br>
+       if (wl_list_empty(message_list) && !emit_method_counts)<br>
                return;<br>
<br>
        printf("static const struct wl_message "<br>
@@ -1070,13 +1071,52 @@ emit_messages(struct wl_list *message_list,<br>
                printf("\", types + %d },\n", m->type_index);<br>
        }<br>
<br>
+       if (emit_method_counts) {<br>
+               printf("\t{ NULL, \"\", (void *)%s_method_counts },\n", interface->name);<br>
+       }<br>
+<br>
+       printf("};\n\n");<br>
+}<br>
+<br>
+static int<br>
+emit_method_counts(struct wl_list *message_list,<br>
+                  struct interface *interface)<br>
+{<br>
+       struct message *m;<br>
+       int version = 1;<br>
+       int count;<br>
+<br>
+       if (wl_list_empty(message_list) || interface->version == 1)<br>
+               return 0;<br>
+<br>
+       printf("static const uint32_t "<br>
+              "%s_method_counts[] = { ",<br>
+              interface->name);<br>
+<br>
+       count = 0;<br>
+       wl_list_for_each(m, message_list, link) {<br>
+               while (m->since != version) {<br>
+                       printf("%d,", count);<br>
+                       version++;<br>
+               }<br>
+               count++;<br>
+       }<br>
+<br>
+       while (version <= interface->version) {<br>
+               printf("%d, ", count);<br>
+               version++;<br>
+       }<br>
+<br>
        printf("};\n\n");<br>
+<br>
+       return 1;<br>
 }<br>
<br>
 static void<br>
 emit_code(struct protocol *protocol)<br>
 {<br>
        struct interface *i;<br>
+       int has_method_counts;<br>
<br>
        if (protocol->copyright)<br>
                format_copyright(protocol->copyright);<br>
@@ -1101,8 +1141,10 @@ emit_code(struct protocol *protocol)<br>
<br>
        wl_list_for_each(i, &protocol->interface_list, link) {<br>
<br>
-               emit_messages(&i->request_list, i, "requests");<br>
-               emit_messages(&i->event_list, i, "events");<br>
+<br>
+               emit_messages(&i->request_list, i, "requests", 0);<br>
+               has_method_counts = emit_method_counts(&i->request_list, i);<br>
+               emit_messages(&i->event_list, i, "events", has_method_counts);<br>
<br>
                printf("WL_EXPORT const struct wl_interface "<br>
                       "%s_interface = {\n"<br>
@@ -1115,9 +1157,9 @@ emit_code(struct protocol *protocol)<br>
                else<br>
                        printf("\t0, NULL,\n");<br>
<br>
-               if (!wl_list_empty(&i->event_list))<br>
+               if (!wl_list_empty(&i->event_list) || has_method_counts)<br>
                        printf("\t%d, %s_events,\n",<br>
-                              wl_list_length(&i->event_list), i->name);<br>
+                              wl_list_length(&i->event_list) + has_method_counts, i->name);<br>
                else<br>
                        printf("\t0, NULL,\n");<br>
<br>
diff --git a/src/wayland-server.c b/src/wayland-server.c<br>
index c808f6a..e94366b 100644<br>
--- a/src/wayland-server.c<br>
+++ b/src/wayland-server.c<br>
@@ -124,6 +124,12 @@ wl_resource_post_event(struct wl_resource *resource, uint32_t opcode, ...)<br>
        struct wl_object *object = &resource->object;<br>
        va_list ap;<br>
<br>
+       if (opcode >= (uint32_t)object->interface->event_count ||<br>
+           object->interface->events[opcode].name == NULL) {<br>
+               wl_log ("Trying to post unsupported event\n");<br>
+               return;<br>
+       }<br>
+<br>
        va_start(ap, opcode);<br>
        closure = wl_closure_vmarshal(object, opcode, ap,<br>
                                      &object->interface->events[opcode]);<br>
@@ -150,6 +156,12 @@ wl_resource_queue_event(struct wl_resource *resource, uint32_t opcode, ...)<br>
        struct wl_object *object = &resource->object;<br>
        va_list ap;<br>
<br>
+       if (opcode >= (uint32_t)object->interface->event_count ||<br>
+           object->interface->events[opcode].name == NULL) {<br>
+               wl_log ("Trying to queue unsupported event\n");<br>
+               return;<br>
+       }<br>
+<br>
        va_start(ap, opcode);<br>
        closure = wl_closure_vmarshal(object, opcode, ap,<br>
                                      &object->interface->events[opcode]);<br>
@@ -206,7 +218,8 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)<br>
        struct wl_closure *closure;<br>
        const struct wl_message *message;<br>
        uint32_t p[2];<br>
-       int opcode, size;<br>
+       uint32_t opcode;<br>
+       int size;<br>
        int len;<br>
<br>
        if (mask & (WL_EVENT_ERROR | WL_EVENT_HANGUP)) {<br>
@@ -250,7 +263,7 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)<br>
                }<br>
<br>
                object = &resource->object;<br>
-               if (opcode >= object->interface->method_count) {<br>
+               if (opcode >= (uint32_t)object->interface->method_count) {<br>
                        wl_resource_post_error(client->display_resource,<br>
                                               WL_DISPLAY_ERROR_INVALID_METHOD,<br>
                                               "invalid method %d, object %s@%u",<br>
@@ -260,6 +273,23 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)<br>
                        break;<br>
                }<br>
<br>
+               if (object->interface->event_count > 0 &&<br>
+                   object->interface->events[object->interface->event_count-1].name == NULL) {<br>
+                       uint32_t *method_counts = (uint32_t *)<br>
+                               object->interface->events[object->interface->event_count-1].types;<br>
+<br>
+                       if (opcode >= method_counts[wl_resource_get_version (resource)]) {<br>
+                               wl_resource_post_error(client->display_resource,<br>
+                                                      WL_DISPLAY_ERROR_INVALID_METHOD,<br>
+                                                      "invalid method %d, object %s@%u",<br>
+                                                      opcode,<br>
+                                                      object->interface->name,<br>
+                                                      object->id);<br>
+                               break;<br>
+                       }<br>
+               }<br>
+<br>
+<br>
                message = &object->interface->methods[opcode];<br>
                closure = wl_connection_demarshal(client->connection, size,<br>
                                                  &client->objects, message);<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.8.1.4<br>
<br>
_______________________________________________<br>
wayland-devel mailing list<br>
<a href="mailto:wayland-devel@lists.freedesktop.org">wayland-devel@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/wayland-devel" target="_blank">http://lists.freedesktop.org/mailman/listinfo/wayland-devel</a><br>
</font></span></blockquote></div><br></div><div class="gmail_extra">Thanks,<br></div><div class="gmail_extra">--Jason Ekstrand<br></div></div>