glib: install interfaces earlier, override properties in class_init

GObject upstream has started disabling support for installing interfaces
in GTypes after they have already been initialized (class_init) [1], so
we need to add GInterfaces a bit earlier (*before* class_init starts).

As GLib.Object.OverrideProperty() cannot to be called before class_init
(because it receives a GObjectClass, not a GType) or after (because
otherwise class_init would complain about properties of an interface not
being defined), then we need to call it during class_init.

[1] http://bugzilla.gnome.org/687659

A good side-effect of this fix is that we no longer use the hacky uint
field 'idx' to track the properties count for each class; now it gets
moved to the ClassInitializer class, and thus can be non-static, which
makes a bit more sense (we leave the old OverrideProperty overload for
backwards compatibility).

Simplest way to test this is launching the sample/treemodeldemo.exe and
sample/custom-scrollable.exe in Ubuntu 13.04 beta (which has GLib 2.36).
Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=11510
This commit is contained in:
Andres G. Aragoneses 2013-04-07 07:51:30 +01:00
parent 1a1300c762
commit 9ff7ec8b2c
3 changed files with 142 additions and 59 deletions

View file

@ -117,10 +117,6 @@ namespace GtkSharp.Generation {
} }
sw.WriteLine ("\t\t\tMarshal.StructureToPtr (native_iface, ifaceptr, false);"); sw.WriteLine ("\t\t\tMarshal.StructureToPtr (native_iface, ifaceptr, false);");
} }
foreach (Property prop in Properties.Values) {
sw.WriteLine ("\t\t\tGLib.Object.OverrideProperty (data, \"" + prop.CName + "\");");
}
sw.WriteLine ("\t\t}"); sw.WriteLine ("\t\t}");
sw.WriteLine (); sw.WriteLine ();
} }

View file

