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



commit 768775f596669f40cf28f1b2993b5d7afaaffdc6
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 |  22 +++++++
 tests/Makefile.am                      |   1 +
 tests/basic-types/glists_remove.vala   | 113 +++++++++++++++++++++++++++++++++
 vapi/glib-2.0.vapi                     |  85 +++++++++++++++++++++++++
 4 files changed, 221 insertions(+)
---
diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala
index 7154c18df..914458611 100644
--- a/codegen/valaccodemethodcallmodule.vala
+++ b/codegen/valaccodemethodcallmodule.vala
@@ -826,6 +826,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 c7d982a50..c40ec0c75 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -58,6 +58,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.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 00153e31c..373877f22 100644
--- a/vapi/glib-2.0.vapi
+++ b/vapi/glib-2.0.vapi
@@ -4893,12 +4893,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 ();
@@ -4955,12 +4985,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 ();
@@ -5054,8 +5114,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]