[vala/wip/issue/1238] codegen: Actually free data when using "remove(_all)" on GLib.Queue/(S)List




commit d7a95e2dd2848ea8bd77c54fa39478ca25a4fc81
Author: Rico Tzschichholz <ricotz ubuntu com>
Date:   Thu Oct 14 19:44:55 2021 +0200

    codegen: Actually free data when using "remove(_all)" on GLib.Queue/(S)List
    
    When using e.g. GLib.List.remove() there is no context/feedback whether an
    item was removed or needed manual free'ing.
    
    Replace such calls with custom wrappers where items required free'ing if
    they were found.
    
    Fixes https://gitlab.gnome.org/GNOME/vala/issues/1238

 codegen/valaccodemethodcallmodule.vala     |  20 +
 tests/Makefile.am                          |   1 +
 tests/basic-types/glists_remove.c-expected | 577 +++++++++++++++++++++++++++++
 tests/basic-types/glists_remove.vala       | 113 ++++++
 vapi/glib-2.0.vapi                         |  85 +++++
 5 files changed, 796 insertions(+)
---
diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala
index ceb9370ec..cbefcab9e 100644
--- a/codegen/valaccodemethodcallmodule.vala
+++ b/codegen/valaccodemethodcallmodule.vala
@@ -799,6 +799,26 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule {
 
                }
 
