[vala/staging] codegen: Fix finally blocks with async yields



commit cbd819c59f614254f1f241b3e37ebb8f312f1ae7
Author: Luca Bruno <lucabru src gnome org>
Date:   Wed Dec 24 23:20:58 2014 +0100

    codegen: Fix finally blocks with async yields
    
    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

 ccode/valaccodefunction.vala           |    6 +++-
 codegen/valaccodebasemodule.vala       |    4 +-
 codegen/valaccodemethodcallmodule.vala |    2 +-
 codegen/valaccodemethodmodule.vala     |   34 +++++++++++++--------
 codegen/valagasyncmodule.vala          |    2 +-
 codegen/valagdbusclientmodule.vala     |    2 +-
 tests/Makefile.am                      |    1 +
 tests/asynchronous/bug741929.vala      |   50 ++++++++++++++++++++++++++++++++
 vala/valamethod.vala                   |    2 -
 vala/valamethodcall.vala               |    1 -
 vala/valaobjectcreationexpression.vala |    1 -
 vala/valayieldstatement.vala           |    2 -
 12 files changed, 82 insertions(+), 25 deletions(-)
---
diff --git a/ccode/valaccodefunction.vala b/ccode/valaccodefunction.vala
index 08d3876..ef8dad6 100644
--- a/ccode/valaccodefunction.vala
+++ b/ccode/valaccodefunction.vala
@@ -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") {
diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala
index f083e98..09e8c9c 100644
--- a/codegen/valaccodebasemodule.vala
+++ b/codegen/valaccodebasemodule.vala
@@ -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);
diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala
index 6202f31..69d6802 100644
--- a/codegen/valaccodemethodcallmodule.vala
+++ b/codegen/valaccodemethodcallmodule.vala
@@ -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);
diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala
index 700c7af..4d0d5fb 100644
--- a/codegen/valaccodemethodmodule.vala
+++ b/codegen/valaccodemethodmodule.vala
@@ -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
diff --git a/codegen/valagasyncmodule.vala b/codegen/valagasyncmodule.vala
index a077b4a..74bbf57 100644
--- a/codegen/valagasyncmodule.vala
+++ b/codegen/valagasyncmodule.vala
@@ -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"));
diff --git a/codegen/valagdbusclientmodule.vala b/codegen/valagdbusclientmodule.vala
index 31cd12e..d986b24 100644
--- a/codegen/valagdbusclientmodule.vala
+++ b/codegen/valagdbusclientmodule.vala
@@ -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);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2a2f4d4..fb58de2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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
index 0000000..b781823
--- /dev/null
+++ b/tests/asynchronous/bug741929.vala
@@ -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 ();
+}
+
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index 9988c66..c37cfe7 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -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;
diff --git a/vala/valamethodcall.vala b/vala/valamethodcall.vala
index cefc651..444395a 100644
--- a/vala/valamethodcall.vala
+++ b/vala/valamethodcall.vala
@@ -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
diff --git a/vala/valaobjectcreationexpression.vala b/vala/valaobjectcreationexpression.vala
index c4b119a..4e5a9b9 100644
--- a/vala/valaobjectcreationexpression.vala
+++ b/vala/valaobjectcreationexpression.vala
@@ -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
diff --git a/vala/valayieldstatement.vala b/vala/valayieldstatement.vala
index 8b2069f..5b5a6f3 100644
--- a/vala/valayieldstatement.vala
+++ b/vala/valayieldstatement.vala
@@ -75,8 +75,6 @@ public class Vala.YieldStatement : CodeNode, Statement {
                        error = yield_expression.error;
                }
 
-               context.analyzer.current_method.yield_count++;
-
                return !error;
        }
 


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