From f413cb32470d9c2598416772bc0166c6f974ec9f Mon Sep 17 00:00:00 2001 From: Simon Werbeck Date: Mon, 30 Sep 2019 16:37:33 +0200 Subject: [PATCH] codegen: Fix custom reference-counting for compact classes 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 | 40 +++++++++++++++------ tests/Makefile.am | 1 + tests/objects/compact-class-custom-ref.vala | 29 +++++++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 tests/objects/compact-class-custom-ref.vala diff --git a/codegen/valagtypemodule.vala b/codegen/valagtypemodule.vala index 4eb7878a1..bae49b2c5 100644 --- a/codegen/valagtypemodule.vala +++ b/codegen/valagtypemodule.vala @@ -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; diff --git a/tests/Makefile.am b/tests/Makefile.am index 011fefa1a..ba8f2b0c6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 000000000..7830665e1 --- /dev/null +++ b/tests/objects/compact-class-custom-ref.vala @@ -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); +} -- 2.47.2