]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
codegen: Fix finally blocks with async yields
authorLuca Bruno <lucabru@src.gnome.org>
Wed, 24 Dec 2014 22:20:58 +0000 (23:20 +0100)
committerRico Tzschichholz <ricotz@ubuntu.com>
Tue, 9 May 2017 11:49:28 +0000 (13:49 +0200)
The Method.yield_count is not correct because in C the finally blocks may
be emitted twice.

https://bugzilla.gnome.org/show_bug.cgi?id=741929

12 files changed:
ccode/valaccodefunction.vala
codegen/valaccodebasemodule.vala
codegen/valaccodemethodcallmodule.vala
codegen/valaccodemethodmodule.vala
codegen/valagasyncmodule.vala
codegen/valagdbusclientmodule.vala
tests/Makefile.am
tests/asynchronous/bug741929.vala [new file with mode: 0644]
vala/valamethod.vala
vala/valamethodcall.vala
vala/valaobjectcreationexpression.vala
vala/valayieldstatement.vala

index 08d3876fb9b86b42de0f0f3dce25e135e3ec3606..ef8dad6a8d1c21ac0f572b06e72d2dce4df20aea 100644 (file)
@@ -48,9 +48,13 @@ public class Vala.CCodeFunction : CCodeNode {
         */
        public CCodeLineDirective current_line { get; set; }
 
+       /**
+        * The current block to be written into.
+        */
+       public CCodeBlock current_block { get; set; }
+
        private List<CCodeParameter> parameters = new ArrayList<CCodeParameter> ();
 
-       CCodeBlock current_block;
        List<CCodeStatement> statement_stack = new ArrayList<CCodeStatement> ();
 
        public CCodeFunction (string name, string return_type = "void") {
index f083e9848865ed6e1c076028c077d24d5dd12f8b..09e8c9cb7b2c99aaae66d51ec723eca02bf23b08 100644 (file)
@@ -38,6 +38,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
                public int next_temp_var_id;
                public bool current_method_inner_error;
                public bool current_method_return;
+               public int next_coroutine_state = 1;
                public Map<string,string> variable_name_map = new HashMap<string,string> (str_hash, str_equal);
                public Map<string,int> closure_variable_count_map = new HashMap<string,int> (str_hash, str_equal);
                public Map<LocalVariable,int> closure_variable_clash_map = new HashMap<LocalVariable,int> ();
@@ -270,7 +271,6 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
                set { emit_context.current_method_return = value; }
        }
 
-       public int next_coroutine_state = 1;
        int next_block_id = 0;
        Map<Block,int> block_map = new HashMap<Block,int> ();
 
@@ -4796,7 +4796,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
 
                        if (expr.is_yield_expression) {
                                // set state before calling async function to support immediate callbacks
-                               int state = next_coroutine_state++;
+                               int state = emit_context.next_coroutine_state++;
 
                                ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
                                ccode.add_expression (async_call);
index 6202f319b74f7f05ed356e8be5a7067332b44618..69d68026f6acee6c3d34155a261149b1442d303f 100644 (file)
@@ -704,7 +704,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule {
 
                if (expr.is_yield_expression) {
                        // set state before calling async function to support immediate callbacks
-                       int state = next_coroutine_state++;
+                       int state = emit_context.next_coroutine_state++;
 
                        ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
                        ccode.add_expression (async_call);
index 700c7afdcc40ad21b4bbb5d3a402dce310a1ba55..4d0d5fbc4b27ed4b0ce880f0b78b3e0e3c3d72e2 100644 (file)
@@ -361,10 +361,6 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
                        }
                }
 
-               if (m.coroutine) {
-                       next_coroutine_state = 1;
-               }
-
                var creturn_type = m.return_type;
                if (m.return_type.is_real_non_null_struct_type ()) {
                        // structs are returned via out parameter
@@ -489,6 +485,8 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
 
                push_function (function);
 
+               unowned CCodeBlock? co_switch_block = null;
+
                // generate *_real_* functions for virtual methods
                // also generate them for abstract methods of classes to prevent faulty subclassing
                if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
@@ -500,15 +498,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
                                        ccode.add_case (new CCodeConstant ("0"));
                                        ccode.add_goto ("_state_0");
 
-                                       for (int state = 1; state <= m.yield_count; state++) {
-                                               ccode.add_case (new CCodeConstant (state.to_string ()));
-                                               ccode.add_goto ("_state_%d".printf (state));
-                                       }
-
-
-                                       // let gcc know that this can't happen
-                                       ccode.add_default ();
-                                       ccode.add_expression (new CCodeFunctionCall (new CCodeIdentifier ("g_assert_not_reached")));
+                                       co_switch_block = ccode.current_block;
 
                                        ccode.close ();
 
@@ -732,6 +722,24 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
 
                if (m.body != null) {
                        m.body.emit (this);
+
+                       if (co_switch_block != null) {
+                               // after counting the number of yields for coroutines, append the case statements to the switch
+                               var old_block = ccode.current_block;
+                               ccode.current_block = co_switch_block;
+
+                               for (int state = 1; state < emit_context.next_coroutine_state; state++) {
+                                       ccode.add_case (new CCodeConstant (state.to_string ()));
+                                       ccode.add_goto ("_state_%d".printf (state));
+                               }
+
+                               // let gcc know that this can't happen
+                               ccode.add_default ();
+                               ccode.add_expression (new CCodeFunctionCall (new CCodeIdentifier ("g_assert_not_reached")));
+
+                               ccode.current_block = old_block;
+                               co_switch_block = null;
+                       }
                }
 
                // we generate the same code if we see a return statement, this handles the case without returns
index a077b4a5409d093f5869888e5fa8594baee843d9..74bbf5790f94aedeb32d24c8f27be6bff4275f55 100644 (file)
@@ -764,7 +764,7 @@ public class Vala.GAsyncModule : GtkModule {
                }
 
                if (stmt.yield_expression == null) {
-                       int state = next_coroutine_state++;
+                       int state = emit_context.next_coroutine_state++;
 
                        ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
                        ccode.add_return (new CCodeConstant ("FALSE"));
index 31cd12e5511f183da2883e5c6357c3bf52559c65..d986b24db78480b269e616cb2618545d9d8ebfd5 100644 (file)
@@ -407,7 +407,7 @@ public class Vala.GDBusClientModule : GDBusModule {
 
                if (bus_get_proxy_async || conn_get_proxy_async) {
                        if (expr.is_yield_expression) {
-                               int state = next_coroutine_state++;
+                               int state = emit_context.next_coroutine_state++;
 
                                ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
                                ccode.add_expression (ccall);
index 2a2f4d43c5d9f91b0cf123136c01b39b04eaea08..fb58de2cada173c9522fdb46223fe2165f16b44d 100644 (file)
@@ -258,6 +258,7 @@ TESTS = \
        asynchronous/bug659886.vala \
        asynchronous/bug661961.vala \
        asynchronous/bug710103.vala \
+       asynchronous/bug741929.vala \
        asynchronous/bug742621.vala \
        asynchronous/bug762819.vala \
        asynchronous/bug777242.vala \
diff --git a/tests/asynchronous/bug741929.vala b/tests/asynchronous/bug741929.vala
new file mode 100644 (file)
index 0000000..b781823
--- /dev/null
@@ -0,0 +1,50 @@
+MainLoop? loop = null;
+
+class Foo : Object {
+       bool running = false;
+
+       public Foo () {
+       }
+
+       public async void query_async () throws Error {
+               running = true;
+
+               try {
+                       if (!yield internal_query_async ()) {
+                               return;
+                       }
+               } finally {
+                       try {
+                               yield close_query_async ();
+                       } catch (Error e) {
+                               // ignored
+                       }
+
+                       running = false;
+               }
+       }
+
+       async bool internal_query_async () throws Error {
+               return true;
+       }
+
+       async void close_query_async () throws Error {
+       }
+}
+
+async void go_async () {
+       Foo foo = new Foo ();
+       try {
+               yield foo.query_async ();
+       } catch (Error e) {
+       }
+
+       loop.quit ();
+}
+
+void main () {
+       loop = new MainLoop ();
+       go_async.begin ();
+       loop.run ();
+}
+
index 9988c6628b38a789f8c4b57cd90896f15eb3a0d8..c37cfe7f125eb76d70481cfbcce3c4d864994bb9 100644 (file)
@@ -183,8 +183,6 @@ public class Vala.Method : Subroutine, Callable {
 
        public bool is_async_callback { get; set; }
 
-       public int yield_count { get; set; }
-
        private List<Parameter> parameters = new ArrayList<Parameter> ();
        private List<Expression> preconditions;
        private List<Expression> postconditions;
index cefc65131afea4ca27aced6d48d87365612ca305..444395ac3f7e7a6a47895b361f1d14d42a5ff0ae 100644 (file)
@@ -478,7 +478,6 @@ public class Vala.MethodCall : Expression {
                                        error = true;
                                        Report.error (source_reference, "yield expression not available outside async method");
                                }
-                               context.analyzer.current_method.yield_count++;
                        }
                        if (m != null && m.coroutine && !is_yield_expression && ((MemberAccess) call).member_name != "end") {
                                // .begin call of async method, no error can happen here
index c4b119a44ca03ae9912df09fe975434b7fb9a1bd..4e5a9b958277661dd0f57934cf45cc102ff325d2 100644 (file)
@@ -358,7 +358,6 @@ public class Vala.ObjectCreationExpression : Expression {
                                        error = true;
                                        Report.error (source_reference, "yield expression not available outside async method");
                                }
-                               context.analyzer.current_method.yield_count++;
                        }
 
                        // FIXME partial code duplication of MethodCall.check
index 8b2069f21833f68efcfff81f05c83bd95136669d..5b5a6f30717645f211a01b9447f146fa6df74637 100644 (file)
@@ -75,8 +75,6 @@ public class Vala.YieldStatement : CodeNode, Statement {
                        error = yield_expression.error;
                }
 
-               context.analyzer.current_method.yield_count++;
-
                return !error;
        }