]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
codegen: Fix custom reference-counting for compact classes f413cb32470d9c2598416772bc0166c6f974ec9f
authorSimon Werbeck <simon.werbeck@gmail.com>
Mon, 30 Sep 2019 14:37:33 +0000 (16:37 +0200)
committerRico Tzschichholz <ricotz@ubuntu.com>
Wed, 9 Oct 2019 06:10:03 +0000 (08:10 +0200)
This fix defers emitting G_DEFINE_AUTOPTR_CLEANUP_FUNC when a compact
class defines custom unref function and set by ccode "unref_function".

codegen/valagtypemodule.vala
tests/Makefile.am
tests/objects/compact-class-custom-ref.vala [new file with mode: 0644]

index 4eb7878a12c9dc530e9583392c1e8f41ef9d32ea..bae49b2c5729060e8f768836f03a95c14f81583e 100644 (file)
@@ -195,17 +195,21 @@ public class Vala.GTypeModule : GErrorModule {
                while (base_class.base_class != null) {
                        base_class = base_class.base_class;
                }
-               string autoptr_cleanup_func;
-               if (is_reference_counting (base_class)) {
-                       autoptr_cleanup_func = get_ccode_unref_function (base_class);
-               } else {
-                       autoptr_cleanup_func = get_ccode_free_function (base_class);
-               }
-               if (autoptr_cleanup_func == null || autoptr_cleanup_func == "") {
-                       Report.error (cl.source_reference, "internal error: autoptr_cleanup_func not available");
+               // Custom unref-methods need to be emitted before G_DEFINE_AUTOPTR_CLEANUP_FUNC,
+               // so we guard against that special case and handle it in generate_method_declaration.
+               if (!(base_class.is_compact && is_reference_counting (base_class))) {
+                       string autoptr_cleanup_func;
+                       if (is_reference_counting (base_class)) {
+                               autoptr_cleanup_func = get_ccode_unref_function (base_class);
+                       } else {
+                               autoptr_cleanup_func = get_ccode_free_function (base_class);
+                       }
+                       if (autoptr_cleanup_func == null || autoptr_cleanup_func == "") {
+                               Report.error (cl.source_reference, "internal error: autoptr_cleanup_func not available");
+                       }
+                       decl_space.add_type_member_declaration (new CCodeIdentifier ("G_DEFINE_AUTOPTR_CLEANUP_FUNC (%s, %s)".printf (get_ccode_name (cl), autoptr_cleanup_func)));
+                       decl_space.add_type_member_declaration (new CCodeNewline ());
                }
-               decl_space.add_type_member_declaration (new CCodeIdentifier ("G_DEFINE_AUTOPTR_CLEANUP_FUNC (%s, %s)".printf (get_ccode_name (cl), autoptr_cleanup_func)));
-               decl_space.add_type_member_declaration (new CCodeNewline ());
        }
 
        public override void generate_class_struct_declaration (Class cl, CCodeFile decl_space) {
@@ -441,6 +445,22 @@ public class Vala.GTypeModule : GErrorModule {
                }
        }
 
+       public override bool generate_method_declaration (Method m, CCodeFile decl_space) {
+               if (base.generate_method_declaration (m, decl_space)) {
+                       // Custom unref-methods need to be emitted before G_DEFINE_AUTOPTR_CLEANUP_FUNC,
+                       // in addition to the non-ref-countable case in generate_class_declaration.
+                       unowned Class? cl = m.parent_symbol as Class;
+                       if (cl != null && cl.is_compact && get_ccode_unref_function (cl) == get_ccode_name (m)) {
+                               decl_space.add_type_member_declaration (new CCodeIdentifier ("G_DEFINE_AUTOPTR_CLEANUP_FUNC (%s, %s)".printf (get_ccode_name (cl), get_ccode_name (m))));
+                               decl_space.add_type_member_declaration (new CCodeNewline ());
+                       }
+
+                       return true;
+               }
+
+               return false;
+       }
+
        public virtual void generate_virtual_method_declaration (Method m, CCodeFile decl_space, CCodeStruct type_struct) {
                if (!m.is_abstract && !m.is_virtual) {
                        return;
index 011fefa1a03a0ba7435382c87816458eb530eee6..ba8f2b0c65750eacd4f0b8f1dc91fe07131ac0d1 100644 (file)
@@ -305,6 +305,7 @@ TESTS = \
        objects/compact-class.vala \
        objects/compact-class-destructor.vala \
        objects/compact-class-refcount.vala \
+       objects/compact-class-custom-ref.vala \
        objects/constructor-abstract-public.test \
        objects/constructor-variadic.test \
        objects/constructors.vala \
diff --git a/tests/objects/compact-class-custom-ref.vala b/tests/objects/compact-class-custom-ref.vala
new file mode 100644 (file)
index 0000000..7830665
--- /dev/null
@@ -0,0 +1,29 @@
+[Compact]
+[CCode (ref_function = "foo_ref", unref_function = "foo_unref")]
+public class Foo {
+       [CCode (type = "volatile int")]
+       public int ref_count = 1;
+
+       public unowned Foo ref () {
+               AtomicInt.inc (ref ref_count);
+               return this;
+       }
+
+       public void unref () {
+               if (AtomicInt.dec_and_test (ref ref_count)) {
+                       free ();
+               }
+       }
+
+       [DestroysInstance]
+       extern void free ();
+}
+
+void main () {
+       var foo = new Foo ();
+       assert(foo.ref_count == 1);
+       foo.ref ();
+       assert(foo.ref_count == 2);
+       foo.unref ();
+       assert(foo.ref_count == 1);
+}