From: Rico Tzschichholz Date: Thu, 14 Oct 2021 17:44:55 +0000 (+0200) Subject: codegen: Actually free data when using "remove(_all)" on GLib.Queue/(S)List X-Git-Tag: 0.55.1~138 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6ac079db81d5847490957c09ae898304862bfe44;p=thirdparty%2Fvala.git 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 --- diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala index ceb9370ec..94c31007b 100644 --- a/codegen/valaccodemethodcallmodule.vala +++ b/codegen/valaccodemethodcallmodule.vala @@ -799,6 +799,28 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { } + // Transform and add free function argument to GLib.[List,Queue,SList].remove[_all] calls + unowned DataType? collection_type = null; + if (ma != null && ma.inner != null) { + collection_type = ma.inner.value_type; + } + if (collection_type != null + && (collection_type.type_symbol == glist_type || collection_type.type_symbol == gslist_type || collection_type.type_symbol == gqueue_type) + && (ma.member_name == "remove" || ma.member_name == "remove_all") + //FIXME Perform stricter type argument check earlier + && collection_type.check_type_arguments (context)) { + var remove_method = (Method) collection_type.type_symbol.scope.lookup (ma.member_name + "_full"); + var type_arg = collection_type.get_type_arguments ()[0]; + if (remove_method != null && requires_destroy (type_arg)) { + // 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 7cd0168b7..bbd5bfcc9 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 +#include +#include +#include + +#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 (); + 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 (); + 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 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 (); + 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 (); + 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 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 (); + 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 (); + 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 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 compare_func); [ReturnsModifiedPointer ()] public void remove (G data); + [CCode (cname = "vala_g_list_remove_full")] + [ReturnsModifiedPointer ()] + public unowned List remove_full (G data, FreeFunc? func) { + unowned List? 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 llink); [ReturnsModifiedPointer ()] public void delete_link (List link_); [ReturnsModifiedPointer ()] public void remove_all (G data); + [CCode (cname = "vala_g_list_remove_all_full")] + [ReturnsModifiedPointer ()] + public unowned List remove_all_full (G data, FreeFunc? func) { + unowned List? 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 copy (); @@ -5082,12 +5112,42 @@ namespace GLib { public void insert_sorted (owned G data, CompareFunc compare_func); [ReturnsModifiedPointer ()] public void remove (G data); + [CCode (cname = "vala_g_slist_remove_full")] + [ReturnsModifiedPointer ()] + public unowned SList remove_full (G data, FreeFunc? func) { + unowned SList? 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 llink); [ReturnsModifiedPointer ()] public void delete_link (SList link_); [ReturnsModifiedPointer ()] public void remove_all (G data); + [CCode (cname = "vala_g_slist_remove_all_full")] + [ReturnsModifiedPointer ()] + public unowned SList remove_all_full (G data, FreeFunc? func) { + unowned SList? 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 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? 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? l = head; + while (l != null) { + unowned List? 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 link); [Version (since = "2.4")]