+2006-09-27 Raffaele Sandrini <rasa@gmx.ch>
+
+ * vapi/glib-2.0.vala: add threading and assorted structures
+ * vala/valamember.vala: add an additional abstraction for all members
+ of a class
+ * vala/valalockstatement.vala, vala/valacodegenerator.vala,
+ vala/scanner.l, vala/valasemanticanalyzer.vala,
+ vala/valalockable.vala, vala/parser.y, vala/valacodevisitor.vala:
+ add lock feature to vala
+ * vala/valamethod.vala, vala/valafield.vala, vala/valaconstant.vala,
+ vala/valasignal.vala, vala/valaproperty.vala: make those members
+ lockable
+ * vala/valacodegenerator.vala: use member abstraction to simlify field
+ initialisation and destruction
+ * vala/valacodegenerator.vala: introduce new C macros VALA_FREE_CHECKED
+ and VALA_FREE_UNCHECKED to make safe freeing code nicer
+ * vala/vala.h: update and bring arraycreationexpression in order
+ * vala/Makefile.am: update
+
2006-09-27 Jürg Billeter <j@bitron.ch>
* vapi/glib-2.0.vala: add Process struct with spawn functions
valalocalvariabledeclaration.c \
valalocalvariabledeclaration.h \
valalocalvariabledeclaration.vala \
+ valalockable.c \
+ valalockable.h \
+ valalockable.vala \
+ valalockstatement.c \
+ valalockstatement.h \
+ valalockstatement.vala \
+ valamember.c \
+ valamember.h \
+ valamember.vala \
valamemberaccess.c \
valamemberaccess.h \
valamemberaccess.vala \
valaliteral.h \
valaliteralexpression.h \
valalocalvariabledeclaration.h \
+ valalockable.h \
+ valamember.h \
+ valalockstatement.h \
valamemberaccess.h \
valamemberaccessibility.h \
valamemorymanager.h \
%token IN "in"
%token INTERFACE "interface"
%token IS "is"
+%token LOCK "lock"
%token NAMESPACE "namespace"
%token NEW "new"
%token VALA_NULL "null"
%type <statement> break_statement
%type <statement> continue_statement
%type <statement> return_statement
+%type <statement> lock_statement
%type <namespace> namespace_declaration
%type <str> opt_name_specifier
%type <str> name_specifier
| selection_statement
| iteration_statement
| jump_statement
+ | lock_statement
;
block
}
;
+lock_statement
+ : comment LOCK OPEN_PARENS expression CLOSE_PARENS embedded_statement
+ {
+ ValaSourceReference *src = src_com(@4, $1);
+ $$ = VALA_STATEMENT (vala_lock_statement_new ($4, $6, src));
+ g_object_unref (src);
+ g_object_unref ($4);
+ g_object_unref ($6);
+ }
+
namespace_declaration
: comment opt_attributes NAMESPACE IDENTIFIER
{
"virtual" { uploc; return VIRTUAL; }
"weak" { uploc; return WEAK; }
"while" { uploc; return WHILE; }
+"lock" { uploc; return LOCK; }
{real_literal} { uploc; yylval->str = g_strdup (yytext); return REAL_LITERAL; }
{integer_literal} { uploc; yylval->str = g_strdup (yytext); return INTEGER_LITERAL; }
+#include <vala/valaarraycreationexpression.h>
#include <vala/valaassignment.h>
#include <vala/valaattribute.h>
#include <vala/valabinaryexpression.h>
#include <vala/valaliteral.h>
#include <vala/valaliteralexpression.h>
#include <vala/valalocalvariabledeclaration.h>
+#include <vala/valalockstatement.h>
#include <vala/valamemberaccess.h>
#include <vala/valamethod.h>
#include <vala/valanamedargument.h>
#include <vala/valaunaryexpression.h>
#include <vala/valavariabledeclarator.h>
#include <vala/valawhilestatement.h>
-#include <vala/valaarraycreationexpression.h>
Symbol root_symbol;
Symbol current_symbol;
Symbol current_type_symbol;
+ Class current_class;
CCodeFragment header_begin;
CCodeFragment header_type_declaration;
CCodeFragment source_include_directives;
CCodeFragment source_type_member_declaration;
CCodeFragment source_type_member_definition;
+ CCodeFragment instance_init_fragment;
+ CCodeFragment instance_dispose_fragment;
CCodeStruct instance_struct;
CCodeStruct type_struct;
TypeReference double_type;
DataType list_type;
DataType slist_type;
+ TypeReference mutex_type;
public construct (bool manage_memory = true) {
memory_management = manage_memory;
list_type = (DataType) glib_ns.lookup ("List").node;
slist_type = (DataType) glib_ns.lookup ("SList").node;
+
+ mutex_type = new TypeReference ();
+ mutex_type.data_type = (DataType) glib_ns.lookup ("Mutex").node;
/* we're only interested in non-pkg source files */
var source_files = context.get_source_files ();
}
}
}
+
+ /* generate hardcoded "well-known" macros */
+ source_begin.append (new CCodeMacroReplacement ("VALA_FREE_CHECKED(o,f)", "((o) == NULL ? NULL : ((o) = (f (o), NULL)))"));
+ source_begin.append (new CCodeMacroReplacement ("VALA_FREE_UNCHECKED(o,f)", "((o) = (f (o), NULL))"));
}
private static ref string get_define_for_filename (string! filename) {
public override void visit_begin_class (Class! cl) {
current_symbol = cl.symbol;
current_type_symbol = cl.symbol;
+ current_class = cl;
instance_struct = new CCodeStruct ("_%s".printf (cl.get_cname ()));
type_struct = new CCodeStruct ("_%sClass".printf (cl.get_cname ()));
instance_priv_struct = new CCodeStruct ("_%sPrivate".printf (cl.get_cname ()));
prop_enum = new CCodeEnum ();
prop_enum.add_value ("%s_DUMMY_PROPERTY".printf (cl.get_upper_case_cname (null)), null);
+ instance_init_fragment = new CCodeFragment ();
+ instance_dispose_fragment = new CCodeFragment ();
header_type_declaration.append (new CCodeNewline ());
source_type_member_definition.append (type_fun);
current_type_symbol = null;
+ current_class = null;
}
private void add_class_init_function (Class! cl) {
init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), ccall)));
}
- var fields = cl.get_fields ();
- foreach (Field f in fields) {
- if (f.initializer != null) {
- ref CCodeExpression lhs = null;
- if (f.instance) {
- if (f.access == MemberAccessibility.PRIVATE) {
- lhs = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), f.get_cname ());
- } else {
- lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
- }
- } /* else {
- lhs = new CCodeIdentifier ("%s_%s".printf (cl.get_lower_case_cname (null), f.get_cname ()));
- } */
- if (lhs != null) {
- init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (lhs, (CCodeExpression) f.initializer.ccodenode)));
- }
- }
- }
+ init_block.add_statement (instance_init_fragment);
var init_sym = cl.symbol.lookup ("init");
if (init_sym != null) {
cblock.add_statement (cdecl);
-
- var fields = cl.get_fields ();
- foreach (Field f in fields) {
- if (f.instance && f.type_reference.takes_ownership) {
- var cself = new CCodeIdentifier ("self");
- CCodeExpression cstruct = cself;
- if (f.access == MemberAccessibility.PRIVATE) {
- cstruct = new CCodeMemberAccess.pointer (cself, "priv");
- }
- var cfield = new CCodeMemberAccess.pointer (cstruct, f.get_cname ());
-
- cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (cfield, f.type_reference)));
- }
- }
-
+ cblock.add_statement (instance_dispose_fragment);
cdecl = new CCodeDeclaration ("%sClass *".printf (cl.get_cname ()));
cdecl.add_declarator (new CCodeVariableDeclarator ("klass"));
source_type_member_declaration.append (ctypedef);
}
}
+
+ public override void visit_member (Member! m) {
+ /* stuff meant for all lockable members */
+ if (m is Lockable && ((Lockable)m).get_lock_used ()) {
+ instance_priv_struct.add_field (mutex_type.get_cname (), get_symbol_lock_name (m.symbol));
+
+ instance_init_fragment.append (
+ new CCodeExpressionStatement (
+ new CCodeAssignment (
+ new CCodeMemberAccess.pointer (
+ new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"),
+ get_symbol_lock_name (m.symbol)),
+ new CCodeFunctionCall (new CCodeIdentifier (((Struct)mutex_type.data_type).default_construction_method.get_cname ())))));
+
+ var fc = new CCodeFunctionCall (new CCodeIdentifier ("VALA_FREE_CHECKED"));
+ fc.add_argument (
+ new CCodeMemberAccess.pointer (
+ new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"),
+ get_symbol_lock_name (m.symbol)));
+ fc.add_argument (new CCodeIdentifier (mutex_type.data_type.get_free_function ()));
+ instance_dispose_fragment.append (new CCodeExpressionStatement (fc));
+ }
+ }
public override void visit_constant (Constant! c) {
if (c.symbol.parent_symbol.node is DataType) {
}
public override void visit_field (Field! f) {
+ CCodeExpression lhs = null;
+
if (f.access != MemberAccessibility.PRIVATE) {
instance_struct.add_field (f.type_reference.get_cname (), f.get_cname ());
+ if (f.instance) {
+ lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), f.get_cname ());
+ }
} else if (f.access == MemberAccessibility.PRIVATE) {
if (f.instance) {
instance_priv_struct.add_field (f.type_reference.get_cname (), f.get_cname ());
+ lhs = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), f.get_cname ());
} else {
if (f.symbol.parent_symbol.node is DataType) {
var t = (DataType) f.symbol.parent_symbol.node;
}
}
}
+
+ if (f.instance) {
+ if (f.initializer != null) {
+ instance_init_fragment.append (new CCodeExpressionStatement (new CCodeAssignment (lhs, (CCodeExpression) f.initializer.ccodenode)));
+ }
+
+ if (f.type_reference.takes_ownership) {
+ instance_dispose_fragment.append (new CCodeExpressionStatement (get_unref_expression (lhs, f.type_reference)));
+ }
+ }
}
public override void visit_begin_method (Method! m) {
}
}
+ private ref string get_symbol_lock_name (Symbol! sym) {
+ return "__lock_%s".printf (sym.name);
+ }
+
+ /**
+ * Visit operation called for lock statements.
+ *
+ * @param stmt a lock statement
+ */
+ public override void visit_lock_statement (LockStatement! stmt) {
+ var cn = new CCodeFragment ();
+ CCodeExpression l = null;
+ CCodeFunctionCall fc;
+ var inner_node = ((MemberAccess)stmt.resource).inner;
+
+ if (inner_node == null) {
+ l = new CCodeIdentifier ("self");
+ } else if (stmt.resource.symbol_reference.parent_symbol.node != current_class) {
+ l = new CCodeFunctionCall (new CCodeIdentifier (((DataType) stmt.resource.symbol_reference.parent_symbol.node).get_upper_case_cname ()));
+ ((CCodeFunctionCall) l).add_argument ((CCodeExpression)inner_node.ccodenode);
+ } else {
+ l = (CCodeExpression)inner_node.ccodenode;
+ }
+ l = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (l, "priv"), get_symbol_lock_name (stmt.resource.symbol_reference));
+
+ fc = new CCodeFunctionCall (new CCodeIdentifier (((Method)mutex_type.data_type.symbol.lookup ("lock").node).get_cname ()));
+ fc.add_argument (l);
+ cn.append (new CCodeExpressionStatement (fc));
+
+ cn.append (stmt.body.ccodenode);
+
+ fc = new CCodeFunctionCall (new CCodeIdentifier (((Method)mutex_type.data_type.symbol.lookup ("unlock").node).get_cname ()));
+ fc.add_argument (l);
+ cn.append (new CCodeExpressionStatement (fc));
+
+ stmt.ccodenode = cn;
+ }
+
/**
* Visit operations called for array creation expresions.
*
*/
public virtual void visit_end_callback (Callback! cb) {
}
+
+ /**
+ * Visit operation called for Members.
+ *
+ * @param m a member
+ */
+ public virtual void visit_member (Member! m) {
+ }
/**
* Visit operation called for constants.
}
/**
+ * Visit operation called for lock statements before the body has been visited.
+ *
+ * @param stmt a lock statement
+ */
+ public virtual void visit_lock_statement (LockStatement! stmt) {
+ }
+
+ /**
* Visit operation called at end of return statements.
*
* @param stmt a return statement
/**
* Represents a type member with a constant value.
*/
-public class Vala.Constant : CodeNode {
+public class Vala.Constant : Member, Lockable {
/**
* The symbol name of this constant.
*/
public MemberAccessibility access;
private string cname;
+
+ private bool lock_used = false;
/**
* Creates a new constant.
}
public override void accept (CodeVisitor! visitor) {
+ visitor.visit_member (this);
+
type_reference.accept (visitor);
if (initializer != null) {
initializer.accept (visitor);
}
-
+
visitor.visit_constant (this);
}
}
return cname;
}
+
+ public override bool get_lock_used () {
+ return lock_used;
+ }
+
+ public override void set_lock_used (bool used) {
+ lock_used = used;
+ }
}
/**
* Represents a type or namespace field.
*/
-public class Vala.Field : CodeNode, Invokable {
+public class Vala.Field : Member, Invokable, Lockable {
/**
* The symbol name of this field.
*/
private string cname;
private bool _instance = true;
+ private bool lock_used = false;
+
/**
* Creates a new field.
*
}
public override void accept (CodeVisitor! visitor) {
+ visitor.visit_member (this);
+
type_reference.accept (visitor);
if (initializer != null) {
public override bool is_invokable () {
return (type_reference.data_type is Callback);
}
+
+ public override bool get_lock_used () {
+ return lock_used;
+ }
+
+ public override void set_lock_used (bool used) {
+ lock_used = used;
+ }
}
--- /dev/null
+/* valalockable.vala
+ *
+ * Copyright (C) 2006 Raffaele Sandrini
+ *
+ * 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 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:
+ * Raffaele Sandrini <rasa@gmx.ch>
+ */
+
+/**
+ * Represents a lockable object.
+ */
+public interface Vala.Lockable {
+ /**
+ * Indicates a specific lockable object beeing actually locked somewhere.
+ */
+ public abstract bool get_lock_used ();
+
+ /**
+ * Set this lockable object as beeing locked somewhere.
+ */
+ public abstract void set_lock_used (bool used);
+}
--- /dev/null
+/* valalockstatement.vala
+ *
+ * Copyright (C) 2006 Raffaele Sandrini
+ *
+ * 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 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:
+ * Raffaele Sandrini <rasa@gmx.ch>
+ */
+
+using GLib;
+
+/**
+ * Represents a lock statement e.g. "lock (a) { f(a) }".
+ */
+public class Vala.LockStatement : Statement {
+ /**
+ * Expression representing the resource to be locked.
+ */
+ public Expression! resource { get; set construct; }
+
+ /**
+ * The statement during its execution the resource is locked.
+ */
+ public Statement! body { get; set construct; }
+
+ public construct (Expression _resource, Statement _body, SourceReference source) {
+ resource = _resource;
+ body = _body;
+ source_reference = source;
+ }
+
+ public override void accept (CodeVisitor! visitor) {
+ resource.accept (visitor);
+ body.accept (visitor);
+ visitor.visit_lock_statement (this);
+ }
+}
--- /dev/null
+/* valamember.vala
+ *
+ * Copyright (C) 2006 Raffaele Sandrini
+ *
+ * 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 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:
+ * Raffaele Sandrini <rasa@gmx.ch>
+ */
+
+/**
+ * Represents a general class member.
+ */
+public class Vala.Member : CodeNode {
+ public override void accept (CodeVisitor! visitor) {
+ visitor.visit_member (this);
+ }
+}
/**
* Represents a type or namespace method.
*/
-public class Vala.Method : CodeNode, Invokable {
+public class Vala.Method : Member, Invokable {
/**
* The symbol name of this method.
*/
/**
* Represents a property declaration in the source code.
*/
-public class Vala.Property : CodeNode {
+public class Vala.Property : Member, Lockable {
/**
* The property name.
*/
*/
public bool interface_only { get; set; }
+ private bool lock_used = false;
+
/**
* Creates a new property.
*
}
public override void accept (CodeVisitor! visitor) {
+ visitor.visit_member (this);
visitor.visit_begin_property (this);
type_reference.accept (visitor);
}
}
}
+
+ public override bool get_lock_used () {
+ return lock_used;
+ }
+
+ public override void set_lock_used (bool used) {
+ lock_used = used;
+ }
}
Symbol current_symbol;
SourceFile current_source_file;
TypeReference current_return_type;
+ Class current_class;
List<weak NamespaceReference> current_using_directives;
public override void visit_begin_class (Class! cl) {
current_symbol = cl.symbol;
+ current_class = cl;
if (cl.base_class != null) {
current_source_file.add_symbol_dependency (cl.base_class.symbol, SourceFileDependencyType.HEADER_FULL);
public override void visit_begin_struct (Struct! st) {
current_symbol = st.symbol;
+ current_class = null;
}
public override void visit_end_struct (Struct! st) {
current_symbol = current_symbol.parent_symbol;
}
+
+ public override void visit_constant (Constant! c) {
+ if (!current_source_file.pkg) {
+ if (c.initializer == null) {
+ c.error = true;
+ Report.error (c.source_reference, "A const field requires a initializer to be provided");
+ }
+ }
+ }
public override void visit_field (Field! f) {
if (f.access != MemberAccessibility.PRIVATE) {
}
}
+ /**
+ * Visit operation called for lock statements.
+ *
+ * @param stmt a lock statement
+ */
+ public override void visit_lock_statement (LockStatement! stmt) {
+ /* resource must be a member access and denote a Lockable */
+ if (!(stmt.resource is MemberAccess && stmt.resource.symbol_reference.node is Lockable)) {
+ stmt.error = true;
+ stmt.resource.error = true;
+ Report.error (stmt.resource.source_reference, "Expression is either not a member access or does not denote a lockable member");
+ return;
+ }
+
+ /* parent symbol must be the current class */
+ if (stmt.resource.symbol_reference.parent_symbol.node != current_class) {
+ stmt.error = true;
+ stmt.resource.error = true;
+ Report.error (stmt.resource.source_reference, "Only members of the current class are lockable");
+ }
+
+ ((Lockable)stmt.resource.symbol_reference.node).set_lock_used (true);
+ }
+
public override void visit_begin_array_creation_expression (ArrayCreationExpression! expr) {
if (expr.initializer_list != null) {
expr.initializer_list.expected_type = expr.element_type.copy ();
/**
* Represents an object signal. Signals enable objects to provide notifications.
*/
-public class Vala.Signal : CodeNode, Invokable {
+public class Vala.Signal : Member, Invokable, Lockable {
/**
* The symbol name of this signal.
*/
private Callback generated_callback;
private string cname;
+
+ private bool lock_used = false;
/**
* Creates a new signal.
}
public override void accept (CodeVisitor! visitor) {
+ visitor.visit_member (this);
+
visitor.visit_begin_signal (this);
return_type.accept (visitor);
}
}
}
+
+ public override bool get_lock_used () {
+ return lock_used;
+ }
+
+ public override void set_lock_used (bool used) {
+ lock_used = used;
+ }
}
/* glib-2.0.vala
*
- * Copyright (C) 2006 Jürg Billeter
+ * Copyright (C) 2006 Jürg Billeter, Raffaele Sandrini
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
*
* Author:
* Jürg Billeter <j@bitron.ch>
+ * Raffaele Sandrini <rasa@gmx.ch>
*/
[CCode (cname = "gboolean", cheader_filename = "glib.h", type_id = "G_TYPE_BOOLEAN", marshaller_type_name = "BOOLEAN")]
public callback bool SourceFunc (pointer data);
+ /* Thread support */
[ReferenceType ()]
public struct ThreadFunctions {
}
+ public callback pointer ThreadFunc (pointer data);
+ public callback void Func (pointer data, pointer user_data);
+
+ public enum ThreadPriority {
+ LOW,
+ NORMAL,
+ HIGH,
+ URGENT
+ }
+
[ReferenceType ()]
public struct Thread {
- public static void init (ThreadFunctions vtable);
+ public static void init (ThreadFunctions vtable = null);
public static bool supported ();
+ public static ref Thread create (ThreadFunc func, pointer data, bool joinable, out Error error);
+ public static ref Thread create_full (ThreadFunc func, pointer data, ulong stack_size, bool joinable, bool bound, ThreadPriority priority, out Error error);
+ public static ref Thread self ();
+ public pointer join ();
+ public void set_priority (ThreadPriority priority);
+ public static void yield ();
+ public static void exit (pointer retval);
+ public static void @foreach (Func thread_func, pointer user_data);
[CCode (cname = "g_usleep")]
public static void usleep (ulong microseconds);
}
+ [ReferenceType (free_function = "g_mutex_free")]
+ public struct Mutex {
+ public construct ();
+ public void @lock ();
+ public bool try_lock ();
+ public void unlock ();
+ }
+
+ [ReferenceType (free_function = "g_cond_free")]
+ public struct Cond {
+ public construct ();
+ public void @signal ();
+ public void broadcast ();
+ public void wait (Mutex mutex);
+ public bool timed_wait (Mutex mutex, TimeVal abs_time);
+ }
+
public static pointer malloc0 (ulong n_bytes);
[ReferenceType ()]
public static ref string decode (string! text, ref ulong out_len);
}
+ [ReferenceType (free_function = "g_free")]
public struct TimeVal {
+ [CCode (cname = "g_get_current_time")]
+ public void get_current_time ();
+ public void add (long microseconds);
+ [InstanceLast ()]
+ public bool from_iso8601 (string iso_date);
+ public string to_iso8601 ();
+
}
public struct Environment {