From: Rico Tzschichholz Date: Tue, 3 Jul 2018 10:34:07 +0000 (+0200) Subject: Add support for SingleInstance attribute for GObject classes X-Git-Tag: 0.43.1~192 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dc7202d69d9945a31e0b89caf6a5731aef60fabc;p=thirdparty%2Fvala.git Add support for SingleInstance attribute for GObject classes Fixes https://gitlab.gnome.org/GNOME/vala/issues/647 --- diff --git a/codegen/valagobjectmodule.vala b/codegen/valagobjectmodule.vala index ad808a138..837046742 100644 --- a/codegen/valagobjectmodule.vala +++ b/codegen/valagobjectmodule.vala @@ -454,6 +454,63 @@ public class Vala.GObjectModule : GTypeModule { ccode.add_declaration ("GObject *", new CCodeVariableDeclarator ("obj")); ccode.add_declaration ("GObjectClass *", new CCodeVariableDeclarator ("parent_class")); + if (cl.is_singleton) { + var singleton_ref_name = "%s_singleton__ref".printf (get_ccode_name (cl)); + var singleton_lock_name = "%s_singleton__lock".printf (get_ccode_name (cl)); + var singleton_once_name = "%s_singleton__volatile".printf (get_ccode_name (cl)); + + var singleton_ref = new CCodeDeclaration("GObject *"); + singleton_ref.add_declarator (new CCodeVariableDeclarator (singleton_ref_name, new CCodeConstant ("NULL"))); + singleton_ref.modifiers = CCodeModifiers.STATIC; + ccode.add_statement (singleton_ref); + + var mutex_lock = new CCodeDeclaration("GMutex"); + mutex_lock.add_declarator (new CCodeVariableDeclarator (singleton_lock_name)); + mutex_lock.modifiers = CCodeModifiers.STATIC; + ccode.add_statement (mutex_lock); + + var once_lock = new CCodeDeclaration("gsize"); + once_lock.add_declarator (new CCodeVariableDeclarator (singleton_once_name, new CCodeConstant ("0"))); + once_lock.modifiers = CCodeModifiers.STATIC | CCodeModifiers.VOLATILE; + ccode.add_statement (once_lock); + + var once_init = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_enter")); + once_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (singleton_once_name))); + + var once_block = new CCodeBlock(); + + var singleton_mutex_init = new CCodeFunctionCall (new CCodeIdentifier ("g_mutex_init")); + singleton_mutex_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (singleton_lock_name))); + once_block.add_statement (new CCodeExpressionStatement (singleton_mutex_init)); + + var once_leave = new CCodeFunctionCall (new CCodeIdentifier ("g_once_init_leave")); + once_leave.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (singleton_once_name))); + once_leave.add_argument (new CCodeConstant ("42")); + once_block.add_statement (new CCodeExpressionStatement (once_leave)); + + var if_once = new CCodeIfStatement (once_init, once_block); + ccode.add_statement (if_once); + + var singleton_mutex_lock = new CCodeFunctionCall (new CCodeIdentifier ("g_mutex_lock")); + singleton_mutex_lock.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (singleton_lock_name))); + ccode.add_statement (new CCodeExpressionStatement (singleton_mutex_lock)); + + var check_existance = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier (singleton_ref_name), new CCodeConstant ("NULL")); + var return_singleton = new CCodeBlock(); + + var ref_object = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref")); + ref_object.add_argument (new CCodeIdentifier (singleton_ref_name)); + return_singleton.add_statement (new CCodeExpressionStatement (ref_object)); + + var singleton_mutex_unlock = new CCodeFunctionCall (new CCodeIdentifier ("g_mutex_unlock")); + singleton_mutex_unlock.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (singleton_lock_name))); + return_singleton.add_statement (new CCodeExpressionStatement (singleton_mutex_unlock)); + return_singleton.add_statement (new CCodeReturnStatement (new CCodeIdentifier (singleton_ref_name))); + + var if_singleton_alive = new CCodeIfStatement (check_existance, return_singleton); + ccode.add_statement (if_singleton_alive); + } + var ccast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_CLASS")); ccast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (get_ccode_lower_case_name (cl, null)))); ccode.add_assignment (new CCodeIdentifier ("parent_class"), ccast); @@ -479,6 +536,22 @@ public class Vala.GObjectModule : GTypeModule { ccode.add_declaration ("GError *", new CCodeVariableDeclarator.zero ("_inner_error_", new CCodeConstant ("NULL"))); } + if (cl.is_singleton) { + var singleton_ref_name = "%s_singleton__ref".printf (get_ccode_name (cl)); + var singleton_lock_name = "%s_singleton__lock".printf (get_ccode_name (cl)); + + ccode.add_assignment (new CCodeIdentifier (singleton_ref_name), new CCodeIdentifier ("obj")); + + var set_weak_ref_to_volatile = new CCodeFunctionCall (new CCodeIdentifier ("g_object_add_weak_pointer")); + set_weak_ref_to_volatile.add_argument (new CCodeIdentifier (singleton_ref_name)); + set_weak_ref_to_volatile.add_argument (new CCodeCastExpression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (singleton_ref_name)), "gpointer")); + ccode.add_statement (new CCodeExpressionStatement (set_weak_ref_to_volatile)); + + var final_singleton_mutex_unlock = new CCodeFunctionCall (new CCodeIdentifier ("g_mutex_unlock")); + final_singleton_mutex_unlock.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (singleton_lock_name))); + ccode.add_statement (new CCodeExpressionStatement (final_singleton_mutex_unlock)); + } + ccode.add_return (new CCodeIdentifier ("obj")); pop_function (); diff --git a/tests/Makefile.am b/tests/Makefile.am index d18208a13..59cf5685c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -256,6 +256,7 @@ TESTS = \ objects/regex.vala \ objects/signals.vala \ objects/signals-delegate.vala \ + objects/singleton.vala \ objects/test-025.vala \ objects/test-026.vala \ objects/test-029.vala \ @@ -508,6 +509,8 @@ TESTS = \ semantic/class-missing-implement-method.test \ semantic/class-missing-implement-property.test \ semantic/class-missing-prerequisites.test \ + semantic/class-singleton-base.test \ + semantic/class-singleton-non-gobject.test \ semantic/class-too-few-type-arguments.test \ semantic/class-too-many-type-arguments.test \ semantic/constant-extern.test \ diff --git a/tests/objects/singleton.vala b/tests/objects/singleton.vala new file mode 100644 index 000000000..6a54b5c22 --- /dev/null +++ b/tests/objects/singleton.vala @@ -0,0 +1,59 @@ +[SingleInstance] +public class Foo : Object { + public int bar = 42; + construct { + } +} + +[SingleInstance] +public class Bar : Object { + public int foo = 42; +} + +void lifetime_1 () { + Foo a = new Foo (); + Foo b = (Foo) Object.new (typeof (Foo)); + + assert (a == b); + assert (a.bar == 23); +} + +void lifetime_2 () { + Foo a = new Foo (); + Foo b = (Foo) Object.new (typeof (Foo)); + + assert (a == b); + assert (a.bar == 42); +} + +void lifetime_3 () { + Bar a = new Bar (); + Bar b = (Bar) Object.new (typeof (Bar)); + + assert (a == b); + assert (a.foo == 23); +} + +void main () { + { + // create singleton instance here + // which lives as long until it runs out of scope + Foo singleton = new Foo (); + singleton.bar = 23; + lifetime_1 (); + } + + { + // create new singleton instance here + Foo singleton = new Foo (); + assert (singleton.bar == 42); + lifetime_2 (); + } + + { + // create singleton instance here + Bar singleton = new Bar (); + singleton.foo = 23; + lifetime_3 (); + } +} diff --git a/tests/semantic/class-singleton-base.test b/tests/semantic/class-singleton-base.test new file mode 100644 index 000000000..7b894035d --- /dev/null +++ b/tests/semantic/class-singleton-base.test @@ -0,0 +1,11 @@ +Invalid Code + +[SingleInstance] +public class Foo : Object { +} + +public class Bar : Foo { +} + +void main () { +} diff --git a/tests/semantic/class-singleton-non-gobject.test b/tests/semantic/class-singleton-non-gobject.test new file mode 100644 index 000000000..89dc9fb02 --- /dev/null +++ b/tests/semantic/class-singleton-non-gobject.test @@ -0,0 +1,8 @@ +Invalid Code + +[SingleInstance] +public class Foo { +} + +void main () { +} diff --git a/vala/valaclass.vala b/vala/valaclass.vala index 2167e4e4b..4f5aa4b87 100644 --- a/vala/valaclass.vala +++ b/vala/valaclass.vala @@ -79,6 +79,22 @@ public class Vala.Class : ObjectTypeSymbol { } } + /** + * Instances of immutable classes are immutable after construction. + */ + public bool is_singleton { + get { + if (_is_singleton == null) { + _is_singleton = get_attribute ("SingleInstance") != null; + } + return _is_singleton; + } + set { + _is_singleton = value; + set_attribute ("SingleInstance", value); + } + } + /** * Specifies whether this class has private fields. */ @@ -91,6 +107,7 @@ public class Vala.Class : ObjectTypeSymbol { private bool? _is_compact; private bool? _is_immutable; + private bool? _is_singleton; private List base_types = new ArrayList (); @@ -495,6 +512,23 @@ public class Vala.Class : ObjectTypeSymbol { p.check (context); } + if (base_class != null && base_class.is_singleton) { + error = true; + Report.error (source_reference, "`%s' cannot inherit from SingleInstance class `%s'".printf (get_full_name (), base_class.get_full_name ())); + } + + if (is_singleton && !is_subtype_of (context.analyzer.object_type)) { + error = true; + Report.error (source_reference, "SingleInstance class `%s' requires inheritance from `GLib.Object'".printf (get_full_name ())); + } + + /* singleton classes require an instance construtor */ + if (is_singleton && constructor == null) { + var c = new Constructor (source_reference); + c.body = new Block (source_reference); + add_constructor (c); + } + /* process enums first to avoid order problems in C code */ foreach (Enum en in get_enums ()) { en.check (context); diff --git a/vala/valausedattr.vala b/vala/valausedattr.vala index dab15a864..28047daf3 100644 --- a/vala/valausedattr.vala +++ b/vala/valausedattr.vala @@ -43,6 +43,7 @@ public class Vala.UsedAttr : CodeVisitor { "use_inplace", "feature_test_macro", "default_value_on_error", "", "Immutable", "", + "SingleInstance", "", "Compact", "", "NoWrapper", "", "NoThrow", "", diff --git a/valadoc/treebuilder.vala b/valadoc/treebuilder.vala index 5c9a8d72a..5c76aafcc 100644 --- a/valadoc/treebuilder.vala +++ b/valadoc/treebuilder.vala @@ -219,6 +219,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor { "DestroysInstance", "GenericAccessors", "NoAccessorMethod", + "SingleInstance", "NoArrayLength", "Experimental", "Diagnostics",