[perl-Glib-Object-Introspection] Be more selective when installing object vfunc wrappers
- From: Torsten SchÃnfeld <tsch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [perl-Glib-Object-Introspection] Be more selective when installing object vfunc wrappers
- Date: Fri, 14 Oct 2011 17:37:45 +0000 (UTC)
commit ce8edf2718cfad1b615bcde07ee8d3c2c661e541
Author: Torsten SchÃnfeld <kaffeetisch gmx de>
Date: Fri Oct 14 19:10:57 2011 +0200
Be more selective when installing object vfunc wrappers
* Only install fallback Perl vfuncs when there is an implementation somewhere
in the ancestry.
* Only set vfunc class struct fields when we can see a Perl implementation at
INIT time.
GObjectIntrospection.xs | 121 +++++++++++++++++++++++++++++---------
gperl-i11n-vfunc-object.c | 18 +++++-
lib/Glib/Object/Introspection.pm | 48 +++++++++++----
t/vfunc-implementation.t | 12 +++-
4 files changed, 156 insertions(+), 43 deletions(-)
---
diff --git a/GObjectIntrospection.xs b/GObjectIntrospection.xs
index 03b83ea..5eac4ad 100644
--- a/GObjectIntrospection.xs
+++ b/GObjectIntrospection.xs
@@ -217,7 +217,7 @@ static void generic_interface_init (gpointer iface, gpointer data);
static void generic_interface_finalize (gpointer iface, gpointer data);
/* object vfuncs */
-static void generic_class_init (GIObjectInfo *info, gpointer class);
+static void generic_class_init (GIObjectInfo *info, const gchar *target_package, gpointer class);
/* misc. */
#define ccroak(...) call_carp_croak (form (__VA_ARGS__));
@@ -535,10 +535,11 @@ _install_overrides (class, basename, object_name, target_package)
PREINIT:
GIRepository *repository;
GIObjectInfo *info;
- GType gtype, object_gtype;
+ GType gtype;
gpointer klass;
- PPCODE:
- dwarn ("_install_overrides: %s.%s for %s\n", basename, object_name, target_package);
+ CODE:
+ dwarn ("_install_overrides: %s.%s for %s\n",
+ basename, object_name, target_package);
repository = g_irepository_get_default ();
info = g_irepository_find_by_name (repository, basename, object_name);
if (!GI_IS_OBJECT_INFO (info))
@@ -551,13 +552,31 @@ _install_overrides (class, basename, object_name, target_package)
if (!klass)
ccroak ("internal problem: can't peek at type class for %s (%d)",
g_type_name (gtype), gtype);
- generic_class_init (info, klass);
- /* find all non-Perl parents up to and including the object type */
+ generic_class_init (info, target_package, klass);
+ g_base_info_unref (info);
+
+void
+_find_non_perl_parents (class, basename, object_name, target_package)
+ const gchar *basename
+ const gchar *object_name
+ const gchar *target_package
+ PREINIT:
+ GIRepository *repository;
+ GIObjectInfo *info;
+ GType gtype, object_gtype;
+ GQuark reg_quark = g_quark_from_static_string ("__gperl_type_reg");
+ PPCODE:
+ repository = g_irepository_get_default ();
+ info = g_irepository_find_by_name (repository, basename, object_name);
+ g_assert (info && GI_IS_OBJECT_INFO (info));
+ gtype = gperl_object_type_from_package (target_package);
object_gtype = g_registered_type_info_get_g_type (info);
+ /* find all non-Perl parents up to and including the object type */
while ((gtype = g_type_parent (gtype))) {
/* FIXME: we should export gperl_type_reg_quark from Glib */
- if (!g_type_get_qdata (gtype, g_quark_from_static_string ("__gperl_type_reg"))) {
- XPUSHs (sv_2mortal (newSVpv (gperl_object_package_from_type (gtype), PL_na)));
+ if (!g_type_get_qdata (gtype, reg_quark)) {
+ const gchar *package = gperl_object_package_from_type (gtype);
+ XPUSHs (sv_2mortal (newSVpv (package, PL_na)));
}
if (gtype == object_gtype) {
break;
@@ -566,9 +585,59 @@ _install_overrides (class, basename, object_name, target_package)
g_base_info_unref (info);
void
-_invoke_fallback_vfunc (class, basename, object_name, vfunc_name, target_package, ...)
+_find_vfuncs_with_implementation (class, object_package, target_package)
+ const gchar *object_package
+ const gchar *target_package
+ PREINIT:
+ GIRepository *repository;
+ GType object_gtype, target_gtype;
+ gpointer object_klass, target_klass;
+ GIObjectInfo *object_info;
+ GIStructInfo *struct_info;
+ gint n_vfuncs, i;
+ PPCODE:
+ repository = g_irepository_get_default ();
+ target_gtype = gperl_object_type_from_package (target_package);
+ object_gtype = gperl_object_type_from_package (object_package);
+ g_assert (target_gtype && object_gtype);
+ target_klass = g_type_class_peek (target_gtype);
+ object_klass = g_type_class_peek (object_gtype);
+ g_assert (target_klass && object_klass);
+ object_info = g_irepository_find_by_gtype (repository, object_gtype);
+ g_assert (object_info && GI_IS_OBJECT_INFO (object_info));
+ struct_info = g_object_info_get_class_struct (object_info);
+ g_assert (struct_info);
+ n_vfuncs = g_object_info_get_n_vfuncs (object_info);
+ for (i = 0; i < n_vfuncs; i++) {
+ GIVFuncInfo *vfunc_info;
+ const gchar *vfunc_name;
+ GIFieldInfo *field_info;
+ gint field_offset;
+ gchar *perl_method_name;
+ vfunc_info = g_object_info_get_vfunc (object_info, i);
+ vfunc_name = g_base_info_get_name (vfunc_info);
+ /* FIXME: g_vfunc_info_get_offset does not seem to work here. */
+ field_info = get_field_info (struct_info, vfunc_name);
+ g_assert (field_info);
+ field_offset = g_field_info_get_offset (field_info);
+ perl_method_name = g_ascii_strup (vfunc_name, -1);
+ if (G_STRUCT_MEMBER (gpointer, target_klass, field_offset)) {
+ AV *av = newAV ();
+ av_push (av, newSVpv (vfunc_name, PL_na));
+ av_push (av, newSVpv (perl_method_name, PL_na));
+ XPUSHs (sv_2mortal (newRV_noinc ((SV *) av)));
+ }
+ g_free (perl_method_name);
+ g_base_info_unref (field_info);
+ g_base_info_unref (vfunc_info);
+ }
+ g_base_info_unref (struct_info);
+ g_base_info_unref (object_info);
+
+void
+_invoke_fallback_vfunc (class, basename, vfunc_package, vfunc_name, target_package, ...)
const gchar *basename
- const gchar *object_name
+ const gchar *vfunc_package
const gchar *vfunc_name
const gchar *target_package
PREINIT:
@@ -583,16 +652,15 @@ _invoke_fallback_vfunc (class, basename, object_name, vfunc_name, target_package
gint field_offset;
gpointer func_pointer;
PPCODE:
- dwarn ("_invoke_parent_vfunc: %s\n", vfunc_name);
- repository = g_irepository_get_default ();
- info = g_irepository_find_by_name (repository, basename, object_name);
- g_assert (info);
+ dwarn ("_invoke_parent_vfunc: %s.%s, target = %s\n",
+ vfunc_package, vfunc_name, target_package);
gtype = gperl_object_type_from_package (target_package);
- dwarn (" target: %s\n", target_package);
klass = g_type_class_peek (gtype);
- if (!klass)
- ccroak ("internal problem: can't peek at type class for %s (%d)",
- g_type_name (gtype), gtype);
+ g_assert (klass);
+ repository = g_irepository_get_default ();
+ info = g_irepository_find_by_gtype (
+ repository, gperl_object_type_from_package (vfunc_package));
+ g_assert (info && GI_IS_OBJECT_INFO (info));
struct_info = g_object_info_get_class_struct (info);
g_assert (struct_info);
vfunc_info = g_object_info_find_vfunc (info, vfunc_name);
@@ -602,15 +670,14 @@ _invoke_fallback_vfunc (class, basename, object_name, vfunc_name, target_package
g_assert (field_info);
field_offset = g_field_info_get_offset (field_info);
func_pointer = G_STRUCT_MEMBER (gpointer, klass, field_offset);
- if (func_pointer) {
- invoke_callable (vfunc_info, func_pointer,
- sp, ax, mark, items,
- internal_stack_offset);
- /* SPAGAIN since invoke_callable probably modified the stack
- * pointer. so we need to make sure that our local variable
- * 'sp' is correct before the implicit PUTBACK happens. */
- SPAGAIN;
- }
+ g_assert (func_pointer);
+ invoke_callable (vfunc_info, func_pointer,
+ sp, ax, mark, items,
+ internal_stack_offset);
+ /* SPAGAIN since invoke_callable probably modified the stack
+ * pointer. so we need to make sure that our local variable
+ * 'sp' is correct before the implicit PUTBACK happens. */
+ SPAGAIN;
g_base_info_unref (field_info);
g_base_info_unref (vfunc_info);
g_base_info_unref (info);
diff --git a/gperl-i11n-vfunc-object.c b/gperl-i11n-vfunc-object.c
index 46ad0ef..f7355ad 100644
--- a/gperl-i11n-vfunc-object.c
+++ b/gperl-i11n-vfunc-object.c
@@ -1,7 +1,7 @@
/* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
static void
-generic_class_init (GIObjectInfo *info, gpointer class)
+generic_class_init (GIObjectInfo *info, const gchar *target_package, gpointer class)
{
GIStructInfo *struct_info;
gint n, i;
@@ -19,13 +19,27 @@ generic_class_init (GIObjectInfo *info, gpointer class)
vfunc_info = g_object_info_get_vfunc (info, i);
vfunc_name = g_base_info_get_name (vfunc_info);
+ perl_method_name = g_ascii_strup (vfunc_name, -1);
+ {
+ /* If there is no implementation of this vfunc at INIT
+ * time, we assume that the intention is to provide no
+ * implementation and we thus skip setting up the class
+ * struct member. */
+ HV * stash = gv_stashpv (target_package, 0);
+ GV * slot = gv_fetchmethod (stash, perl_method_name);
+ if (!slot) {
+ g_base_info_unref (vfunc_info);
+ g_free (perl_method_name);
+ continue;
+ }
+ }
+
/* FIXME: g_vfunc_info_get_offset does not seem to work here. */
field_info = get_field_info (struct_info, vfunc_name);
g_assert (field_info);
field_offset = g_field_info_get_offset (field_info);
field_type_info = g_field_info_get_type (field_info);
- perl_method_name = g_ascii_strup (vfunc_name, -1);
callback_info = create_perl_callback_closure_for_named_sub (
field_type_info, perl_method_name);
dwarn ("installing vfunc %s as %s at offset %d (vs. %d) inside %p\n",
diff --git a/lib/Glib/Object/Introspection.pm b/lib/Glib/Object/Introspection.pm
index d74b395..208a06e 100644
--- a/lib/Glib/Object/Introspection.pm
+++ b/lib/Glib/Object/Introspection.pm
@@ -28,6 +28,10 @@ $Carp::Internal{(__PACKAGE__)}++;
require XSLoader;
XSLoader::load(__PACKAGE__, $VERSION);
+my %FORBIDDEN_SUB_NAMES = map { $_ => 1 } qw/AUTOLOAD CLONE DESTROY BEGIN
+ UNITCHECK CHECK INIT END/;
+my @_OBJECT_PACKAGES_WITH_VFUNCS = ();
+
sub _create_invoker_sub {
my ($basename, $namespace, $name,
$shift_package_name, $flatten_array_ref_return,
@@ -143,39 +147,59 @@ sub setup {
};
}
- my %forbidden_sub_names = map { $_ => 1 } qw/AUTOLOAD CLONE DESTROY
- BEGIN UNITCHECK CHECK INIT END/;
-
foreach my $object_name (keys %{$objects_with_vfuncs}) {
my $object_package = $package . '::' . $object_name;
my $installer_name = $object_package . '::_INSTALL_OVERRIDES';
*{$installer_name} = sub {
my ($target_package) = @_;
+
+ # For each vfunc in our ancestry that has an implementation, add a
+ # wrapper sub to our immediate parent.
my @non_perl_parent_packages =
- __PACKAGE__->_install_overrides($basename, $object_name,
- $target_package);
+ __PACKAGE__->_find_non_perl_parents($basename, $object_name,
+ $target_package);
+ my $first_parent = $non_perl_parent_packages[0];
foreach my $parent_package (@non_perl_parent_packages) {
+ my @vfuncs = __PACKAGE__->_find_vfuncs_with_implementation(
+ $parent_package, $first_parent);
VFUNC:
- foreach my $vfunc_names (@{$objects_with_vfuncs->{$object_name}}) {
- my $vfunc_name = $vfunc_names->[0];
- my $perl_vfunc_name = $vfunc_names->[1];
- if (exists $forbidden_sub_names{$perl_vfunc_name}) {
+ foreach my $vfunc_names (@vfuncs) {
+ my ($vfunc_name, $perl_vfunc_name) = @{$vfunc_names};
+ if (exists $FORBIDDEN_SUB_NAMES{$perl_vfunc_name}) {
$perl_vfunc_name .= '_VFUNC';
}
- my $full_perl_vfunc_name = $parent_package . '::' . $perl_vfunc_name;
+ my $full_perl_vfunc_name =
+ $first_parent . '::' . $perl_vfunc_name;
if (defined &{$full_perl_vfunc_name}) {
next VFUNC;
}
*{$full_perl_vfunc_name} = sub {
- __PACKAGE__->_invoke_fallback_vfunc($basename, $object_name, $vfunc_name,
- $parent_package, @_);
+ __PACKAGE__->_invoke_fallback_vfunc($basename,
+ $parent_package,
+ $vfunc_name,
+ $first_parent,
+ @_);
}
}
}
+
+ # Delay hooking up the vfuncs until INIT so that we can see whether the
+ # package defines the relevant subs or not.
+ push @_OBJECT_PACKAGES_WITH_VFUNCS,
+ [$basename, $object_name, $target_package];
};
}
}
+sub INIT {
+ no strict qw(refs);
+ foreach my $target (@_OBJECT_PACKAGES_WITH_VFUNCS) {
+ my ($basename, $object_name, $target_package) = @{$target};
+ __PACKAGE__->_install_overrides($basename, $object_name, $target_package);
+ }
+ @_OBJECT_PACKAGES_WITH_VFUNCS = ();
+}
+
package Glib::Object::Introspection::_FuncWrapper;
use overload
diff --git a/t/vfunc-implementation.t b/t/vfunc-implementation.t
index 17618e7..94de845 100644
--- a/t/vfunc-implementation.t
+++ b/t/vfunc-implementation.t
@@ -5,7 +5,7 @@ BEGIN { require './t/inc/setup.pl' };
use strict;
use warnings;
-plan tests => 35;
+plan tests => 31;
{
package GoodImplementation;
@@ -109,6 +109,8 @@ plan tests => 35;
is ($foo->get ('int'), 23);
}
+=for segfault
+
{
package NoImplementation;
use Glib::Object::Subclass 'GI::Object';
@@ -121,6 +123,10 @@ plan tests => 35;
like ($@, qr/method_int8_in/);
}
+=cut
+
+=for supported?
+
{
package BadChaininig;
use Glib::Object::Subclass 'GI::Object';
@@ -136,5 +142,7 @@ plan tests => 35;
my $foo = BadChaininig->new;
local $@;
eval { $foo->method_int8_in (23) };
- like ($@, qr/method_int8_in/);
+ like ($@, qr/method_int8_in/i);
}
+
+=cut
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]