[vala/wip/singleton: 90/90] Add support for SingleInstance attribute for GObject classes
- From: Rico Tzschichholz <ricotz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala/wip/singleton: 90/90] Add support for SingleInstance attribute for GObject classes
- Date: Wed, 17 Oct 2018 13:22:19 +0000 (UTC)
commit dc7202d69d9945a31e0b89caf6a5731aef60fabc
Author: Rico Tzschichholz <ricotz ubuntu com>
Date: Tue Jul 3 12:34:07 2018 +0200
Add support for SingleInstance attribute for GObject classes
Fixes https://gitlab.gnome.org/GNOME/vala/issues/647
codegen/valagobjectmodule.vala | 73 +++++++++++++++++++++++++
tests/Makefile.am | 3 +
tests/objects/singleton.vala | 59 ++++++++++++++++++++
tests/semantic/class-singleton-base.test | 11 ++++
tests/semantic/class-singleton-non-gobject.test | 8 +++
vala/valaclass.vala | 34 ++++++++++++
vala/valausedattr.vala | 1 +
valadoc/treebuilder.vala | 1 +
8 files changed, 190 insertions(+)
---
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<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 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",
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]