Commit 28b4f45b authored by Daniel Espinosa Ortiz's avatar Daniel Espinosa Ortiz Committed by Rico Tzschichholz

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
parent 1a5cb4b0
......@@ -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)));
......
......@@ -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);
......
......@@ -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")) {
......
......@@ -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");
......
......@@ -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 \
......
[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);
}
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment