summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRico Tzschichholz <ricotz@ubuntu.com>2018-07-03 12:34:07 +0200
committerRico Tzschichholz <ricotz@ubuntu.com>2018-07-30 09:52:38 +0200
commitb5394573db2a833a9c6bb2b0326b6413524bb76d (patch)
treea5c767bc5c2b1fcb7e9a8bf9b37623ea1f1359f3
parenta62bda136d404743305161326390f3db3880c9d5 (diff)
downloadvala-wip/singleton.tar.gz
Add support for singleton object classeswip/singleton
https://gitlab.gnome.org/GNOME/vala/issues/647
-rw-r--r--codegen/valagobjectmodule.vala73
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/objects/singleton.vala59
-rw-r--r--tests/semantic/class-singleton-base.test11
-rw-r--r--tests/semantic/class-singleton-non-gobject.test8
-rw-r--r--vala/valaclass.vala34
-rw-r--r--vala/valausedattr.vala1
-rw-r--r--valadoc/treebuilder.vala1
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",