Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / dbus / dbus-object-tree.c
index 87624f5..f6ae19d 100644 (file)
@@ -1,4 +1,4 @@
-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* dbus-object-tree.c  DBusObjectTree (internals of DBusConnection)
  *
  * Copyright (C) 2003, 2005  Red Hat Inc.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+
+#include <config.h>
 #include "dbus-object-tree.h"
 #include "dbus-connection-internal.h"
 #include "dbus-internals.h"
@@ -74,7 +76,7 @@ struct DBusObjectSubtree
   void                              *user_data;           /**< Data for functions */
   DBusObjectSubtree                **subtrees;            /**< Child nodes */
   int                                n_subtrees;          /**< Number of child nodes */
-  unsigned int                       subtrees_sorted : 1; /**< Whether children are sorted */
+  int                                max_subtrees;        /**< Number of allocated entries in subtrees */
   unsigned int                       invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */
   char                               name[1]; /**< Allocated as large as necessary */
 };
@@ -152,36 +154,6 @@ _dbus_object_tree_unref (DBusObjectTree *tree)
     }
 }
 
-static int
-subtree_cmp (DBusObjectSubtree *subtree_a,
-             DBusObjectSubtree *subtree_b)
-{
-  return strcmp (subtree_a->name, subtree_b->name);
-}
-
-static int
-subtree_qsort_cmp (const void *a,
-                   const void *b)
-{
-  DBusObjectSubtree **subtree_a_p = (void*) a;
-  DBusObjectSubtree **subtree_b_p = (void*) b;
-
-  return subtree_cmp (*subtree_a_p, *subtree_b_p);
-}
-
-static void
-ensure_sorted (DBusObjectSubtree *subtree)
-{
-  if (subtree->subtrees && !subtree->subtrees_sorted)
-    {
-      qsort (subtree->subtrees,
-             subtree->n_subtrees,
-             sizeof (DBusObjectSubtree*),
-             subtree_qsort_cmp);
-      subtree->subtrees_sorted = TRUE;
-    }
-}
-
 /** Set to 1 to get a bunch of debug spew about finding the
  * subtree nodes
  */
