diff options
author | Rico Tzschichholz <ricotz@ubuntu.com> | 2018-07-03 12:34:07 +0200 |
---|---|---|
committer | Rico Tzschichholz <ricotz@ubuntu.com> | 2018-07-30 09:52:38 +0200 |
commit | b5394573db2a833a9c6bb2b0326b6413524bb76d (patch) | |
tree | a5c767bc5c2b1fcb7e9a8bf9b37623ea1f1359f3 | |
parent | a62bda136d404743305161326390f3db3880c9d5 (diff) | |
download | vala-wip/singleton.tar.gz |
Add support for singleton object classeswip/singleton
https://gitlab.gnome.org/GNOME/vala/issues/647
-rw-r--r-- | codegen/valagobjectmodule.vala | 73 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/objects/singleton.vala | 59 | ||||
-rw-r--r-- | tests/semantic/class-singleton-base.test | 11 | ||||
-rw-r--r-- | tests/semantic/class-singleton-non-gobject.test | 8 | ||||
-rw-r--r-- | vala/valaclass.vala | 34 | ||||
-rw-r--r-- | vala/valausedattr.vala | 1 | ||||
-rw-r--r-- | valadoc/treebuilder.vala | 1 |
8 files changed, 190 insertions, 0 deletions
diff --git a/codegen/valagobjectmodule.vala b/codegen/valagobjectmodule.vala index 11aa6e686..5ba6726c5 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 e9574e09b..4bee56b74 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -245,6 +245,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 \ @@ -488,6 +489,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..d0e96be28 --- /dev/null +++ b/tests/objects/singleton.vala @@ -0,0 +1,59 @@ +[Singleton] +public class Foo : Object { + public int bar = 42; + construct { + } +} + +[Singleton] +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..74a09b03c --- /dev/null +++ b/tests/semantic/class-singleton-base.test @@ -0,0 +1,11 @@ +Invalid Code + +[Singleton] +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..031fed23d --- /dev/null +++ b/tests/semantic/class-singleton-non-gobject.test @@ -0,0 +1,8 @@ +Invalid Code + +[Singleton] +public class Foo { +} + +void main () { +} diff --git a/vala/valaclass.vala b/vala/valaclass.vala index 0a44e9789..129b346f9 100644 --- a/vala/valaclass.vala +++ b/vala/valaclass.vala @@ -80,6 +80,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 ("Singleton") != null; + } + return _is_singleton; + } + set { + _is_singleton = value; + set_attribute ("Singleton", value); + } + } + + /** * Specifies whether this class has private fields. */ public bool has_private_fields { get; set; } @@ -91,6 +107,7 @@ public class Vala.Class : ObjectTypeSymbol { private bool? _is_compact; private bool? _is_immutable; + private bool? _is_singleton; private List<DataType> base_types = new ArrayList<DataType> (); @@ -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 Singleton 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, "Singleton 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..9361a2678 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", "", + "Singleton", "", "Compact", "", "NoWrapper", "", "NoThrow", "", diff --git a/valadoc/treebuilder.vala b/valadoc/treebuilder.vala index 0b81481c0..1c7051b4a 100644 --- a/valadoc/treebuilder.vala +++ b/valadoc/treebuilder.vala @@ -230,6 +230,7 @@ public class Valadoc.Drivers.TreeBuilder : Vala.CodeVisitor { "HasEmitter", "ModuleInit", "NoWrapper", + "Singleton", "Immutable", "ErrorBase", "NoReturn", |