]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
codegen: Don't leak memory of already assigned out-parameter on error
authorRico Tzschichholz <ricotz@ubuntu.com>
Thu, 31 Dec 2020 08:55:16 +0000 (09:55 +0100)
committerRico Tzschichholz <ricotz@ubuntu.com>
Thu, 31 Dec 2020 09:33:11 +0000 (10:33 +0100)
Fixes https://gitlab.gnome.org/GNOME/vala/issues/1123

codegen/valaccodebasemodule.vala
codegen/valagasyncmodule.vala
codegen/valagerrormodule.vala
tests/Makefile.am
tests/asynchronous/out-parameter-free-on-error.vala [new file with mode: 0644]
tests/methods/parameter-out-free-on-error.vala [new file with mode: 0644]

index b1f7e68699f447e0e8d08a6cd6e25be6d0bba45b..b7db3183514dae06b4fdafd608c2398b1535b4f0 100644 (file)
@@ -3972,6 +3972,17 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
                }
        }
 
+       public void append_out_param_free (Method? m) {
+               if (m == null) {
+                       return;
+               }
+               foreach (Parameter param in m.get_parameters ()) {
+                       if (param.direction == ParameterDirection.OUT && param.variable_type.is_disposable ()) {
+                               ccode.add_expression (destroy_parameter (param));
+                       }
+               }
+       }
+
        public bool variable_accessible_in_finally (LocalVariable local) {
                if (current_try == null) {
                        return false;
index f39f86c6ea6a23915c871be36f0a8140a24fda92..c9946599f5a0a060e5c156fedcfda7cbe8cc9a1e 100644 (file)
@@ -743,8 +743,12 @@ public class Vala.GAsyncModule : GtkModule {
                set_error.add_argument (error_expr);
                ccode.add_expression (set_error);
 
+               // free local variables
                append_local_free (current_symbol);
 
+               // free possibly already assigned out-parameter
+               append_out_param_free (current_method);
+
                // We already returned the error above, we must not return anything else here.
                var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
                unref.add_argument (async_result_expr);
index fedc424ae71767515df522b6ede8467295260480..d724c3c3a2f163f2524c806a4275ed4cd50326b5 100644 (file)
@@ -104,6 +104,9 @@ public class Vala.GErrorModule : CCodeDelegateModule {
                // free local variables
                append_local_free (current_symbol);
 
+               // free possibly already assigned out-parameter
+               append_out_param_free (current_method);
+
                if (current_method is CreationMethod && current_method.parent_symbol is Class) {
                        var cl = (Class) current_method.parent_symbol;
                        ccode.add_expression (destroy_value (new GLibValue (new ObjectType (cl), new CCodeIdentifier ("self"), true)));
@@ -123,6 +126,9 @@ public class Vala.GErrorModule : CCodeDelegateModule {
                        append_local_free (current_symbol);
                }
 
+               // free possibly already assigned out-parameter
+               append_out_param_free (current_method);
+
                cfile.add_include ("glib.h");
 
                var ccritical = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
index 517d5746a4e084968d61d0c0f09e3e4893d97aa2..3d41840ed264c02e8b1ead92615c6eca387a0566 100644 (file)
@@ -162,6 +162,7 @@ TESTS = \
        methods/extern.vala \
        methods/iterator.vala \
        methods/parameter-fixed-array-initializer.vala \
+       methods/parameter-out-free-on-error.vala \
        methods/parameter-ref-array-resize.vala \
        methods/parameter-ref-array-resize-captured.vala \
        methods/parameter-ref-delegate.vala \
@@ -632,6 +633,7 @@ TESTS = \
        asynchronous/constructor-argument-check.vala \
        asynchronous/finish-name.vala \
        asynchronous/generator.vala \
+       asynchronous/out-parameter-free-on-error.vala \
        asynchronous/out-parameter-invalid.test \
        asynchronous/params-array-invalid.test \
        asynchronous/result-pos.vala \
diff --git a/tests/asynchronous/out-parameter-free-on-error.vala b/tests/asynchronous/out-parameter-free-on-error.vala
new file mode 100644 (file)
index 0000000..a81d3df
--- /dev/null
@@ -0,0 +1,31 @@
+errordomain FooError {
+       FAIL
+}
+
+class Manam : Object {
+}
+
+async void foo_async (Manam i, out Manam o) throws FooError {
+       o = i;
+       throw new FooError.FAIL ("foo");
+}
+
+async void run () {
+       var manam = new Manam ();
+       assert (manam.ref_count == 1);
+       try {
+               Manam minim;
+               yield foo_async (manam, out minim);
+       } catch {
+       }
+       assert (manam.ref_count == 2);
+       loop.quit ();
+}
+
+MainLoop loop;
+
+void main () {
+       loop = new MainLoop ();
+       run.begin ();
+       loop.run ();
+}
diff --git a/tests/methods/parameter-out-free-on-error.vala b/tests/methods/parameter-out-free-on-error.vala
new file mode 100644 (file)
index 0000000..ee9716b
--- /dev/null
@@ -0,0 +1,39 @@
+errordomain FooError {
+       FAIL
+}
+
+class Manam : Object {
+}
+
+void foo (Manam i, out Manam o) throws FooError {
+       o = i;
+       throw new FooError.FAIL ("foo");
+}
+
+void bar (Manam i, out unowned Manam o) throws FooError {
+       o = i;
+       throw new FooError.FAIL ("bar");
+}
+
+void main () {
+       {
+               var manam = new Manam ();
+               assert (manam.ref_count == 1);
+               try {
+                       Manam minim;
+                       foo (manam, out minim);
+               } catch (FooError e) {
+               }
+               assert (manam.ref_count == 1);
+       }
+       {
+               var manam = new Manam ();
+               assert (manam.ref_count == 1);
+               try {
+                       unowned Manam minim;
+                       bar (manam, out minim);
+               } catch (FooError e) {
+               }
+               assert (manam.ref_count == 1);
+       }
+}