From: Daniel Espinosa Date: Mon, 15 Jan 2018 13:28:43 +0000 (+0100) Subject: codegen: Add support for abstract/virtual methods and properties in compact classes X-Git-Tag: 0.39.5~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=28b4f45b709622e821e86655f245fdcb75d3afaf;p=thirdparty%2Fvala.git codegen: Add support for abstract/virtual methods and properties in compact classes Reworked and extened by Rico Tzschichholz https://bugzilla.gnome.org/show_bug.cgi?id=741465 --- diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index a65a3f4f5..56e12c65d 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -1651,17 +1651,21 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { } } - CCodeFunctionCall vcast = null; + CCodeExpression vcast; if (prop.parent_symbol is Interface) { var iface = (Interface) prop.parent_symbol; vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (get_ccode_upper_case_name (iface, null)))); + ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self")); } else { var cl = (Class) prop.parent_symbol; - - vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl, null)))); + if (!cl.is_compact) { + vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl, null)))); + ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self")); + } else { + vcast = new CCodeIdentifier ("self"); + } } - vcast.add_argument (new CCodeIdentifier ("self")); if (acc.readable) { var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name))); diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala index 007bcbdee..bfcc21582 100644 --- a/codegen/valaccodemethodcallmodule.vala +++ b/codegen/valaccodemethodcallmodule.vala @@ -871,7 +871,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { } } - if (m is CreationMethod && m.parent_symbol is Class && current_class.base_class == gsource_type) { + if (m is CreationMethod && m.parent_symbol is Class && ((current_class.is_compact && current_class.base_class != null) || current_class.base_class == gsource_type)) { var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (current_class, null)))); cinitcall.add_argument (get_this_cexpression ()); ccode.add_expression (cinitcall); diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala index 4efd04b72..33c5ee485 100644 --- a/codegen/valaccodemethodmodule.vala +++ b/codegen/valaccodemethodmodule.vala @@ -655,7 +655,6 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { } if (cl.base_class == null) { - // derived compact classes do not have fields var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (cl, null)))); cinitcall.add_argument (get_this_cexpression ()); ccode.add_expression (cinitcall); @@ -815,17 +814,19 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { // complain during compile time of such en error. // add critical warning that this method should not have been called - var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE")); - type_from_instance_call.add_argument (new CCodeIdentifier ("self")); - - var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name")); - type_name_call.add_argument (type_from_instance_call); + var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical")); + if (!((Class) current_type_symbol).is_compact) { + var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE")); + type_from_instance_call.add_argument (new CCodeIdentifier ("self")); - var error_string = "\"Type `%%s' does not implement abstract method `%s'\"".printf (get_ccode_name (m)); + var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name")); + type_name_call.add_argument (type_from_instance_call); - var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical")); - cerrorcall.add_argument (new CCodeConstant (error_string)); - cerrorcall.add_argument (type_name_call); + cerrorcall.add_argument (new CCodeConstant ("\"Type `%%s' does not implement abstract method `%s'\"".printf (get_ccode_name (m)))); + cerrorcall.add_argument (type_name_call); + } else { + cerrorcall.add_argument (new CCodeConstant ("\"Abstract method `%s' is not implemented\"".printf (get_ccode_name (m)))); + } ccode.add_expression (cerrorcall); @@ -1087,17 +1088,21 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { } var vfunc = new CCodeFunction (cname + suffix); - CCodeFunctionCall vcast = null; + CCodeExpression vcast; if (m.parent_symbol is Interface) { var iface = (Interface) m.parent_symbol; vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (get_ccode_upper_case_name (iface)))); + ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self")); } else { var cl = (Class) m.parent_symbol; - - vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl)))); + if (!cl.is_compact) { + vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (get_ccode_upper_case_name (cl)))); + ((CCodeFunctionCall) vcast).add_argument (new CCodeIdentifier ("self")); + } else { + vcast = new CCodeIdentifier ("self"); + } } - vcast.add_argument (new CCodeIdentifier ("self")); cname = get_ccode_vfunc_name (m); if (suffix == "_finish" && cname.has_suffix ("_async")) { diff --git a/codegen/valagtypemodule.vala b/codegen/valagtypemodule.vala index d34219826..688523cf7 100644 --- a/codegen/valagtypemodule.vala +++ b/codegen/valagtypemodule.vala @@ -225,11 +225,6 @@ public class Vala.GTypeModule : GErrorModule { instance_struct.add_field ("volatile int", "ref_count"); } - if (cl.is_compact && cl.base_class == null && cl.get_fields ().size == 0) { - // add dummy member, C doesn't allow empty structs - instance_struct.add_field ("int", "dummy"); - } - if (is_gtypeinstance) { decl_space.add_type_declaration (new CCodeTypeDefinition ("struct %sPrivate".printf (instance_struct.name), new CCodeVariableDeclarator ("%sPrivate".printf (get_ccode_name (cl))))); @@ -245,8 +240,14 @@ public class Vala.GTypeModule : GErrorModule { } } + bool has_struct_member = false; foreach (Method m in cl.get_methods ()) { - generate_virtual_method_declaration (m, decl_space, type_struct); + if (!cl.is_compact) { + generate_virtual_method_declaration (m, decl_space, type_struct); + } else if (cl.is_compact && cl.base_class == null) { + generate_virtual_method_declaration (m, decl_space, instance_struct); + has_struct_member |= (m.is_abstract || m.is_virtual); + } } foreach (Signal sig in cl.get_signals ()) { @@ -290,6 +291,11 @@ public class Vala.GTypeModule : GErrorModule { var vdecl = new CCodeDeclaration (creturn_type); vdecl.add_declarator (vdeclarator); type_struct.add_declaration (vdecl); + + if (cl.is_compact && cl.base_class == null) { + instance_struct.add_declaration (vdecl); + has_struct_member = true; + } } if (prop.set_accessor != null) { CCodeParameter cvalueparam; @@ -315,6 +321,11 @@ public class Vala.GTypeModule : GErrorModule { var vdecl = new CCodeDeclaration ("void"); vdecl.add_declarator (vdeclarator); type_struct.add_declaration (vdecl); + + if (cl.is_compact && cl.base_class == null) { + instance_struct.add_declaration (vdecl); + has_struct_member = true; + } } } @@ -325,6 +336,7 @@ public class Vala.GTypeModule : GErrorModule { generate_type_declaration (f.variable_type, decl_space); instance_struct.add_field (get_ccode_name (f.variable_type), get_ccode_name (f), modifiers, get_ccode_declarator_suffix (f.variable_type)); + has_struct_member = true; if (f.variable_type is ArrayType && get_ccode_array_length (f)) { // create fields to store array dimensions var array_type = (ArrayType) f.variable_type; @@ -362,6 +374,11 @@ public class Vala.GTypeModule : GErrorModule { } } + if (cl.is_compact && cl.base_class == null && !has_struct_member) { + // add dummy member, C doesn't allow empty structs + instance_struct.add_field ("int", "dummy"); + } + if (!cl.is_compact || cl.base_class == null || is_gsource) { // derived compact classes do not have a struct decl_space.add_type_definition (instance_struct); @@ -592,7 +609,7 @@ public class Vala.GTypeModule : GErrorModule { begin_class_finalize_function (cl); begin_finalize_function (cl); } else { - if (cl.base_class == null || cl.base_class == gsource_type) { + if (cl.is_compact || cl.base_class == null || cl.base_class == gsource_type) { begin_instance_init_function (cl); begin_finalize_function (cl); } @@ -735,8 +752,7 @@ public class Vala.GTypeModule : GErrorModule { cfile.add_function (unref_fun); } } else { - if (cl.base_class == null || cl.base_class == gsource_type) { - // derived compact classes do not have fields + if (cl.is_compact || cl.base_class == null || cl.base_class == gsource_type) { add_instance_init_function (cl); add_finalize_function (cl); } @@ -1568,6 +1584,49 @@ public class Vala.GTypeModule : GErrorModule { // Add declaration, since the instance_init function is explicitly called // by the creation methods cfile.add_function_declaration (func); + + // connect overridden methods + foreach (Method m in cl.get_methods ()) { + if (m.base_method == null) { + continue; + } + var base_type = (ObjectTypeSymbol) m.base_method.parent_symbol; + + // there is currently no default handler for abstract async methods + if (!m.is_abstract || !m.coroutine) { + CCodeExpression cfunc = new CCodeIdentifier (get_ccode_real_name (m)); + cfunc = cast_method_pointer (m.base_method, cfunc, base_type, (m.coroutine ? 1 : 3)); + var ccast = new CCodeCastExpression (new CCodeIdentifier ("self"), "%s *".printf (get_ccode_name (base_type))); + func.add_assignment (new CCodeMemberAccess.pointer (ccast, get_ccode_vfunc_name (m.base_method)), cfunc); + + if (m.coroutine) { + cfunc = new CCodeIdentifier (get_ccode_finish_real_name (m)); + cfunc = cast_method_pointer (m.base_method, cfunc, base_type, 2); + ccode.add_assignment (new CCodeMemberAccess.pointer (ccast, get_ccode_finish_vfunc_name (m.base_method)), cfunc); + } + } + } + + // connect overridden properties + foreach (Property prop in cl.get_properties ()) { + if (prop.base_property == null) { + continue; + } + var base_type = prop.base_property.parent_symbol; + + var ccast = new CCodeCastExpression (new CCodeIdentifier ("self"), "%s *".printf (get_ccode_name (base_type))); + + if (!get_ccode_no_accessor_method (prop.base_property) && !get_ccode_concrete_accessor (prop.base_property)) { + if (prop.get_accessor != null) { + string cname = get_ccode_real_name (prop.get_accessor); + ccode.add_assignment (new CCodeMemberAccess.pointer (ccast, "get_%s".printf (prop.name)), new CCodeIdentifier (cname)); + } + if (prop.set_accessor != null) { + string cname = get_ccode_real_name (prop.set_accessor); + ccode.add_assignment (new CCodeMemberAccess.pointer (ccast, "set_%s".printf (prop.name)), new CCodeIdentifier (cname)); + } + } + } } if (!cl.is_compact && (cl.has_private_fields || cl.get_type_parameters ().size > 0)) { @@ -2283,6 +2342,11 @@ public class Vala.GTypeModule : GErrorModule { base_prop = prop.base_interface_property; } + if (cl != null && cl.is_compact && (prop.get_accessor == null || prop.get_accessor.automatic_body)) { + Report.error (prop.source_reference, "Properties without accessor bodies are not supported in compact classes"); + return; + } + if (base_prop.get_attribute ("NoAccessorMethod") == null && prop.name == "type" && ((cl != null && !cl.is_compact) || (st != null && get_ccode_has_type_id (st)))) { Report.error (prop.source_reference, "Property 'type' not allowed"); diff --git a/tests/Makefile.am b/tests/Makefile.am index 5f41f1cc5..e7057642a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -243,6 +243,7 @@ TESTS = \ objects/bug702736.vala \ objects/bug702846.vala \ objects/bug731547.vala \ + objects/bug741465.vala \ objects/bug751338.vala \ objects/bug758816.vala \ objects/bug760031.test \ diff --git a/tests/objects/bug741465.vala b/tests/objects/bug741465.vala new file mode 100644 index 000000000..e420aecf3 --- /dev/null +++ b/tests/objects/bug741465.vala @@ -0,0 +1,86 @@ +[Compact] +abstract class AbstractFoo { + public int field = 23; + public abstract int prop { get; set; } + public abstract unowned string foo (); +} + +[Compact] +class Foo : AbstractFoo { + public override int prop { + get { return field + 1; } + set { field = value - 1; } + } + + public Foo () { + assert (field == 23); + field = 37; + assert (prop == 38); + } + + public override unowned string foo () { + return "Foo"; + } +} + +[Compact] +class Bar : Foo { + public override int prop { + get { return field * 2; } + set { field = value / 2; } + } + + public Bar () { + assert (field == 37); + field = 42; + assert (prop == 84); + } + + public override unowned string foo () { + return "Bar"; + } +} + +[Compact] +class Manam { + public int field = 23; + + public virtual int prop { + get { return field + 1; } + set { field = value - 1; } + } + + public virtual unowned string foo () { + return "Manam"; + } +} + +[Compact] +class Baz : Manam { + public override unowned string foo () { + return "Baz"; + } +} + +void main () { + var foo = new Foo (); + assert (foo.foo () == "Foo"); + assert (foo.prop == 38); + foo.prop = 4711; + assert (foo.field == 4710); + + var bar = new Bar (); + assert (bar.foo () == "Bar"); + assert (bar.prop == 84); + bar.prop = 32; + assert (bar.field == 16); + + var manam = new Manam (); + assert (manam.foo () == "Manam"); + assert (manam.prop == 24); + + var baz = new Baz (); + assert (baz.foo () == "Baz"); + baz.prop = 42; + assert (baz.prop == 42); +} diff --git a/vala/valamethod.vala b/vala/valamethod.vala index 945ec5dba..b0a742a3c 100644 --- a/vala/valamethod.vala +++ b/vala/valamethod.vala @@ -638,6 +638,15 @@ public class Vala.Method : Subroutine, Callable { get_error_types ().clear (); } + if (parent_symbol is Class && (is_abstract || is_virtual)) { + var cl = (Class) parent_symbol; + if (cl.is_compact && cl.base_class != null) { + error = true; + Report.error (source_reference, "Abstract and virtual methods may not be declared in derived compact classes"); + return false; + } + } + if (is_abstract) { if (parent_symbol is Class) { var cl = (Class) parent_symbol; @@ -657,14 +666,6 @@ public class Vala.Method : Subroutine, Callable { Report.error (source_reference, "Virtual methods may not be declared outside of classes and interfaces"); return false; } - - if (parent_symbol is Class) { - var cl = (Class) parent_symbol; - if (cl.is_compact && cl != context.analyzer.gsource_type) { - Report.error (source_reference, "Virtual methods may not be declared in compact classes"); - return false; - } - } } else if (overrides) { if (!(parent_symbol is Class)) { error = true; diff --git a/vala/valaproperty.vala b/vala/valaproperty.vala index 0968600dd..e0a8e4390 100644 --- a/vala/valaproperty.vala +++ b/vala/valaproperty.vala @@ -382,6 +382,15 @@ public class Vala.Property : Symbol, Lockable { checked = true; + if (parent_symbol is Class && (is_abstract || is_virtual)) { + var cl = (Class) parent_symbol; + if (cl.is_compact && cl.base_class != null) { + error = true; + Report.error (source_reference, "Abstract and virtual properties may not be declared in derived compact classes"); + return false; + } + } + if (is_abstract) { if (parent_symbol is Class) { var cl = (Class) parent_symbol; @@ -401,15 +410,6 @@ public class Vala.Property : Symbol, Lockable { Report.error (source_reference, "Virtual properties may not be declared outside of classes and interfaces"); return false; } - - if (parent_symbol is Class) { - var cl = (Class) parent_symbol; - if (cl.is_compact) { - error = true; - Report.error (source_reference, "Virtual properties may not be declared in compact classes"); - return false; - } - } } else if (overrides) { if (!(parent_symbol is Class)) { error = true;