@ -37,10 +37,14 @@ namespace GLib {
IntPtr val; IntPtr val;
struct GTypeInfo { struct GTypeInfo {
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
public delegate void ClassInitDelegate (IntPtr gobject_class_handle);
public ushort class_size; public ushort class_size;
public IntPtr base_init; public IntPtr base_init;
public IntPtr base_finalize; public IntPtr base_finalize;
public IntPtr class_init; public ClassInitDelegate class_init;
public IntPtr class_finalize; public IntPtr class_finalize;
public IntPtr class_data; public IntPtr class_data;
public ushort instance_size; public ushort instance_size;
@ -356,19 +360,23 @@ namespace GLib {
return sb.ToString (); return sb.ToString ();
} }
internal static GType RegisterGObjectType (System.Type t) internal static GType RegisterGObjectType (Object.ClassInitializer gobject_class_initializer)
{ {
GType parent_gtype = LookupGObjectType (t.BaseType); GType parent_gtype = LookupGObjectType (gobject_class_initializer.Type.BaseType);
string name = BuildEscapedName (t); string name = BuildEscapedName (gobject_class_initializer.Type);
IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name);
GTypeQuery query; GTypeQuery query;
g_type_query (parent_gtype.Val, out query); g_type_query (parent_gtype.Val, out query);
GTypeInfo info = new GTypeInfo (); GTypeInfo info = new GTypeInfo ();
info.class_size = (ushort) query.class_size; info.class_size = (ushort) query.class_size;
info.instance_size = (ushort) query.instance_size; info.instance_size = (ushort) query.instance_size;
info.class_init = gobject_class_initializer.ClassInit;
GType gtype = new GType (g_type_register_static (parent_gtype.Val, native_name, ref info, 0)); GType gtype = new GType (g_type_register_static (parent_gtype.Val, native_name, ref info, 0));
GLib.Marshaller.Free (native_name); GLib.Marshaller.Free (native_name);
Register (gtype, t); Register (gtype, gobject_class_initializer.Type);
return gtype; return gtype;
} }

View file

@ -1,9 +1,11 @@
// Object.cs - GObject class wrapper implementation // Object.cs - GObject class wrapper implementation
// //
// Authors: Mike Kestner <mkestner@speakeasy.net> // Authors: Mike Kestner <mkestner@speakeasy.net>
// Andres G. Aragoneses <knocte@gmail.com>
// //
// Copyright (c) 2001-2003 Mike Kestner // Copyright (c) 2001-2003 Mike Kestner
// Copyright (c) 2004-2005 Novell, Inc. // Copyright (c) 2004-2005 Novell, Inc.
// Copyright (c) 2013 Andres G. Aragoneses
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the Lesser GNU General // modify it under the terms of version 2 of the Lesser GNU General
@ -214,6 +216,112 @@ namespace GLib {
public IntPtr dummy7; public IntPtr dummy7;
} }
internal class ClassInitializer {
internal uint Idx { get; set; }
internal Type Type { get; private set; }
internal bool HandlersOverriden { get; private set; }
bool is_first_subclass;
private GType gtype;
private List<GInterfaceAdapter> adapters = new List<GInterfaceAdapter> ();
internal ClassInitializer (Type type)
{
Idx = 1;
Type = type;
gtype = GType.RegisterGObjectType (this);
is_first_subclass = gtype.GetBaseType () == gtype.GetThresholdType ();
}
internal GType Init ()
{
AddGInterfaces ();
gtype.EnsureClass (); //calls class_init
AddProperties (gtype, Type, is_first_subclass, this);
ConnectDefaultHandlers (gtype, Type);
InvokeTypeInitializers (gtype, Type);
AddInterfaceProperties (gtype, Type);
return gtype;
}
private void AddGInterfaces ()
{
foreach (Type iface in Type.GetInterfaces ()) {
if (!iface.IsDefined (typeof (GInterfaceAttribute), true))
continue;
GInterfaceAttribute attr = iface.GetCustomAttributes (typeof (GInterfaceAttribute), false) [0] as GInterfaceAttribute;
GInterfaceAdapter adapter = Activator.CreateInstance (attr.AdapterType, null) as GInterfaceAdapter;
if (!iface.IsAssignableFrom (Type.BaseType)) {
GInterfaceInfo info = adapter.Info;
info.Data = gtype.Val;
g_type_add_interface_static (gtype.Val, adapter.GType.Val, ref info);
adapters.Add (adapter);
}
}
}
internal void ClassInit (IntPtr gobject_class_handle)
{
bool override_ctor = is_first_subclass;
bool override_props = is_first_subclass || adapters.Count > 0;
OverrideHandlers (gobject_class_handle, override_ctor, override_props);
foreach (GInterfaceAdapter adapter in adapters) {
InitializeProperties (adapter, gobject_class_handle);
}
}
private void InitializeProperties (GInterfaceAdapter adapter, IntPtr gobject_class_handle)
{
foreach (PropertyInfo pinfo in adapter.GetType ().GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) {
foreach (object attr in pinfo.GetCustomAttributes (typeof (PropertyAttribute), false)) {
if (pinfo.GetIndexParameters ().Length > 0)
throw new InvalidOperationException (String.Format ("Property {0} of type {1} cannot be overriden because its GLib.PropertyAttribute is expected to have one or more indexed parameters",
pinfo.Name, adapter.GetType ().FullName));
PropertyAttribute property_attr = attr as PropertyAttribute;
if (property_attr != null) {
OverrideProperty (gobject_class_handle, property_attr.Name);
}
}
}
}
void OverrideProperty (IntPtr declaring_class, string name)
{
Object.OverrideProperty (declaring_class, Idx++, name);
Idx++;
}
internal void OverrideHandlers (bool ctor, bool properties)
{
OverrideHandlers (gtype.GetClassPtr (), ctor, properties);
}
private void OverrideHandlers (IntPtr gobject_class_handle, bool ctor, bool properties)
{
if (HandlersOverriden || (ctor == false && properties == false))
return;
GObjectClass gobject_class = (GObjectClass)Marshal.PtrToStructure (gobject_class_handle, typeof(GObjectClass));
if (ctor) {
gobject_class.constructor_cb = GLib.Object.ConstructorHandler;
}
if (properties) {
gobject_class.get_prop_cb = GLib.Object.GetPropertyHandler;
gobject_class.set_prop_cb = GLib.Object.SetPropertyHandler;
}
Marshal.StructureToPtr (gobject_class, gobject_class_handle, false);
HandlersOverriden = true;
}
}
[UnmanagedFunctionPointer (CallingConvention.Cdecl)] [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
delegate IntPtr ConstructorDelegate (IntPtr gtype, uint n_construct_properties, IntPtr construct_properties); delegate IntPtr ConstructorDelegate (IntPtr gtype, uint n_construct_properties, IntPtr construct_properties);
@ -262,10 +370,16 @@ namespace GLib {
[DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void g_object_class_override_property (IntPtr klass, uint prop_id, IntPtr name); static extern void g_object_class_override_property (IntPtr klass, uint prop_id, IntPtr name);
public static void OverrideProperty (IntPtr declaring_class, string name) public static void OverrideProperty (IntPtr oclass, uint property_id, string name)
{ {
IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name); IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name);
g_object_class_override_property (declaring_class, idx, native_name); g_object_class_override_property (oclass, property_id, native_name);
}
[Obsolete ("Use OverrideProperty(oclass,property_id,name)")]
public static void OverrideProperty (IntPtr declaring_class, string name)
{
OverrideProperty (declaring_class, idx, name);
idx++; idx++;
} }
@ -301,13 +415,13 @@ namespace GLib {
return pspec.Handle; return pspec.Handle;
} }
static void AddProperties (GType gtype, System.Type t, bool register_instance_prop, ref bool handlers_overridden) static void AddProperties (GType gtype, System.Type t, bool register_instance_prop, ClassInitializer gobject_class_initializer)
{ {
if (register_instance_prop) { if (register_instance_prop) {
IntPtr declaring_class = gtype.GetClassPtr (); IntPtr declaring_class = gtype.GetClassPtr ();
ParamSpec pspec = new ParamSpec ("gtk-sharp-managed-instance", "", "", GType.Pointer, ParamFlags.Writable | ParamFlags.ConstructOnly); ParamSpec pspec = new ParamSpec ("gtk-sharp-managed-instance", "", "", GType.Pointer, ParamFlags.Writable | ParamFlags.ConstructOnly);
g_object_class_install_property (declaring_class, idx, pspec.Handle); g_object_class_install_property (declaring_class, gobject_class_initializer.Idx, pspec.Handle);
idx++; gobject_class_initializer.Idx++;
} }
foreach (PropertyInfo pinfo in t.GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) { foreach (PropertyInfo pinfo in t.GetProperties (BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) {
@ -315,17 +429,11 @@ namespace GLib {
if(pinfo.GetIndexParameters().Length > 0) if(pinfo.GetIndexParameters().Length > 0)
throw(new InvalidOperationException(String.Format("GLib.RegisterPropertyAttribute cannot be applied to property {0} of type {1} because the property expects one or more indexed parameters", pinfo.Name, t.FullName))); throw(new InvalidOperationException(String.Format("GLib.RegisterPropertyAttribute cannot be applied to property {0} of type {1} because the property expects one or more indexed parameters", pinfo.Name, t.FullName)));
if (!handlers_overridden) { gobject_class_initializer.OverrideHandlers (false, true);
IntPtr class_ptr = gtype.GetClassPtr ();
GObjectClass gobject_class = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass));
gobject_class.get_prop_cb = GetPropertyHandler;
gobject_class.set_prop_cb = SetPropertyHandler;
Marshal.StructureToPtr (gobject_class, class_ptr, false);
handlers_overridden = true;
}
PropertyAttribute property_attr = attr as PropertyAttribute; PropertyAttribute property_attr = attr as PropertyAttribute;
try { try {
IntPtr param_spec = RegisterProperty (gtype, property_attr.Name, property_attr.Nickname, property_attr.Blurb, idx, (GType) pinfo.PropertyType, pinfo.CanRead, pinfo.CanWrite); IntPtr param_spec = RegisterProperty (gtype, property_attr.Name, property_attr.Nickname, property_attr.Blurb, gobject_class_initializer.Idx, (GType) pinfo.PropertyType, pinfo.CanRead, pinfo.CanWrite);
Type type = (Type)gtype; Type type = (Type)gtype;
Dictionary<IntPtr, PropertyInfo> gtype_properties; Dictionary<IntPtr, PropertyInfo> gtype_properties;
if (!Properties.TryGetValue (type, out gtype_properties)) { if (!Properties.TryGetValue (type, out gtype_properties)) {
@ -333,7 +441,7 @@ namespace GLib {
Properties [type] = gtype_properties; Properties [type] = gtype_properties;
} }
gtype_properties.Add (param_spec, pinfo); gtype_properties.Add (param_spec, pinfo);
idx++; gobject_class_initializer.Idx++;
} catch (ArgumentException) { } catch (ArgumentException) {
throw new InvalidOperationException (String.Format ("GLib.PropertyAttribute cannot be applied to property {0} of type {1} because the return type of the property is not supported", pinfo.Name, t.FullName)); throw new InvalidOperationException (String.Format ("GLib.PropertyAttribute cannot be applied to property {0} of type {1} because the return type of the property is not supported", pinfo.Name, t.FullName));
} }
@ -410,7 +518,7 @@ namespace GLib {
[DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport ("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void g_type_add_interface_static (IntPtr gtype, IntPtr iface_type, ref GInterfaceInfo info); static extern void g_type_add_interface_static (IntPtr gtype, IntPtr iface_type, ref GInterfaceInfo info);
static void AddInterfaces (GType gtype, Type t, ref bool handlers_overridden) static void AddInterfaceProperties (GType gtype, Type t)
{ {
foreach (Type iface in t.GetInterfaces ()) { foreach (Type iface in t.GetInterfaces ()) {
if (!iface.IsDefined (typeof (GInterfaceAttribute), true)) if (!iface.IsDefined (typeof (GInterfaceAttribute), true))
@ -418,23 +526,7 @@ namespace GLib {
GInterfaceAttribute attr = iface.GetCustomAttributes (typeof (GInterfaceAttribute), false) [0] as GInterfaceAttribute; GInterfaceAttribute attr = iface.GetCustomAttributes (typeof (GInterfaceAttribute), false) [0] as GInterfaceAttribute;
GInterfaceAdapter adapter = Activator.CreateInstance (attr.AdapterType, null) as GInterfaceAdapter; GInterfaceAdapter adapter = Activator.CreateInstance (attr.AdapterType, null) as GInterfaceAdapter;
if (!handlers_overridden) {
IntPtr class_ptr = gtype.GetClassPtr ();
GObjectClass gobject_class = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass));
gobject_class.get_prop_cb = GetPropertyHandler;
gobject_class.set_prop_cb = SetPropertyHandler;
Marshal.StructureToPtr (gobject_class, class_ptr, false);
handlers_overridden = true;
}
if (!iface.IsAssignableFrom (t.BaseType)) {
GInterfaceInfo info = adapter.Info;
info.Data = gtype.GetClassPtr ();
//FIXME: overiding prop is done inside the init of interface adapter
// not sure that it is the good solution but
// it is the only one I found without exception or loop
g_type_add_interface_static (gtype.Val, adapter.GType.Val, ref info);
}
foreach (PropertyInfo p in iface.GetProperties ()) { foreach (PropertyInfo p in iface.GetProperties ()) {
PropertyAttribute[] attrs = p.GetCustomAttributes (typeof (PropertyAttribute), true) as PropertyAttribute []; PropertyAttribute[] attrs = p.GetCustomAttributes (typeof (PropertyAttribute), true) as PropertyAttribute [];
if (attrs.Length == 0) if (attrs.Length == 0)
@ -457,24 +549,11 @@ namespace GLib {
protected internal static GType RegisterGType (System.Type t) protected internal static GType RegisterGType (System.Type t)
{ {
GType gtype = GType.RegisterGObjectType (t); //this is a deprecated way of tracking a property counter,
bool is_first_subclass = gtype.GetBaseType () == gtype.GetThresholdType (); //but we may still need it for backwards compatibility
if (is_first_subclass) {
IntPtr class_ptr = gtype.GetClassPtr ();
GObjectClass gobject_class = (GObjectClass) Marshal.PtrToStructure (class_ptr, typeof (GObjectClass));
gobject_class.constructor_cb = ConstructorHandler;
gobject_class.get_prop_cb = GetPropertyHandler;
gobject_class.set_prop_cb = SetPropertyHandler;
Marshal.StructureToPtr (gobject_class, class_ptr, false);
}
idx = 1; idx = 1;
bool handlers_overridden = is_first_subclass;
AddProperties (gtype, t, is_first_subclass, ref handlers_overridden); return new ClassInitializer (t).Init ();
ConnectDefaultHandlers (gtype, t);
InvokeTypeInitializers (gtype, t);
AddInterfaces (gtype, t, ref handlers_overridden);
return gtype;
} }
protected GType LookupGType () protected GType LookupGType ()