]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
Fix lock statement
authorJiří Zárevúcky <zarevucky.jiri@gmail.com>
Wed, 24 Mar 2010 07:36:37 +0000 (08:36 +0100)
committerJürg Billeter <j@bitron.ch>
Wed, 24 Mar 2010 07:36:37 +0000 (08:36 +0100)
This patch converts lock statements into try finally statements to
ensure that unlock is always called.

Fixes bug 582553.

codegen/valaccodebasemodule.vala
codegen/valaccodegenerator.vala
codegen/valaccodemodule.vala
vala/Makefile.am
vala/valacodevisitor.vala
vala/valaflowanalyzer.vala
vala/valalockstatement.vala
vala/valaunlockstatement.vala [new file with mode: 0644]

index 89ef3d5833982533c2242fdcff97458f64438b6f..223d49a2128d9d68efb2846b6a62a32043b32363 100644 (file)
@@ -3400,28 +3400,26 @@ internal class Vala.CCodeBaseModule : CCodeModule {
                return "__lock_%s".printf (symname);
        }
 
-       public override void visit_lock_statement (LockStatement stmt) {
-               var cn = new CCodeFragment ();
+       private CCodeExpression get_lock_expression (Statement stmt, Expression resource) {
                CCodeExpression l = null;
-               CCodeFunctionCall fc;
-               var inner_node = ((MemberAccess)stmt.resource).inner;
-               var member = (Member)stmt.resource.symbol_reference;
-               var parent = (TypeSymbol) stmt.resource.symbol_reference.parent_symbol;
+               var inner_node = ((MemberAccess)resource).inner;
+               var member = (Member)resource.symbol_reference;
+               var parent = (TypeSymbol)resource.symbol_reference.parent_symbol;
                
                if (member.is_instance_member ()) {
                        if (inner_node  == null) {
                                l = new CCodeIdentifier ("self");
-                       } else if (stmt.resource.symbol_reference.parent_symbol != current_type_symbol) {
-                                l = generate_instance_cast ((CCodeExpression) inner_node.ccodenode, parent);
+                       } else if (resource.symbol_reference.parent_symbol != current_type_symbol) {
+                               l = generate_instance_cast ((CCodeExpression) inner_node.ccodenode, parent);
                        } else {
                                l = (CCodeExpression) inner_node.ccodenode;
                        }
 
-                       l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (stmt.resource.symbol_reference.name));
+                       l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (resource.symbol_reference.name));
                } else if (member.is_class_member ()) {
                        CCodeExpression klass;
 
-                       if (current_method != null && current_method.binding == MemberBinding.INSTANCE ||
+                       if (current_method != null && current_method.binding == MemberBinding.INSTANCE ||
                            current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE ||
                            (in_constructor && !in_static_or_class_context)) {
                                var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
@@ -3433,23 +3431,33 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 
                        var get_class_private_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf(parent.get_upper_case_cname ())));
                        get_class_private_call.add_argument (klass);
-                       l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (stmt.resource.symbol_reference.name));
+                       l = new CCodeMemberAccess.pointer (get_class_private_call, get_symbol_lock_name (resource.symbol_reference.name));
                } else {
-                       string lock_name = "%s_%s".printf(parent.get_lower_case_cname (), stmt.resource.symbol_reference.name);
+                       string lock_name = "%s_%s".printf(parent.get_lower_case_cname (), resource.symbol_reference.name);
                        l = new CCodeIdentifier (get_symbol_lock_name (lock_name));
                }
+               return l;
+       }
                
-               fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("lock")).get_cname ()));
+       public override void visit_lock_statement (LockStatement stmt) {
+               var l = get_lock_expression (stmt, stmt.resource);
+
+               var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("lock")).get_cname ()));
                fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
 
+               var cn = new CCodeFragment ();
                cn.append (new CCodeExpressionStatement (fc));
+               stmt.ccodenode = cn;
+       }
                
-               cn.append (stmt.body.ccodenode);
+       public override void visit_unlock_statement (UnlockStatement stmt) {
+               var l = get_lock_expression (stmt, stmt.resource);
                
-               fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("unlock")).get_cname ()));
+               var fc = new CCodeFunctionCall (new CCodeIdentifier (((Method) mutex_type.scope.lookup ("unlock")).get_cname ()));
                fc.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, l));