@@ -194,7 +166,7 @@ find_subtree_recurse (DBusObjectSubtree  *subtree,
                       int                *index_in_parent,
                       dbus_bool_t        *exact_match)
 {
-  int i;
+  int i, j;
   dbus_bool_t return_deepest_match;
 
   return_deepest_match = exact_match != NULL;
@@ -217,22 +189,18 @@ find_subtree_recurse (DBusObjectSubtree  *subtree,
                  subtree->name, path[0]);
 #endif
   
-  ensure_sorted (subtree);
-
-  /* FIXME we should do a binary search here instead
-   * of O(n)
-   */
-
   i = 0;
-  while (i < subtree->n_subtrees)
+  j = subtree->n_subtrees;
+  while (i < j)
     {
-      int v;
+      int k, v;
 
-      v = strcmp (path[0], subtree->subtrees[i]->name);
+      k = (i + j) / 2;
+      v = strcmp (path[0], subtree->subtrees[k]->name);
 
 #if VERBOSE_FIND
       _dbus_verbose ("  %s cmp %s = %d\n",
-                     path[0], subtree->subtrees[i]->name,
+                     path[0], subtree->subtrees[k]->name,
                      v);
 #endif
       
@@ -241,16 +209,16 @@ find_subtree_recurse (DBusObjectSubtree  *subtree,
           if (index_in_parent)
             {
 #if VERBOSE_FIND
-              _dbus_verbose ("  storing parent index %d\n", i);
+              _dbus_verbose ("  storing parent index %d\n", k);
 #endif
-              *index_in_parent = i;
+              *index_in_parent = k;
             }
 
           if (return_deepest_match)
             {
               DBusObjectSubtree *next;
 
-              next = find_subtree_recurse (subtree->subtrees[i],
+              next = find_subtree_recurse (subtree->subtrees[k],
                                            &path[1], create_if_not_found, 
                                            index_in_parent, exact_match);
               if (next == NULL &&
@@ -268,19 +236,20 @@ find_subtree_recurse (DBusObjectSubtree  *subtree,
                 return next;
             }
           else
-            return find_subtree_recurse (subtree->subtrees[i],
+            return find_subtree_recurse (subtree->subtrees[k],
                                          &path[1], create_if_not_found, 
                                          index_in_parent, exact_match);
         }
       else if (v < 0)
         {
-          goto not_found;
+          j = k;
+        }
+      else
+        {
+          i = k + 1;
         }
-
-      ++i;
     }
 
- not_found:
 #if VERBOSE_FIND
   _dbus_verbose ("  no match found, current tree %s, create_if_not_found = %d\n",
                  subtree->name, create_if_not_found);
@@ -289,8 +258,7 @@ find_subtree_recurse (DBusObjectSubtree  *subtree,
   if (create_if_not_found)
     {
       DBusObjectSubtree* child;
-      DBusObjectSubtree **new_subtrees;
-      int new_n_subtrees;
+      int child_pos, new_n_subtrees;
 
 #if VERBOSE_FIND
       _dbus_verbose ("  creating subtree %s\n",
@@ -302,25 +270,41 @@ find_subtree_recurse (DBusObjectSubtree  *subtree,
       if (child == NULL)
         return NULL;
 
-      /* FIXME we should do the "double alloc each time" standard thing */
       new_n_subtrees = subtree->n_subtrees + 1;
-      new_subtrees = dbus_realloc (subtree->subtrees,
-                                   new_n_subtrees * sizeof (DBusObjectSubtree*));
-      if (new_subtrees == NULL)
+      if (new_n_subtrees > subtree->max_subtrees)
         {
-          child->unregister_function = NULL;
-          child->message_function = NULL;
-          _dbus_object_subtree_unref (child);
-          return NULL;
+          int new_max_subtrees;
+          DBusObjectSubtree **new_subtrees;
+
+          new_max_subtrees = subtree->max_subtrees == 0 ? 1 : 2 * subtree->max_subtrees;
+          new_subtrees = dbus_realloc (subtree->subtrees,
+                                       new_max_subtrees * sizeof (DBusObjectSubtree*));
+          if (new_subtrees == NULL)
+            {
+              _dbus_object_subtree_unref (child);
+              return NULL;
+            }
+          subtree->subtrees = new_subtrees;
+          subtree->max_subtrees = new_max_subtrees;
         }
 
-      new_subtrees[subtree->n_subtrees] = child;
+      /* The binary search failed, so i == j points to the 
+         place the child should be inserted. */
+      child_pos = i;
+      _dbus_assert (child_pos < new_n_subtrees &&
+                    new_n_subtrees <= subtree->max_subtrees);
+      if (child_pos + 1 < new_n_subtrees)
+       {
+         memmove (&subtree->subtrees[child_pos+1], 
+                  &subtree->subtrees[child_pos], 
+                  (new_n_subtrees - child_pos - 1) * 
+                  sizeof subtree->subtrees[0]);
+       }
+      subtree->subtrees[child_pos] = child;
+
       if (index_in_parent)
-        *index_in_parent = subtree->n_subtrees;
-      subtree->subtrees_sorted = FALSE;
+        *index_in_parent = child_pos;
       subtree->n_subtrees = new_n_subtrees;
-      subtree->subtrees = new_subtrees;
-
       child->parent = subtree;
 
       return find_subtree_recurse (child,
@@ -373,6 +357,9 @@ find_handler (DBusObjectTree *tree,
   _dbus_verbose ("Looking for deepest handler\n");
 #endif
   _dbus_assert (exact_match != NULL);
+
+  *exact_match = FALSE; /* ensure always initialized */
+  
   return find_subtree_recurse (tree->root, path, FALSE, NULL, exact_match);
 }
 
@@ -386,6 +373,8 @@ ensure_subtree (DBusObjectTree *tree,
   return find_subtree_recurse (tree->root, path, TRUE, NULL, NULL);
 }
 
+static char *flatten_path (const char **path);
+
 /**
  * Registers a new subtree in the global object tree.
  *
@@ -394,14 +383,17 @@ ensure_subtree (DBusObjectTree *tree,
  * @param path NULL-terminated array of path elements giving path to subtree
  * @param vtable the vtable used to traverse this subtree
  * @param user_data user data to pass to methods in the vtable
- * @returns #FALSE if not enough memory
+ * @param error address where an error can be returned
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ *    #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
  */
 dbus_bool_t
 _dbus_object_tree_register (DBusObjectTree              *tree,
                             dbus_bool_t                  fallback,
                             const char                 **path,
                             const DBusObjectPathVTable  *vtable,
-                            void                        *user_data)
+                            void                        *user_data,
+                            DBusError                   *error)
 {
   DBusObjectSubtree  *subtree;
 
@@ -411,24 +403,33 @@ _dbus_object_tree_register (DBusObjectTree              *tree,
 
   subtree = ensure_subtree (tree, path);
   if (subtree == NULL)
-    return FALSE;
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
 
-#ifndef DBUS_DISABLE_CHECKS
   if (subtree->message_function != NULL)
     {
-      _dbus_warn ("A handler is already registered for the path starting with path[0] = \"%s\"\n",
-                  path[0] ? path[0] : "null");
+      if (error != NULL)
+        {
+          char *complete_path = flatten_path (path);
+
+          dbus_set_error (error, DBUS_ERROR_OBJECT_PATH_IN_USE,
+                          "A handler is already registered for %s",
+                          complete_path ? complete_path
+                                        : "(cannot represent path: out of memory!)");
+
+          dbus_free (complete_path);
+        }
+
       return FALSE;
     }
-#else
-  _dbus_assert (subtree->message_function == NULL);
-#endif
 
   subtree->message_function = vtable->message_function;
   subtree->unregister_function = vtable->unregister_function;
   subtree->user_data = user_data;
   subtree->invoke_as_fallback = fallback != FALSE;
-  
+
   return TRUE;
 }
 
@@ -451,6 +452,9 @@ _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
 
   _dbus_assert (path != NULL);
 
+  unregister_function = NULL;
+  user_data = NULL;
+
   subtree = find_subtree (tree, path, &i);
 
 #ifndef DBUS_DISABLE_CHECKS
@@ -459,7 +463,7 @@ _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
       _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
                   path[0] ? path[0] : "null",
                   path[1] ? path[1] : "null");
-      return;
+      goto unlock;    
     }
 #else
   _dbus_assert (subtree != NULL);
@@ -495,22 +499,23 @@ _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
     }
   subtree = NULL;
 
+unlock:
   connection = tree->connection;
 
   /* Unlock and call application code */
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   if (connection)
 #endif
     {
       _dbus_connection_ref_unlocked (connection);
-      _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+      _dbus_verbose ("unlock\n");
       _dbus_connection_unlock (connection);
     }
 
   if (unregister_function)
     (* unregister_function) (connection, user_data);
 
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   if (connection)
 #endif
     dbus_connection_unref (connection);
@@ -633,11 +638,11 @@ handle_default_introspect_and_unlock (DBusObjectTree          *tree,
                                     DBUS_INTERFACE_INTROSPECTABLE,
                                     "Introspect"))
     {
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
       if (tree->connection)
 #endif
         {
-          _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+          _dbus_verbose ("unlock\n");
           _dbus_connection_unlock (tree->connection);
         }
       
@@ -648,11 +653,11 @@ handle_default_introspect_and_unlock (DBusObjectTree          *tree,
   
   if (!_dbus_string_init (&xml))
     {
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
       if (tree->connection)
 #endif
         {
-          _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+          _dbus_verbose ("unlock\n");
           _dbus_connection_unlock (tree->connection);
         }
 
@@ -693,7 +698,7 @@ handle_default_introspect_and_unlock (DBusObjectTree          *tree,
   if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &v_STRING))
     goto out;
   
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   if (tree->connection)
 #endif
     {
@@ -706,13 +711,13 @@ handle_default_introspect_and_unlock (DBusObjectTree          *tree,
   result = DBUS_HANDLER_RESULT_HANDLED;
   
  out:
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   if (tree->connection)
 #endif
     {
       if (!already_unlocked)
         {
-          _dbus_verbose ("unlock %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+          _dbus_verbose ("unlock\n");
           _dbus_connection_unlock (tree->connection);
         }
     }
@@ -736,11 +741,13 @@ handle_default_introspect_and_unlock (DBusObjectTree          *tree,
  *
  * @param tree the global object tree
  * @param message the message to dispatch
+ * @param found_object return location for the object
  * @returns whether message was handled successfully
  */
 DBusHandlerResult
 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
-                                       DBusMessage             *message)
+                                       DBusMessage             *message,
+                                       dbus_bool_t             *found_object)
 {
   char **path;
   dbus_bool_t exact_match;
@@ -756,11 +763,11 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
   path = NULL;
   if (!dbus_message_get_path_decomposed (message, &path))
     {
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
       if (tree->connection)
 #endif
         {
-          _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+          _dbus_verbose ("unlock\n");
           _dbus_connection_unlock (tree->connection);
         }
       
@@ -771,11 +778,11 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
 
   if (path == NULL)
     {
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
       if (tree->connection)
 #endif
         {
-          _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+          _dbus_verbose ("unlock\n");
           _dbus_connection_unlock (tree->connection);
         }
       
@@ -786,6 +793,9 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
   /* Find the deepest path that covers the path in the message */
   subtree = find_handler (tree, (const char**) path, &exact_match);
   
+  if (found_object)
+    *found_object = !!subtree;
+
   /* Build a list of all paths that cover the path in the message */
 
   list = NULL;
@@ -837,11 +847,11 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
           _dbus_verbose ("  (invoking a handler)\n");
 #endif
           
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
           if (tree->connection)
 #endif
             {
-              _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+              _dbus_verbose ("unlock\n");
               _dbus_connection_unlock (tree->connection);
             }
 
@@ -854,7 +864,7 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
                                          message,
                                          user_data);
 
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
           if (tree->connection)
 #endif
             _dbus_connection_lock (tree->connection);
@@ -877,11 +887,11 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
     }
   else
     {
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
       if (tree->connection)
 #endif
         {
-          _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+          _dbus_verbose ("unlock\n");
           _dbus_connection_unlock (tree->connection);
         }
     }
@@ -899,6 +909,36 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
 }
 
 /**
+ * Looks up the data passed to _dbus_object_tree_register() for a
+ * handler at the given path.
+ *
+ * @param tree the global object tree
+ * @param path NULL-terminated array of path elements giving path to subtree
+ * @returns the object's user_data or #NULL if none found
+ */
+void*
+_dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
+                                          const char    **path)
+{
+  dbus_bool_t exact_match;
+  DBusObjectSubtree *subtree;
+
+  _dbus_assert (tree != NULL);
+  _dbus_assert (path != NULL);
+  
+  /* Find the deepest path that covers the path in the message */
+  subtree = find_handler (tree, (const char**) path, &exact_match);
+
+  if ((subtree == NULL) || !exact_match)
+    {
+      _dbus_verbose ("No object at specified path found\n");
+      return NULL;
+    }
+
+  return subtree->user_data;
+}
+
+/**
  * Allocates a subtree object.
  *
  * @param name name to duplicate.
@@ -915,7 +955,7 @@ allocate_subtree_object (const char *name)
 
   len = strlen (name);
 
-  subtree = dbus_malloc (front_padding + (len + 1));
+  subtree = dbus_malloc0 (MAX (front_padding + (len + 1), sizeof (DBusObjectSubtree)));
 
   if (subtree == NULL)
     return NULL;
@@ -952,28 +992,29 @@ _dbus_object_subtree_new (const char                  *name,
     }
 
   subtree->user_data = user_data;
-  subtree->refcount.value = 1;
+  _dbus_atomic_inc (&subtree->refcount);
   subtree->subtrees = NULL;
   subtree->n_subtrees = 0;
-  subtree->subtrees_sorted = TRUE;
+  subtree->max_subtrees = 0;
   subtree->invoke_as_fallback = FALSE;
 
   return subtree;
 
  oom:
-  if (subtree)
-    {
-      dbus_free (subtree);
-    }
-
   return NULL;
 }
 
 static DBusObjectSubtree *
 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
 {
-  _dbus_assert (subtree->refcount.value > 0);
+#ifdef DBUS_DISABLE_ASSERT
   _dbus_atomic_inc (&subtree->refcount);
+#else
+  dbus_int32_t old_value;
+
+  old_value = _dbus_atomic_inc (&subtree->refcount);
+  _dbus_assert (old_value > 0);
+#endif
 
   return subtree;
 }
@@ -981,9 +1022,12 @@ _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
 static void
 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
 {
-  _dbus_assert (subtree->refcount.value > 0);
+  dbus_int32_t old_value;
 
-  if (_dbus_atomic_dec (&subtree->refcount) == 1)
+  old_value = _dbus_atomic_dec (&subtree->refcount);
+  _dbus_assert (old_value > 0);
+
+  if (old_value == 1)
     {
       _dbus_assert (subtree->unregister_function == NULL);
       _dbus_assert (subtree->message_function == NULL);
@@ -1014,11 +1058,11 @@ _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
                                                        parent_path,
                                                        child_entries);
   
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   if (tree->connection)
 #endif
     {
-      _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME);
+      _dbus_verbose ("unlock\n");
       _dbus_connection_unlock (tree->connection);
     }
 
@@ -1050,6 +1094,7 @@ _dbus_decompose_path (const char*     data,
   int i, j, comp;
 
   _dbus_assert (data != NULL);
+  _dbus_assert (path != NULL);
   
 #if VERBOSE_DECOMPOSE
   _dbus_verbose ("Decomposing path \"%s\"\n",
@@ -1062,6 +1107,7 @@ _dbus_decompose_path (const char*     data,
       i = 0;
       while (i < len)
         {
+          _dbus_assert (data[i] != '\0');
           if (data[i] == '/')
             n_components += 1;
           ++i;
@@ -1125,10 +1171,6 @@ _dbus_decompose_path (const char*     data,
 
 /** @} */
 
-#ifdef DBUS_BUILD_TESTS
-#include "dbus-test.h"
-#include <stdio.h>
-
 static char*
 flatten_path (const char **path)
 {
@@ -1173,6 +1215,13 @@ flatten_path (const char **path)
 }
 
 
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include "dbus-test.h"
+#include <stdio.h>
+
 typedef enum 
 {
   STR_EQUAL,
@@ -1195,9 +1244,9 @@ path_contains (const char **container,
 
       if (container[i] == NULL)
         return STR_PREFIX; /* container ran out, child continues;
-                       * thus the container is a parent of the
-                       * child.
-                       */
+                            * thus the container is a parent of the
+                            * child.
+                            */
 
       _dbus_assert (container[i] != NULL);
       _dbus_assert (child[i] != NULL);
@@ -1206,8 +1255,8 @@ path_contains (const char **container,
 
       if (v != 0)
         return STR_DIFFERENT; /* they overlap until here and then are different,
-                          * not overlapping
-                          */
+                               * not overlapping
+                               */
 
       ++i;
     }
@@ -1296,7 +1345,7 @@ do_register (DBusObjectTree *tree,
 {
   DBusObjectPathVTable vtable = { test_unregister_function,
                                   test_message_function, NULL };
-  
+
   tree_test_data[i].message_handled = FALSE;
   tree_test_data[i].handler_unregistered = FALSE;
   tree_test_data[i].handler_fallback = fallback;
@@ -1304,9 +1353,13 @@ do_register (DBusObjectTree *tree,
 
   if (!_dbus_object_tree_register (tree, fallback, path,
                                    &vtable,
-                                   &tree_test_data[i]))
+                                   &tree_test_data[i],
+                                   NULL))
     return FALSE;
 
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path) ==
+                &tree_test_data[i]);
+  
   return TRUE;
 }
 
@@ -1343,7 +1396,7 @@ do_test_dispatch (DBusObjectTree *tree,
       ++j;
     }
 
-  result = _dbus_object_tree_dispatch_and_unlock (tree, message);
+  result = _dbus_object_tree_dispatch_and_unlock (tree, message, NULL);
   if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
     goto oom;
 
@@ -1713,6 +1766,7 @@ object_tree_test_iteration (void *data)
     goto out;
 
   _dbus_object_tree_unregister_and_unlock (tree, path0);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path0) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (find_subtree (tree, path1, NULL));
@@ -1725,6 +1779,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
   
   _dbus_object_tree_unregister_and_unlock (tree, path1);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path1) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1737,6 +1792,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
 
   _dbus_object_tree_unregister_and_unlock (tree, path2);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1749,6 +1805,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
   
   _dbus_object_tree_unregister_and_unlock (tree, path3);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path3) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1761,6 +1818,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
   
   _dbus_object_tree_unregister_and_unlock (tree, path4);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path4) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1773,6 +1831,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
   
   _dbus_object_tree_unregister_and_unlock (tree, path5);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path5) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1785,6 +1844,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
   
   _dbus_object_tree_unregister_and_unlock (tree, path6);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path6) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1797,6 +1857,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
 
   _dbus_object_tree_unregister_and_unlock (tree, path7);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path7) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1809,6 +1870,7 @@ object_tree_test_iteration (void *data)
   _dbus_assert (find_subtree (tree, path8, NULL));
 
   _dbus_object_tree_unregister_and_unlock (tree, path8);
+  _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path8) == NULL);
 
   _dbus_assert (!find_subtree (tree, path0, NULL));
   _dbus_assert (!find_subtree (tree, path1, NULL));
@@ -1899,4 +1961,6 @@ _dbus_object_tree_test (void)
   return TRUE;
 }
 
-#endif /* DBUS_BUILD_TESTS */
+#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
+
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */