[perl-Glib] Make Glib::Object subclassing more robust
- From: Torsten SchÃnfeld <tsch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [perl-Glib] Make Glib::Object subclassing more robust
- Date: Thu, 3 Jan 2013 23:14:07 +0000 (UTC)
commit ebf55199407d3be92162f5c2573c7f9c34614cb1
Author: Torsten SchÃnfeld <kaffeetisch gmx de>
Date: Thu Aug 2 21:41:41 2012 +0200
Make Glib::Object subclassing more robust
Rearrange how we register the interfaces, properties and signals of a newly
created type so that:
â The outcome is independent of the order of the arguments passed to
Glib::Object::Subclass or Glib::Type->register. This also avoids fallout from
the change to hash randomization in perl 5.17.6.
â We register things in the correct order: interfaces first, before entering
class_init; then properties and signals from within class_init. This also
avoids prematurely creating the new type's class.
GType.xs | 92 +++++++++++++++++++++++++++++++++++++++++++++++---------------
NEWS | 7 +++++
2 files changed, 77 insertions(+), 22 deletions(-)
---
diff --git a/GType.xs b/GType.xs
index 02e24a4..fdaad68 100644
--- a/GType.xs
+++ b/GType.xs
@@ -1234,13 +1234,10 @@ parse_signal_hash (GType instance_type,
static void
-add_signals (GType instance_type, HV * signals)
+add_signals (GType instance_type, HV * signals, AV * interfaces)
{
- GObjectClass *oclass;
HE * he;
- oclass = g_type_class_ref (instance_type);
-
hv_iterinit (signals);
while (NULL != (he = hv_iternext (signals))) {
I32 keylen;
@@ -1250,9 +1247,36 @@ add_signals (GType instance_type, HV * signals)
/* the key is the signal name */
signal_name = hv_iterkey (he, &keylen);
- /* if the signal is defined at this point, we're going to
- * override the installed closure. */
- signal_id = g_signal_lookup (signal_name, instance_type);
+
+ /* if, at this point, the signal is already defined in the
+ * ancestry or the interfaces we just added to instance_type,
+ * we can only override the installed closure. trying to
+ * create a new signal with the same name is an error.
+ *
+ * unfortunately, we cannot simply use instance_type to do the
+ * lookup because g_signal_lookup would complain about it since
+ * it hasn't been fully loaded yet. see
+ * <https://bugzilla.gnome.org/show_bug.cgi?id=691096>.
+ *
+ * FIXME: the "if (signal_id)" check in the hash ref block
+ * below could be removed since g_signal_newv also checks this.
+ * consequently, this lookup code could be moved into the class
+ * closure block below. */
+ signal_id = g_signal_lookup (signal_name,
+ g_type_parent (instance_type));
+ if (!signal_id && interfaces) {
+ int i;
+ for (i = 0; i <= av_len (interfaces); i++) {
+ GType interface_type;
+ SV ** svp = av_fetch (interfaces, i, FALSE);
+ if (!svp || !gperl_sv_is_defined (*svp))
+ continue;
+ interface_type = gperl_object_type_from_package (SvPV_nolen (*svp));
+ signal_id = g_signal_lookup (signal_name, interface_type);
+ if (signal_id)
+ break;
+ }
+ }
/* parse the key's value... */
value = hv_iterval (signals, he);
@@ -1310,8 +1334,6 @@ add_signals (GType instance_type, HV * signals)
signal_name);
}
}
-
- g_type_class_unref (oclass);
}
typedef struct {
@@ -1407,13 +1429,10 @@ prop_handler_lookup (GType instance_type,
}
static void
-add_properties (GType instance_type, AV * properties)
+add_properties (GType instance_type, GObjectClass * oclass, AV * properties)
{
- GObjectClass *oclass;
int propid;
- oclass = g_type_class_ref (instance_type);
-
for (propid = 0; propid <= av_len (properties); propid++) {
SV * sv = *av_fetch (properties, propid, 1);
GParamSpec * pspec = NULL;
@@ -1452,8 +1471,6 @@ add_properties (GType instance_type, AV * properties)
}
g_object_class_install_property (oclass, propid + 1, pspec);
}
-
- g_type_class_unref (oclass);
}
/*
@@ -1776,12 +1793,26 @@ gperl_type_reg_quark (void)
return q;
}
+typedef struct {
+ GType instance_type;
+ AV *interfaces;
+ AV *properties;
+ HV *signals;
+} GPerlClassData;
+
static void
-gperl_type_class_init (GObjectClass * class)
+gperl_type_class_init (GObjectClass * class, GPerlClassData * class_data)
{
class->finalize = gperl_type_finalize;
class->get_property = gperl_type_get_property;
class->set_property = gperl_type_set_property;
+
+ if (class_data->properties)
+ add_properties (class_data->instance_type, class,
+ class_data->properties);
+ if (class_data->signals)
+ add_signals (class_data->instance_type,
+ class_data->signals, class_data->interfaces);
}
static void
@@ -2152,15 +2183,18 @@ g_type_register_object (class, parent_package, new_package, ...);
PREINIT:
int i;
GTypeInfo type_info;
+ GPerlClassData class_data;
GTypeQuery query;
GType parent_type, new_type;
char * new_type_name;
CODE:
/* start with a clean slate */
memset (&type_info, 0, sizeof (GTypeInfo));
+ memset (&class_data, 0, sizeof (GPerlClassData));
type_info.base_init = (GBaseInitFunc) gperl_type_base_init;
type_info.class_init = (GClassInitFunc) gperl_type_class_init;
type_info.instance_init = (GInstanceInitFunc) gperl_type_instance_init;
+ type_info.class_data = &class_data;
/* yeah, i could just call gperl_object_type_from_package directly,
* but i want the error messages to be more informative. */
@@ -2196,29 +2230,43 @@ g_type_register_object (class, parent_package, new_package, ...);
/* mark this type as "one of ours". */
g_type_set_qdata (new_type, gperl_type_reg_quark (), (gpointer) TRUE);
- /* now look for things we should initialize presently, e.g.
- * signals and properties and interfaces and such, things that
- * would generally go into a class_init. */
+ /* put it into the class data so that add_signals and add_properties
+ * can use it. */
+ class_data.instance_type = new_type;
+
+ /* now look for things we should initialize, e.g. signals and
+ * properties and interfaces. put the corresponding data into the
+ * class_data struct. the interfaces will be handled directly further
+ * below, while the properties and signals will be handled in the
+ * class_init function so that they have access to the class instance.
+ * this mimics the way things are supposed to be done in C: register
+ * interfaces in the get_type function, and register properties and
+ * signals in the class_init function. */
for (i = 3 ; i < items ; i += 2) {
char * key = SvPV_nolen (ST (i));
if (strEQ (key, "signals")) {
if (gperl_sv_is_hash_ref (ST (i+1)))
- add_signals (new_type, (HV*)SvRV (ST (i+1)));
+ class_data.signals = (HV*)SvRV (ST (i+1));
else
croak ("signals must be a hash of signalname => signalspec pairs");
} else if (strEQ (key, "properties")) {
if (gperl_sv_is_array_ref (ST (i+1)))
- add_properties (new_type, (AV*)SvRV (ST (i+1)));
+ class_data.properties = (AV*)SvRV (ST (i+1));
else
croak ("properties must be an array of GParamSpecs");
} else if (strEQ (key, "interfaces")) {
if (gperl_sv_is_array_ref (ST (i+1)))
- add_interfaces (new_type, (AV*)SvRV (ST (i+1)));
+ class_data.interfaces = (AV*)SvRV (ST (i+1));
else
croak ("interfaces must be an array of package names");
}
}
+ /* add the interfaces to the type now before we create its class and
+ * enter the class_init function. */
+ if (class_data.interfaces)
+ add_interfaces (new_type, class_data.interfaces);
+
/* instantiate the class right now. perl doesn't let classes go
* away once they've been defined, so we'll just leak this ref and
* let the GObjectClass live as long as the program. in fact,
diff --git a/NEWS b/NEWS
index 5606566..ad71da0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+Overview of changes in Glib <next> (unstable)
+============================================
+
+* Make Glib::Object subclassing more robust. This should in particular fix
+ issues revealed by the change to hash randomization introduced in perl
+ 5.17.6.
+
Overview of changes in Glib 1.280 (stable)
==========================================
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]