+               // Transform and add free function argument to GLib.[List,Queue,SList].remove[_all] calls
+               unowned TypeSymbol? collection_type = null;
+               if (ma != null && ma.inner != null && ma.inner.value_type != null) {
+                       collection_type = ma.inner.value_type.type_symbol;
+               }
+               if (collection_type == glist_type || collection_type == gslist_type || collection_type == 
gqueue_type) {
+                       var type_arg = ma.inner.value_type.get_type_arguments ()[0];
+                       if ((ma.member_name == "remove" || ma.member_name == "remove_all") && 
requires_destroy (type_arg)) {
+                               var remove_method = (Method) collection_type.scope.lookup (ma.member_name + 
"_full");
+                               if (remove_method != null) {
+                                       // only add them once per source file
+                                       if (add_generated_external_symbol (remove_method)) {
+                                               visit_method (remove_method);
+                                       }
+                                       ccall.call = new CCodeIdentifier (get_ccode_name (remove_method));
+                                       ccall.add_argument (get_destroy0_func_expression (type_arg));
+                               }
+                       }
+               }
+
                if (return_result_via_out_param) {
                        ccode.add_expression (ccall_expr);
                        ccall_expr = out_param_ref;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f35f5aeca..628523a58 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -60,6 +60,7 @@ TESTS = \
        basic-types/sizeof.vala \
        basic-types/garray.vala \
        basic-types/glists.vala \
+       basic-types/glists_remove.vala \
        basic-types/gptrarray.vala \
        basic-types/gvariants.vala \
        basic-types/gvariants-hashtable-missing-type-arguments.test \
diff --git a/tests/basic-types/glists_remove.c-expected b/tests/basic-types/glists_remove.c-expected
new file mode 100644
index 000000000..da44d3ea3
--- /dev/null
+++ b/tests/basic-types/glists_remove.c-expected
@@ -0,0 +1,577 @@
+/* basic_types_glists_remove.c generated by valac, the Vala compiler
+ * generated from basic_types_glists_remove.vala, do not modify */
+
+#include <glib-object.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(VALA_EXTERN)
+#if defined(_MSC_VER)
+#define VALA_EXTERN __declspec(dllexport) extern
+#elif __GNUC__ >= 4
+#define VALA_EXTERN __attribute__((visibility("default"))) extern
+#else
+#define VALA_EXTERN extern
+#endif
+#endif
+
+#define TYPE_FOO (foo_get_type ())
+#define FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_FOO, Foo))
+#define FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_FOO, FooClass))
+#define IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_FOO))
+#define IS_FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_FOO))
+#define FOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_FOO, FooClass))
+
+typedef struct _Foo Foo;
+typedef struct _FooClass FooClass;
+typedef struct _FooPrivate FooPrivate;
+enum  {
+       FOO_0_PROPERTY,
+       FOO_NUM_PROPERTIES
+};
+static GParamSpec* foo_properties[FOO_NUM_PROPERTIES];
+#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL)))
+#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, 
__LINE__, G_STRFUNC, msg);
+#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, 
G_STRFUNC, msg); return; }
+#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning 
(G_LOG_DOMAIN, G_STRFUNC, msg); return val; }
+#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, 
__LINE__, G_STRFUNC, msg);
+
+struct _Foo {
+       GObject parent_instance;
+       FooPrivate * priv;
+};
+
+struct _FooClass {
+       GObjectClass parent_class;
+};
+
+static gpointer foo_parent_class = NULL;
+
+VALA_EXTERN GType foo_get_type (void) G_GNUC_CONST ;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (Foo, g_object_unref)
+VALA_EXTERN Foo* foo_new (void);
+VALA_EXTERN Foo* foo_construct (GType object_type);
+static GType foo_get_type_once (void);
+VALA_EXTERN void test_glist (void);
+static void _g_object_unref0_ (gpointer var);
+static inline void _g_list_free__g_object_unref0_ (GList* self);
+VALA_EXTERN void test_gslist (void);
+static inline void _g_slist_free__g_object_unref0_ (GSList* self);
+VALA_EXTERN void test_gqueue (void);
+static inline void _g_queue_free__g_object_unref0_ (GQueue* self);
+static void _vala_main (void);
+
+Foo*
+foo_construct (GType object_type)
+{
+       Foo * self = NULL;
+       self = (Foo*) g_object_new (object_type, NULL);
+       return self;
+}
+
+Foo*
+foo_new (void)
+{
+       return foo_construct (TYPE_FOO);
+}
+
+static void
+foo_class_init (FooClass * klass,
+                gpointer klass_data)
+{
+       foo_parent_class = g_type_class_peek_parent (klass);
+}
+
+static void
+foo_instance_init (Foo * self,
+                   gpointer klass)
+{
+}
+
+static GType
+foo_get_type_once (void)
+{
+       static const GTypeInfo g_define_type_info = { sizeof (FooClass), (GBaseInitFunc) NULL, 
(GBaseFinalizeFunc) NULL, (GClassInitFunc) foo_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Foo), 0, 
(GInstanceInitFunc) foo_instance_init, NULL };
+       GType foo_type_id;
+       foo_type_id = g_type_register_static (G_TYPE_OBJECT, "Foo", &g_define_type_info, 0);
+       return foo_type_id;
+}
+
+GType
+foo_get_type (void)
+{
+       static volatile gsize foo_type_id__volatile = 0;
+       if (g_once_init_enter (&foo_type_id__volatile)) {
+               GType foo_type_id;
+               foo_type_id = foo_get_type_once ();
+               g_once_init_leave (&foo_type_id__volatile, foo_type_id);
+       }
+       return foo_type_id__volatile;
+}
+
+static gpointer
+_g_object_ref0 (gpointer self)
+{
+       return self ? g_object_ref (self) : NULL;
+}
+
+static GList*
+vala_g_list_remove_full (GList* self,
+                         gconstpointer data,
+                         GFreeFunc func)
+{
+       GList* l = NULL;
+       GList* result = NULL;
+       l = self;
+       while (TRUE) {
+               GList* _tmp0_;
+               GList* _tmp1_;
+               gconstpointer _tmp2_;
+               _tmp0_ = l;
+               if (!(_tmp0_ != NULL)) {
+                       break;
+               }
+               _tmp1_ = l;
+               _tmp2_ = ((GList*) _tmp1_)->data;
+               if (_tmp2_ != data) {
+                       GList* _tmp3_;
+                       GList* _tmp4_;
+                       _tmp3_ = l;
+                       _tmp4_ = ((GList*) _tmp3_)->next;
+                       l = _tmp4_;
+               } else {
+                       GList* _tmp5_;
+                       gconstpointer _tmp6_;
+                       GList* _tmp7_;
+                       _tmp5_ = l;
+                       _tmp6_ = ((GList*) _tmp5_)->data;
+                       func (_tmp6_);
+                       _tmp7_ = l;
+                       self = g_list_delete_link (self, (GList*) _tmp7_);
+                       break;
+               }
+       }
+       result = self;
+       return result;
+}
+
+static void
+_g_object_unref0_ (gpointer var)
+{
+       (var == NULL) ? NULL : (var = (g_object_unref (var), NULL));
+}
+
+static inline void
+_g_list_free__g_object_unref0_ (GList* self)
+{
+       g_list_free_full (self, (GDestroyNotify) _g_object_unref0_);
+}
+
+static GList*
+vala_g_list_remove_all_full (GList* self,
+                             gconstpointer data,
+                             GFreeFunc func)
+{
+       GList* l = NULL;
+       GList* result = NULL;
+       l = self;
+       while (TRUE) {
+               GList* _tmp0_;
+               GList* _tmp1_;
+               gconstpointer _tmp2_;
+               _tmp0_ = l;
+               if (!(_tmp0_ != NULL)) {
+                       break;
+               }
+               _tmp1_ = l;
+               _tmp2_ = ((GList*) _tmp1_)->data;
+               if (_tmp2_ != data) {
+                       GList* _tmp3_;
+                       GList* _tmp4_;
+                       _tmp3_ = l;
+                       _tmp4_ = ((GList*) _tmp3_)->next;
+                       l = _tmp4_;
+               } else {
+                       GList* _tmp5_;
+                       gconstpointer _tmp6_;
+                       GList* _tmp7_;
+                       _tmp5_ = l;
+                       _tmp6_ = ((GList*) _tmp5_)->data;
+                       func (_tmp6_);
+                       _tmp7_ = l;
+                       self = g_list_delete_link (self, (GList*) _tmp7_);
+                       l = self;
+               }
+       }
+       result = self;
+       return result;
+}
+
+void
+test_glist (void)
+{
+       {
+               GList* list = NULL;
+               Foo* foo = NULL;
+               Foo* _tmp0_;
+               Foo* _tmp1_;
+               list = NULL;
+               _tmp0_ = foo_new ();
+               foo = _tmp0_;
+               _tmp1_ = _g_object_ref0 (foo);
+               list = g_list_append (list, _tmp1_);
+               _vala_assert (g_list_length (list) == ((guint) 1), "list.length () == 1");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
2), "foo.ref_count == 2");
+               list = vala_g_list_remove_full (list, foo, _g_object_unref0_);
+               _vala_assert (g_list_length (list) == ((guint) 0), "list.length () == 0");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
1), "foo.ref_count == 1");
+               _g_object_unref0 (foo);
+               (list == NULL) ? NULL : (list = (_g_list_free__g_object_unref0_ (list), NULL));
+       }
+       {
+               GList* list = NULL;
+               Foo* foo = NULL;
+               Foo* _tmp2_;
+               Foo* _tmp3_;
+               Foo* _tmp4_;
+               list = NULL;
+               _tmp2_ = foo_new ();
+               foo = _tmp2_;
+               _tmp3_ = _g_object_ref0 (foo);
+               list = g_list_append (list, _tmp3_);
+               _tmp4_ = _g_object_ref0 (foo);
+               list = g_list_append (list, _tmp4_);
+               _vala_assert (g_list_length (list) == ((guint) 2), "list.length () == 2");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
3), "foo.ref_count == 3");
+               list = vala_g_list_remove_all_full (list, foo, _g_object_unref0_);
+               _vala_assert (g_list_length (list) == ((guint) 0), "list.length () == 0");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
1), "foo.ref_count == 1");
+               _g_object_unref0 (foo);
+               (list == NULL) ? NULL : (list = (_g_list_free__g_object_unref0_ (list), NULL));
+       }
+       {
+               GList* list = NULL;
+               const gchar* s = NULL;
+               list = NULL;
+               s = "foo";
+               list = g_list_append (list, s);
+               _vala_assert (g_list_length (list) == ((guint) 1), "list.length () == 1");
+               list = g_list_remove (list, s);
+               _vala_assert (g_list_length (list) == ((guint) 0), "list.length () == 0");
+               list = g_list_append (list, s);
+               list = g_list_remove_all (list, s);
+               _vala_assert (g_list_length (list) == ((guint) 0), "list.length () == 0");
+               (list == NULL) ? NULL : (list = (g_list_free (list), NULL));
+       }
+}
+
+static GSList*
+vala_g_slist_remove_full (GSList* self,
+                          gconstpointer data,
+                          GFreeFunc func)
+{
+       GSList* l = NULL;
+       GSList* result = NULL;
+       l = self;
+       while (TRUE) {
+               GSList* _tmp0_;
+               GSList* _tmp1_;
+               gconstpointer _tmp2_;
+               _tmp0_ = l;
+               if (!(_tmp0_ != NULL)) {
+                       break;
+               }
+               _tmp1_ = l;
+               _tmp2_ = ((GSList*) _tmp1_)->data;
+               if (_tmp2_ != data) {
+                       GSList* _tmp3_;
+                       GSList* _tmp4_;
+                       _tmp3_ = l;
+                       _tmp4_ = ((GSList*) _tmp3_)->next;
+                       l = _tmp4_;
+               } else {
+                       GSList* _tmp5_;
+                       gconstpointer _tmp6_;
+                       GSList* _tmp7_;
+                       _tmp5_ = l;
+                       _tmp6_ = ((GSList*) _tmp5_)->data;
+                       func (_tmp6_);
+                       _tmp7_ = l;
+                       self = g_slist_delete_link (self, (GSList*) _tmp7_);
+                       break;
+               }
+       }
+       result = self;
+       return result;
+}
+
+static inline void
+_g_slist_free__g_object_unref0_ (GSList* self)
+{
+       g_slist_free_full (self, (GDestroyNotify) _g_object_unref0_);
+}
+
+static GSList*
+vala_g_slist_remove_all_full (GSList* self,
+                              gconstpointer data,
+                              GFreeFunc func)
+{
+       GSList* l = NULL;
+       GSList* result = NULL;
+       l = self;
+       while (TRUE) {
+               GSList* _tmp0_;
+               GSList* _tmp1_;
+               gconstpointer _tmp2_;
+               _tmp0_ = l;
+               if (!(_tmp0_ != NULL)) {
+                       break;
+               }
+               _tmp1_ = l;
+               _tmp2_ = ((GSList*) _tmp1_)->data;
+               if (_tmp2_ != data) {
+                       GSList* _tmp3_;
+                       GSList* _tmp4_;
+                       _tmp3_ = l;
+                       _tmp4_ = ((GSList*) _tmp3_)->next;
+                       l = _tmp4_;
+               } else {
+                       GSList* _tmp5_;
+                       gconstpointer _tmp6_;
+                       GSList* _tmp7_;
+                       _tmp5_ = l;
+                       _tmp6_ = ((GSList*) _tmp5_)->data;
+                       func (_tmp6_);
+                       _tmp7_ = l;
+                       self = g_slist_delete_link (self, (GSList*) _tmp7_);
+                       l = self;
+               }
+       }
+       result = self;
+       return result;
+}
+
+void
+test_gslist (void)
+{
+       {
+               GSList* list = NULL;
+               Foo* foo = NULL;
+               Foo* _tmp0_;
+               Foo* _tmp1_;
+               list = NULL;
+               _tmp0_ = foo_new ();
+               foo = _tmp0_;
+               _tmp1_ = _g_object_ref0 (foo);
+               list = g_slist_append (list, _tmp1_);
+               _vala_assert (g_slist_length (list) == ((guint) 1), "list.length () == 1");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
2), "foo.ref_count == 2");
+               list = vala_g_slist_remove_full (list, foo, _g_object_unref0_);
+               _vala_assert (g_slist_length (list) == ((guint) 0), "list.length () == 0");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
1), "foo.ref_count == 1");
+               _g_object_unref0 (foo);
+               (list == NULL) ? NULL : (list = (_g_slist_free__g_object_unref0_ (list), NULL));
+       }
+       {
+               GSList* list = NULL;
+               Foo* foo = NULL;
+               Foo* _tmp2_;
+               Foo* _tmp3_;
+               Foo* _tmp4_;
+               list = NULL;
+               _tmp2_ = foo_new ();
+               foo = _tmp2_;
+               _tmp3_ = _g_object_ref0 (foo);
+               list = g_slist_append (list, _tmp3_);
+               _tmp4_ = _g_object_ref0 (foo);
+               list = g_slist_append (list, _tmp4_);
+               _vala_assert (g_slist_length (list) == ((guint) 2), "list.length () == 2");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
3), "foo.ref_count == 3");
+               list = vala_g_slist_remove_all_full (list, foo, _g_object_unref0_);
+               _vala_assert (g_slist_length (list) == ((guint) 0), "list.length () == 0");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
1), "foo.ref_count == 1");
+               _g_object_unref0 (foo);
+               (list == NULL) ? NULL : (list = (_g_slist_free__g_object_unref0_ (list), NULL));
+       }
+       {
+               GSList* list = NULL;
+               const gchar* s = NULL;
+               list = NULL;
+               s = "foo";
+               list = g_slist_append (list, s);
+               _vala_assert (g_slist_length (list) == ((guint) 1), "list.length () == 1");
+               list = g_slist_remove (list, s);
+               _vala_assert (g_slist_length (list) == ((guint) 0), "list.length () == 0");
+               list = g_slist_append (list, s);
+               list = g_slist_remove_all (list, s);
+               _vala_assert (g_slist_length (list) == ((guint) 0), "list.length () == 0");
+               (list == NULL) ? NULL : (list = (g_slist_free (list), NULL));
+       }
+}
+
+static gboolean
+vala_g_queue_remove_full (GQueue* self,
+                          gconstpointer data,
+                          GFreeFunc func)
+{
+       GList* l = NULL;
+       GList* _tmp0_;
+       GList* _tmp1_;
+       GList* _tmp2_;
+       gboolean result = FALSE;
+       g_return_val_if_fail (self != NULL, FALSE);
+       _tmp0_ = self->head;
+       _tmp1_ = g_list_find (_tmp0_, data);
+       l = _tmp1_;
+       _tmp2_ = l;
+       if (_tmp2_ != NULL) {
+               GList* _tmp3_;
+               gconstpointer _tmp4_;
+               GList* _tmp5_;
+               _tmp3_ = l;
+               _tmp4_ = ((GList*) _tmp3_)->data;
+               func (_tmp4_);
+               _tmp5_ = l;
+               g_queue_delete_link (self, (GList*) _tmp5_);
+               result = TRUE;
+               return result;
+       } else {
+               result = FALSE;
+               return result;
+       }
+}
+
+static inline void
+_g_queue_free__g_object_unref0_ (GQueue* self)
+{
+       g_queue_free_full (self, (GDestroyNotify) _g_object_unref0_);
+}
+
+static guint
+vala_g_queue_remove_all_full (GQueue* self,
+                              gconstpointer data,
+                              GFreeFunc func)
+{
+       guint old_length = 0U;
+       GList* l = NULL;
+       GList* _tmp0_;
+       guint result = 0U;
+       g_return_val_if_fail (self != NULL, 0U);
+       old_length = self->length;
+       _tmp0_ = self->head;
+       l = _tmp0_;
+       while (TRUE) {
+               GList* _tmp1_;
+               GList* next = NULL;
+               GList* _tmp2_;
+               GList* _tmp3_;
+               GList* _tmp4_;
+               gconstpointer _tmp5_;
+               GList* _tmp9_;
+               _tmp1_ = l;
+               if (!(_tmp1_ != NULL)) {
+                       break;
+               }
+               _tmp2_ = l;
+               _tmp3_ = ((GList*) _tmp2_)->next;
+               next = _tmp3_;
+               _tmp4_ = l;
+               _tmp5_ = ((GList*) _tmp4_)->data;
+               if (_tmp5_ == data) {
+                       GList* _tmp6_;
+                       gconstpointer _tmp7_;
+                       GList* _tmp8_;
+                       _tmp6_ = l;
+                       _tmp7_ = ((GList*) _tmp6_)->data;
+                       func (_tmp7_);
+                       _tmp8_ = l;
+                       g_queue_delete_link (self, (GList*) _tmp8_);
+               }
+               _tmp9_ = next;
+               l = _tmp9_;
+       }
+       result = old_length - self->length;
+       return result;
+}
+
+void
+test_gqueue (void)
+{
+       {
+               GQueue* queue = NULL;
+               GQueue* _tmp0_;
+               Foo* foo = NULL;
+               Foo* _tmp1_;
+               Foo* _tmp2_;
+               _tmp0_ = g_queue_new ();
+               queue = _tmp0_;
+               _tmp1_ = foo_new ();
+               foo = _tmp1_;
+               _tmp2_ = _g_object_ref0 (foo);
+               g_queue_push_head (queue, _tmp2_);
+               _vala_assert (queue->length == ((guint) 1), "queue.length == 1");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
2), "foo.ref_count == 2");
+               vala_g_queue_remove_full (queue, foo, _g_object_unref0_);
+               _vala_assert (queue->length == ((guint) 0), "queue.length == 0");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
1), "foo.ref_count == 1");
+               _g_object_unref0 (foo);
+               (queue == NULL) ? NULL : (queue = (_g_queue_free__g_object_unref0_ (queue), NULL));
+       }
+       {
+               GQueue* queue = NULL;
+               GQueue* _tmp3_;
+               Foo* foo = NULL;
+               Foo* _tmp4_;
+               Foo* _tmp5_;
+               Foo* _tmp6_;
+               _tmp3_ = g_queue_new ();
+               queue = _tmp3_;
+               _tmp4_ = foo_new ();
+               foo = _tmp4_;
+               _tmp5_ = _g_object_ref0 (foo);
+               g_queue_push_head (queue, _tmp5_);
+               _tmp6_ = _g_object_ref0 (foo);
+               g_queue_push_head (queue, _tmp6_);
+               _vala_assert (queue->length == ((guint) 2), "queue.length == 2");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
3), "foo.ref_count == 3");
+               vala_g_queue_remove_all_full (queue, foo, _g_object_unref0_);
+               _vala_assert (queue->length == ((guint) 0), "queue.length == 0");
+               _vala_assert (G_TYPE_CHECK_INSTANCE_CAST (foo, G_TYPE_OBJECT, GObject)->ref_count == ((guint) 
1), "foo.ref_count == 1");
+               _g_object_unref0 (foo);
+               (queue == NULL) ? NULL : (queue = (_g_queue_free__g_object_unref0_ (queue), NULL));
+       }
+       {
+               GQueue* queue = NULL;
+               GQueue* _tmp7_;
+               const gchar* s = NULL;
+               _tmp7_ = g_queue_new ();
+               queue = _tmp7_;
+               s = "foo";
+               g_queue_push_head (queue, s);
+               _vala_assert (queue->length == ((guint) 1), "queue.length == 1");
+               g_queue_remove (queue, s);
+               _vala_assert (queue->length == ((guint) 0), "queue.length == 0");
+               g_queue_push_head (queue, s);
+               g_queue_remove_all (queue, s);
+               _vala_assert (queue->length == ((guint) 0), "queue.length == 0");
+               (queue == NULL) ? NULL : (queue = (g_queue_free (queue), NULL));
+       }
+}
+
+static void
+_vala_main (void)
+{
+       test_glist ();
+       test_gslist ();
+       test_gqueue ();
+}
+
+int
+main (int argc,
+      char ** argv)
+{
+       _vala_main ();
+       return 0;
+}
+
diff --git a/tests/basic-types/glists_remove.vala b/tests/basic-types/glists_remove.vala
new file mode 100644
index 000000000..8770f60f8
--- /dev/null
+++ b/tests/basic-types/glists_remove.vala
@@ -0,0 +1,113 @@
+class Foo : Object {
+}
+
+void test_glist () {
+       {
+               var list = new GLib.List<Foo> ();
+               var foo = new Foo ();
+               list.append (foo);
+               assert (list.length () == 1);
+               assert (foo.ref_count == 2);
+               list.remove (foo);
+               assert (list.length () == 0);
+               assert (foo.ref_count == 1);
+       }
+       {
+               var list = new GLib.List<Foo> ();
+               var foo = new Foo ();
+               list.append (foo);
+               list.append (foo);
+               assert (list.length () == 2);
+               assert (foo.ref_count == 3);
+               list.remove_all (foo);
+               assert (list.length () == 0);
+               assert (foo.ref_count == 1);
+       }
+       {
+               var list = new GLib.List<unowned string> ();
+               unowned var s = "foo";
+               list.append (s);
+               assert (list.length () == 1);
+               list.remove (s);
+               assert (list.length () == 0);
+               list.append (s);
+               list.remove_all (s);
+               assert (list.length () == 0);
+       }
+}
+
+void test_gslist () {
+       {
+               var list = new GLib.SList<Foo> ();
+               var foo = new Foo ();
+               list.append (foo);
+               assert (list.length () == 1);
+               assert (foo.ref_count == 2);
+               list.remove (foo);
+               assert (list.length () == 0);
+               assert (foo.ref_count == 1);
+       }
+       {
+               var list = new GLib.SList<Foo> ();
+               var foo = new Foo ();
+               list.append (foo);
+               list.append (foo);
+               assert (list.length () == 2);
+               assert (foo.ref_count == 3);
+               list.remove_all (foo);
+               assert (list.length () == 0);
+               assert (foo.ref_count == 1);
+       }
+       {
+               var list = new GLib.SList<unowned string> ();
+               unowned var s = "foo";
+               list.append (s);
+               assert (list.length () == 1);
+               list.remove (s);
+               assert (list.length () == 0);
+               list.append (s);
+               list.remove_all (s);
+               assert (list.length () == 0);
+       }
+}
+
+void test_gqueue () {
+       {
+               var queue = new GLib.Queue<Foo> ();
+               var foo = new Foo ();
+               queue.push_head (foo);
+               assert (queue.length == 1);
+               assert (foo.ref_count == 2);
+               queue.remove (foo);
+               assert (queue.length == 0);
+               assert (foo.ref_count == 1);
+       }
+       {
+               var queue = new GLib.Queue<Foo> ();
+               var foo = new Foo ();
+               queue.push_head (foo);
+               queue.push_head (foo);
+               assert (queue.length == 2);
+               assert (foo.ref_count == 3);
+               queue.remove_all (foo);
+               assert (queue.length == 0);
+               assert (foo.ref_count == 1);
+       }
+       {
+               var queue = new GLib.Queue<unowned string> ();
+               unowned var s = "foo";
+               queue.push_head (s);
+               assert (queue.length == 1);
+               queue.remove (s);
+               assert (queue.length == 0);
+               queue.push_head (s);
+               queue.remove_all (s);
+               assert (queue.length == 0);
+       }
+}
+
+void main () {
+       test_glist ();
+       test_gslist ();
+       test_gqueue ();
+}
diff --git a/vapi/glib-2.0.vapi b/vapi/glib-2.0.vapi
index 6d2d44ef5..86bdfa88c 100644
--- a/vapi/glib-2.0.vapi
+++ b/vapi/glib-2.0.vapi
@@ -5015,12 +5015,42 @@ namespace GLib {
                public void insert_sorted (owned G data, CompareFunc<G> compare_func);
                [ReturnsModifiedPointer ()]
                public void remove (G data);
+               [CCode (cname = "vala_g_list_remove_full")]
+               [ReturnsModifiedPointer ()]
+               public unowned List<G> remove_full (G data, FreeFunc? func) {
+                       unowned List<G>? l = this;
+                       while (l != null) {
+                               if (((!) l).data != data) {
+                                       l = ((!) l).next;
+                               } else {
+                                       func (((!) l).data);
+                                       delete_link ((!) l);
+                                       break;
+                               }
+                       }
+                       return this;
+               }
                [ReturnsModifiedPointer ()]
                public void remove_link (List<G> llink);
                [ReturnsModifiedPointer ()]
                public void delete_link (List<G> link_);
                [ReturnsModifiedPointer ()]
                public void remove_all (G data);
+               [CCode (cname = "vala_g_list_remove_all_full")]
+               [ReturnsModifiedPointer ()]
+               public unowned List<G> remove_all_full (G data, FreeFunc? func) {
+                       unowned List<G>? l = this;
+                       while (l != null) {
+                               if (((!) l).data != data) {
+                                       l = ((!) l).next;
+                               } else {
+                                       func (((!) l).data);
+                                       delete_link ((!) l);
+                                       l = this;
+                               }
+                       }
+                       return this;
+               }
 
                public uint length ();
                public List<unowned G> copy ();
@@ -5082,12 +5112,42 @@ namespace GLib {
                public void insert_sorted (owned G data, CompareFunc<G> compare_func);
                [ReturnsModifiedPointer ()]
                public void remove (G data);
+               [CCode (cname = "vala_g_slist_remove_full")]
+               [ReturnsModifiedPointer ()]
+               public unowned SList<G> remove_full (G data, FreeFunc? func) {
+                       unowned SList<G>? l = this;
+                       while (l != null) {
+                               if (((!) l).data != data) {
+                                       l = ((!) l).next;
+                               } else {
+                                       func (((!) l).data);
+                                       delete_link ((!) l);
+                                       break;
+                               }
+                       }
+                       return this;
+               }
                [ReturnsModifiedPointer ()]
                public void remove_link (SList<G> llink);
                [ReturnsModifiedPointer ()]
                public void delete_link (SList<G> link_);
                [ReturnsModifiedPointer ()]
                public void remove_all (G data);
+               [CCode (cname = "vala_g_slist_remove_all_full")]
+               [ReturnsModifiedPointer ()]
+               public unowned SList<G> remove_all_full (G data, FreeFunc? func) {
+                       unowned SList<G>? l = this;
+                       while (l != null) {
+                               if (((!) l).data != data) {
+                                       l = ((!) l).next;
+                               } else {
+                                       func (((!) l).data);
+                                       delete_link ((!) l);
+                                       l = this;
+                               }
+                       }
+                       return this;
+               }
 
                public uint length ();
                public SList<unowned G> copy ();
@@ -5186,8 +5246,33 @@ namespace GLib {
                public int index (G data);
                [Version (since = "2.4")]
                public bool remove (G data);
+               [CCode (cname = "vala_g_queue_remove_full")]
+               public bool remove_full (G data, FreeFunc? func) {
+                       unowned List<G>? l = head.find (data);
+                       if (l != null) {
+                               func (((!) l).data);
+                               delete_link ((!) l);
+                               return true;
+                       } else {
+                               return false;
+                       }
+               }
                [Version (since = "2.4")]
                public uint remove_all (G data);
+               [CCode (cname = "vala_g_queue_remove_all_full")]
+               public uint remove_all_full (G data, FreeFunc? func) {
+                       var old_length = length;
+                       unowned List<G>? l = head;
+                       while (l != null) {
+                               unowned List<G>? next = ((!) l).next;
+                               if (((!) l).data == data) {
+                                       func (((!) l).data);
+                                       delete_link ((!) l);
+                               }
+                               l = next;
+                       }
+                       return old_length - length;
+               }
                [Version (since = "2.4")]
                public void delete_link (List<G> link);
                [Version (since = "2.4")]


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]