-               cn.append (new CCodeExpressionStatement (fc));
                
+               var cn = new CCodeFragment ();
+               cn.append (new CCodeExpressionStatement (fc));
                stmt.ccodenode = cn;
        }
 
index fd465d17cb483a87dcc6ae2b064f273293cd7887..f8af697d09fb26e7284f834d67c55112d8236d6c 100644 (file)
@@ -228,6 +228,10 @@ public class Vala.CCodeGenerator : CodeGenerator {
                head.visit_lock_statement (stmt);
        }
 
+       public override void visit_unlock_statement (UnlockStatement stmt) {
+               head.visit_unlock_statement (stmt);
+       }
+
        public override void visit_delete_statement (DeleteStatement stmt) {
                head.visit_delete_statement (stmt);
        }
index d8e88e3169e1fc6898645ff7087f9ab947c5f002..0355e0c2762a8734b805c00a9af7224cb4cac88f 100644 (file)
@@ -207,6 +207,10 @@ public abstract class Vala.CCodeModule {
                next.visit_lock_statement (stmt);
        }
 
+       public virtual void visit_unlock_statement (UnlockStatement stmt) {
+               next.visit_unlock_statement (stmt);
+       }
+
        public virtual void visit_delete_statement (DeleteStatement stmt) {
                next.visit_delete_statement (stmt);
        }
index 69f0d0ac6f158b667012c71bfc5fef59d883b542..aa0b861f4867c246426b3f95f223ef7a3788867b 100644 (file)
@@ -151,6 +151,7 @@ libvalacore_la_VALASOURCES = \
        valatypeparameter.vala \
        valatypesymbol.vala \
        valaunaryexpression.vala \
+       valaunlockstatement.vala \
        valaunresolvedsymbol.vala \
        valaunresolvedtype.vala \
        valausingdirective.vala \
