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);");
}
foreach (Property prop in Properties.Values) {
sw.WriteLine ("\t\t\tGLib.Object.OverrideProperty (data, \"" + prop.CName + "\");");
}
sw.WriteLine ("\t\t}");
sw.WriteLine ();
}

View File

@ -37,10 +37,14 @@ namespace GLib {
IntPtr val;
struct GTypeInfo {
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
public delegate void ClassInitDelegate (IntPtr gobject_class_handle);
public ushort class_size;
public IntPtr base_init;
public IntPtr base_finalize;
public IntPtr class_init;
public ClassInitDelegate class_init;
public IntPtr class_finalize;
public IntPtr class_data;
public ushort instance_size;
@ -356,19 +360,23 @@ namespace GLib {
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);
string name = BuildEscapedName (t);
GType parent_gtype = LookupGObjectType (gobject_class_initializer.Type.BaseType);
string name = BuildEscapedName (gobject_class_initializer.Type);
IntPtr native_name = GLib.Marshaller.StringToPtrGStrdup (name);
GTypeQuery query;
g_type_query (parent_gtype.Val, out query);
GTypeInfo info = new GTypeInfo ();
info.class_size = (ushort) query.class_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));
GLib.Marshaller.Free (native_name);
Register (gtype, t);
Register (gtype, gobject_class_initializer.Type);
return gtype;
}

View File

@ -1,9 +1,11 @@
// Object.cs - GObject class wrapper implementation
//
// Authors: Mike Kestner <mkestner@speakeasy.net>
// Andres G. Aragoneses <knocte@gmail.com>
//
// Copyright (c) 2001-2003 Mike Kestner
// Copyright (c) 2004-2005 Novell, Inc.
// Copyright (c) 2013 Andres G. Aragoneses
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the Lesser GNU General
@ -214,6 +216,112 @@ namespace GLib {
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)]
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)]
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);
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++;
}
@ -301,31 +415,25 @@ namespace GLib {
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) {
IntPtr declaring_class = gtype.GetClassPtr ();
ParamSpec pspec = new ParamSpec ("gtk-sharp-managed-instance", "", "", GType.Pointer, ParamFlags.Writable | ParamFlags.ConstructOnly);
g_object_class_install_property (declaring_class, idx, pspec.Handle);
idx++;
g_object_class_install_property (declaring_class, gobject_class_initializer.Idx, pspec.Handle);
gobject_class_initializer.Idx++;
}
foreach (PropertyInfo pinfo in t.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("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) {
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;
}
gobject_class_initializer.OverrideHandlers (false, true);
PropertyAttribute property_attr = attr as PropertyAttribute;
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;
Dictionary<IntPtr, PropertyInfo> gtype_properties;
if (!Properties.TryGetValue (type, out gtype_properties)) {
@ -333,7 +441,7 @@ namespace GLib {
Properties [type] = gtype_properties;
}
gtype_properties.Add (param_spec, pinfo);
idx++;
gobject_class_initializer.Idx++;
} 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));
}
@ -410,7 +518,7 @@ namespace GLib {
[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 void AddInterfaces (GType gtype, Type t, ref bool handlers_overridden)
static void AddInterfaceProperties (GType gtype, Type t)
{
foreach (Type iface in t.GetInterfaces ()) {
if (!iface.IsDefined (typeof (GInterfaceAttribute), true))
@ -418,23 +526,7 @@ namespace GLib {
GInterfaceAttribute attr = iface.GetCustomAttributes (typeof (GInterfaceAttribute), false) [0] as GInterfaceAttribute;
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 ()) {
PropertyAttribute[] attrs = p.GetCustomAttributes (typeof (PropertyAttribute), true) as PropertyAttribute [];
if (attrs.Length == 0)
@ -457,24 +549,11 @@ namespace GLib {
protected internal static GType RegisterGType (System.Type t)
{
GType gtype = GType.RegisterGObjectType (t);
bool is_first_subclass = gtype.GetBaseType () == gtype.GetThresholdType ();
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);
}
//this is a deprecated way of tracking a property counter,
//but we may still need it for backwards compatibility
idx = 1;
bool handlers_overridden = is_first_subclass;
AddProperties (gtype, t, is_first_subclass, ref handlers_overridden);
ConnectDefaultHandlers (gtype, t);
InvokeTypeInitializers (gtype, t);
AddInterfaces (gtype, t, ref handlers_overridden);
return gtype;
return new ClassInitializer (t).Init ();
}
protected GType LookupGType ()