index f11a3ef43e5ec650746e1037ee0e7a7da6bcf68e..55f87d7cc4f3761c0117cbf697cf82aae9293d8a 100644 (file)
@@ -404,6 +404,14 @@ public abstract class Vala.CodeVisitor {
        public virtual void visit_lock_statement (LockStatement stmt) {
        }
 
+       /**
+        * Visit operation called for unlock statements.
+        *
+        * @param stmt an unlock statement
+        */
+       public virtual void visit_unlock_statement (UnlockStatement stmt) {
+       }
+
        /**
         * Visit operation called for delete statements.
         *
index ff5f255dbd03b76025ce19b23e59e37a6f17cc77..193f6103a59acfee073cceb56e3c8dd975c49aef 100644 (file)
@@ -1010,8 +1010,12 @@ public class Vala.FlowAnalyzer : CodeVisitor {
                if (unreachable (stmt)) {
                        return;
                }
+       }
 
-               stmt.body.accept (this);
+       public override void visit_unlock_statement (UnlockStatement stmt) {
+               if (unreachable (stmt)) {
+                       return;
+               }
        }
 
        public override void visit_expression (Expression expr) {
index 9ad5dfbddb54680b9475d64f179d845eadb21616..da9a0be94706d70b4a95c029744f8371669c1c83 100644 (file)
@@ -1,5 +1,6 @@
 /* valalockstatement.vala
  *
+ * Copyright (C) 2009  Jiří Zárevúcky
  * Copyright (C) 2006-2007  Raffaele Sandrini, Jürg Billeter
  *
  * This library is free software; you can redistribute it and/or
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
  *
- * Author:
+ * Authors:
  *     Raffaele Sandrini <raffaele@sandrini.ch>
+ *     Jiří Zárevúcky <zarevucky.jiri@gmail.com>
  */
 
 using GLib;
 
 /**
- * Represents a lock statement e.g. {{{ lock (a) { f(a) } }}}.
+ * Represents a lock statement e.g. {{{ lock (a); }}} or {{{ lock (a) { f(a); } }}}.
+ *
+ * If the statement is empty, the mutex remains locked until a corresponding UnlockStatement
+ * occurs. Otherwise it's translated into a try/finally statement which unlocks the mutex
+ * after the block is finished.
  */
 public class Vala.LockStatement : CodeNode, Statement {
        /**
@@ -34,9 +40,9 @@ public class Vala.LockStatement : CodeNode, Statement {
        /**
         * The statement during its execution the resource is locked.
         */
-       public Block body { get; set; }
+       public Block? body { get; set; }
        
-       public LockStatement (Expression resource, Block body, SourceReference? source_reference = null) {
+       public LockStatement (Expression resource, Block? body, SourceReference? source_reference = null) {
                this.body = body;
                this.source_reference = source_reference;
                this.resource = resource;
@@ -44,11 +50,29 @@ public class Vala.LockStatement : CodeNode, Statement {
        
        public override void accept (CodeVisitor visitor) {
                resource.accept (visitor);
-               body.accept (visitor);
+               if (body != null) {
+                       body.accept (visitor);
+               }
                visitor.visit_lock_statement (this);
        }
 
        public override bool check (SemanticAnalyzer analyzer) {
+               if (body != null) {
+                       // if the statement isn't empty, it is converted into a try statement
+
+                       var fin_body = new Block (source_reference);
+                       fin_body.add_statement (new UnlockStatement (resource, source_reference));
+
+                       var block = new Block (source_reference);
+                       block.add_statement (new LockStatement (resource, null, source_reference));
+                       block.add_statement (new TryStatement (body, fin_body, source_reference));
+
+                       var parent_block = (Block) parent_node;
+                       parent_block.replace_statement (this, block);
+
+                       return block.check (analyzer);
+               }
+
                if (checked) {
                        return !error;
                }
@@ -56,11 +80,10 @@ public class Vala.LockStatement : CodeNode, Statement {
                checked = true;
 
                resource.check (analyzer);
-               body.check (analyzer);
 
                /* resource must be a member access and denote a Lockable */
                if (!(resource is MemberAccess && resource.symbol_reference is Lockable)) {
-                       error = true;
+                       error = true;
                        resource.error = true;
                        Report.error (resource.source_reference, "Expression is either not a member access or does not denote a lockable member");
                        return false;
@@ -68,7 +91,7 @@ public class Vala.LockStatement : CodeNode, Statement {
 
                /* parent symbol must be the current class */
                if (resource.symbol_reference.parent_symbol != analyzer.current_class) {
-                       error = true;
+                       error = true;
                        resource.error = true;
                        Report.error (resource.source_reference, "Only members of the current class are lockable");
                }
diff --git a/vala/valaunlockstatement.vala b/vala/valaunlockstatement.vala
new file mode 100644 (file)
index 0000000..e0ae1a0
--- /dev/null
@@ -0,0 +1,70 @@
+/* valaunlockstatement.vala
+ *
+ * Copyright (C) 2009  Jiří Zárevúcky, Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ *     Jiří Zárevúcky <zarevucky.jiri@gmail.com>
+ */
+
+/**
+ * Represents an unlock statement e.g. {{{ unlock (a); }}}.
+ */
+public class Vala.UnlockStatement : CodeNode, Statement {
+       /**
+        * Expression representing the resource to be unlocked.
+        */
+       public Expression resource { get; set; }
+
+       public UnlockStatement (Expression resource, SourceReference? source_reference = null) {
+               this.source_reference = source_reference;
+               this.resource = resource;
+       }
+
+       public override void accept (CodeVisitor visitor) {
+               resource.accept (visitor);
+               visitor.visit_unlock_statement (this);
+       }
+
+       public override bool check (SemanticAnalyzer analyzer) {
+               if (checked) {
+                       return !error;
+               }
+
+               checked = true;
+
+               resource.check (analyzer);
+
+               /* resource must be a member access and denote a Lockable */
+               if (!(resource is MemberAccess && resource.symbol_reference is Lockable)) {
+                       error = true;
+                       resource.error = true;
+                       Report.error (resource.source_reference, "Expression is either not a member access or does not denote a lockable member");
+                       return false;
+               }
+
+               /* parent symbol must be the current class */
+               if (resource.symbol_reference.parent_symbol != analyzer.current_class) {
+                       error = true;
+                       resource.error = true;
+                       Report.error (resource.source_reference, "Only members of the current class are lockable");
+               }
+
+               ((Lockable) resource.symbol_reference).set_lock_used (true);
+
+               return !error;
+       }
+}