[perl-Glib-Object-Introspection] Move most code into separate files
- From: Torsten SchÃnfeld <tsch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [perl-Glib-Object-Introspection] Move most code into separate files
- Date: Fri, 7 Oct 2011 21:54:06 +0000 (UTC)
commit 5e32a372ce18b352389f5d7b05846ca2bb851cd1
Author: Torsten SchÃnfeld <kaffeetisch gmx de>
Date: Fri Oct 7 23:32:36 2011 +0200
Move most code into separate files
.gitignore | 2 +-
GObjectIntrospection.xs | 2855 ++--------------------------------------
MANIFEST | 24 +
MANIFEST.SKIP | 2 +-
gperl-i11n-callback.c | 82 ++
gperl-i11n-croak.c | 20 +
gperl-i11n-field.c | 131 ++
gperl-i11n-gvalue.c | 19 +
gperl-i11n-info.c | 111 ++
gperl-i11n-invoke-c.c | 238 ++++
gperl-i11n-invoke-info.c | 211 +++
gperl-i11n-invoke-perl.c | 222 ++++
gperl-i11n-marshal-arg.c | 232 ++++
gperl-i11n-marshal-array.c | 167 +++
gperl-i11n-marshal-callback.c | 82 ++
gperl-i11n-marshal-hash.c | 175 +++
gperl-i11n-marshal-interface.c | 205 +++
gperl-i11n-marshal-list.c | 120 ++
gperl-i11n-marshal-raw.c | 155 +++
gperl-i11n-marshal-struct.c | 142 ++
gperl-i11n-method.c | 120 ++
gperl-i11n-size.c | 152 +++
gperl-i11n-vfunc-interface.c | 50 +
gperl-i11n-vfunc-object.c | 40 +
24 files changed, 2803 insertions(+), 2754 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 617ebc1..4994ee1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-*.c
+GObjectIntrospection.c
*.o
*.bs
Makefile
diff --git a/GObjectIntrospection.xs b/GObjectIntrospection.xs
index 9124fdd..0caa674 100644
--- a/GObjectIntrospection.xs
+++ b/GObjectIntrospection.xs
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2005 muppet
- * Copyright (C) 2005-2010 Torsten Schoenfeld <kaffeetisch gmx de>
+ * Copyright (C) 2005-2011 Torsten Schoenfeld <kaffeetisch gmx de>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
@@ -26,59 +26,6 @@
#include <girepository.h>
#include <girffi.h>
-/* #define NOISY */
-#ifdef NOISY
-# define dwarn(...) warn(__VA_ARGS__)
-#else
-# define dwarn(...)
-#endif
-
-/* ------------------------------------------------------------------------- */
-
-/* Call Carp's croak() so that errors are reported at their location in the
- * user's program, not in Introspection.pm. Adapted from
- * <http://www.perlmonks.org/?node_id=865159>. */
-#define ccroak(...) call_carp_croak (form (__VA_ARGS__));
-static void
-call_carp_croak (const char *msg)
-{
- dSP;
-
- ENTER;
- SAVETMPS;
-
- PUSHMARK (SP);
- XPUSHs (sv_2mortal (newSVpv(msg, PL_na)));
- PUTBACK;
-
- call_pv("Carp::croak", G_VOID | G_DISCARD);
-
- FREETMPS;
- LEAVE;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* Semi-private package for marshalling into GValues. */
-#define GVALUE_WRAPPER_PACKAGE "Glib::Object::Introspection::GValueWrapper"
-
-static GValue *
-SvGValueWrapper (SV *sv)
-{
- return sv_derived_from (sv, GVALUE_WRAPPER_PACKAGE)
- ? INT2PTR (GValue*, SvIV (SvRV (sv)))
- : NULL;
-}
-
-static SV *
-newSVGValueWrapper (GValue *v)
-{
- SV *sv;
- sv = newSV (0);
- sv_setref_pv (sv, GVALUE_WRAPPER_PACKAGE, v);
- return sv;
-}
-
/* ------------------------------------------------------------------------- */
typedef struct {
@@ -146,19 +93,53 @@ typedef struct {
GSList * array_infos;
} GPerlI11nInvocationInfo;
+/* callbacks */
static GPerlI11nCallbackInfo* create_callback_closure (GITypeInfo *cb_type, SV *code);
static void attach_callback_data (GPerlI11nCallbackInfo *info, SV *data);
-
-static void invoke_callback (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata);
+static GPerlI11nCallbackInfo * create_callback_closure_for_named_sub (GITypeInfo *cb_type, gchar *sub_name, gchar *package_name);
static void release_callback (gpointer data);
-static SV * arg_to_sv (GIArgument * arg,
- GITypeInfo * info,
- GITransfer transfer,
- GPerlI11nInvocationInfo * iinfo);
+/* invocation */
+static void invoke_callback (ffi_cif* cif,
+ gpointer resp,
+ gpointer* args,
+ gpointer userdata);
+
+void invoke_callable (GICallableInfo *info,
+ gpointer func_pointer,
+ SV **sp, I32 ax, SV **mark, I32 items, /* these correspond to dXSARGS */
+ UV internal_stack_offset);
+
+/* invocation info */
+static void prepare_invocation_info (GPerlI11nInvocationInfo *iinfo,
+ GICallableInfo *info,
+ IV items,
+ UV internal_stack_offset);
+static void clear_invocation_info (GPerlI11nInvocationInfo *iinfo);
+static gpointer allocate_out_mem (GITypeInfo *arg_type);
+static void handle_automatic_arg (guint pos,
+ GIArgument * arg,
+ GPerlI11nInvocationInfo * invocation_info);
+
+/* info finders */
+static GIFunctionInfo * get_function_info (GIRepository *repository,
+ const gchar *basename,
+ const gchar *namespace,
+ const gchar *method);
+static GIFieldInfo * get_field_info (GIBaseInfo *info,
+ const gchar *field_name);
+
+/* marshallers */
static SV * interface_to_sv (GITypeInfo* info,
GIArgument *arg,
gboolean own);
+static void sv_to_interface (GIArgInfo * arg_info,
+ GITypeInfo * type_info,
+ SV * sv,
+ GIArgument * arg,
+ GPerlI11nInvocationInfo * invocation_info);
+
+static gpointer instance_sv_to_pointer (GICallableInfo *info, SV *sv);
static void sv_to_arg (SV * sv,
GIArgument * arg,
@@ -167,2716 +148,86 @@ static void sv_to_arg (SV * sv,
GITransfer transfer,
gboolean may_be_null,
GPerlI11nInvocationInfo * invocation_info);
+static SV * arg_to_sv (GIArgument * arg,
+ GITypeInfo * info,
+ GITransfer transfer,
+ GPerlI11nInvocationInfo *iinfo);
-/* ------------------------------------------------------------------------- */
-
-/* Caller owns return value */
-static GIFunctionInfo *
-get_function_info (GIRepository *repository,
- const gchar *basename,
- const gchar *namespace,
- const gchar *method)
-{
- dwarn ("%s: %s, %s, %s\n", G_STRFUNC, basename, namespace, method);
-
- if (namespace) {
- GIFunctionInfo *function_info = NULL;
- GIBaseInfo *namespace_info = g_irepository_find_by_name (
- repository, basename, namespace);
- if (!namespace_info)
- ccroak ("Can't find information for namespace %s",
- namespace);
-
- switch (g_base_info_get_type (namespace_info)) {
- case GI_INFO_TYPE_OBJECT:
- function_info = g_object_info_find_method (
- (GIObjectInfo *) namespace_info,
- method);
- break;
- case GI_INFO_TYPE_INTERFACE:
- function_info = g_interface_info_find_method (
- (GIInterfaceInfo *) namespace_info,
- method);
- break;
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- function_info = g_struct_info_find_method (
- (GIStructInfo *) namespace_info,
- method);
- break;
- case GI_INFO_TYPE_UNION:
- function_info = g_union_info_find_method (
- (GIUnionInfo *) namespace_info,
- method);
- break;
- default:
- ccroak ("Base info for namespace %s has incorrect type",
- namespace);
- }
-
- if (!function_info)
- ccroak ("Can't find information for method "
- "%s::%s", namespace, method);
-
- g_base_info_unref (namespace_info);
-
- return function_info;
- } else {
- GIBaseInfo *method_info = g_irepository_find_by_name (
- repository, basename, method);
-
- if (!method_info)
- ccroak ("Can't find information for method %s", method);
-
- switch (g_base_info_get_type (method_info)) {
- case GI_INFO_TYPE_FUNCTION:
- return (GIFunctionInfo *) method_info;
- default:
- ccroak ("Base info for method %s has incorrect type",
- method);
- }
- }
-
- return NULL;
-}
-
-/* Caller owns return value */
-static GIFieldInfo *
-get_field_info (GIBaseInfo *info, const gchar *field_name)
-{
- GIInfoType info_type;
- info_type = g_base_info_get_type (info);
- switch (info_type) {
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- {
- gint n_fields, i;
- n_fields = g_struct_info_get_n_fields ((GIStructInfo *) info);
- for (i = 0; i < n_fields; i++) {
- GIFieldInfo *field_info;
- field_info = g_struct_info_get_field ((GIStructInfo *) info, i);
- if (0 == strcmp (field_name, g_base_info_get_name (field_info))) {
- return field_info;
- }
- g_base_info_unref (field_info);
- }
- break;
- }
- case GI_INFO_TYPE_UNION:
- {
- gint n_fields, i;
- n_fields = g_union_info_get_n_fields ((GIStructInfo *) info);
- for (i = 0; i < n_fields; i++) {
- GIFieldInfo *field_info;
- field_info = g_union_info_get_field ((GIStructInfo *) info, i);
- if (0 == strcmp (field_name, g_base_info_get_name (field_info))) {
- return field_info;
- }
- g_base_info_unref (field_info);
- }
- break;
- }
- default:
- break;
- }
- return NULL;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static gpointer
-handle_callback_arg (GIArgInfo * arg_info,
- GITypeInfo * type_info,
- SV * sv,
- GPerlI11nInvocationInfo * invocation_info)
-{
- GPerlI11nCallbackInfo *callback_info;
-
- GSList *l;
- for (l = invocation_info->callback_infos; l != NULL; l = l->next) {
- GPerlI11nCallbackInfo *callback_info = l->data;
- if (invocation_info->current_pos == callback_info->notify_pos) {
- dwarn (" destroy notify for callback %p\n",
- callback_info);
- /* Decrease the dynamic stack offset so that this
- * destroy notify callback doesn't consume any Perl
- * value from the stack. */
- invocation_info->dynamic_stack_offset--;
- return release_callback;
- }
- }
-
- callback_info = create_callback_closure (type_info, sv);
- callback_info->data_pos = g_arg_info_get_closure (arg_info);
- callback_info->notify_pos = g_arg_info_get_destroy (arg_info);
- callback_info->free_after_use = FALSE;
-
- dwarn (" callback data at %d, destroy at %d\n",
- callback_info->data_pos, callback_info->notify_pos);
-
- switch (g_arg_info_get_scope (arg_info)) {
- case GI_SCOPE_TYPE_CALL:
- dwarn (" callback has scope 'call'\n");
- invocation_info->free_after_call
- = g_slist_prepend (invocation_info->free_after_call,
- callback_info);
- break;
- case GI_SCOPE_TYPE_NOTIFIED:
- dwarn (" callback has scope 'notified'\n");
- /* This case is already taken care of by the notify
- * stuff above */
- break;
- case GI_SCOPE_TYPE_ASYNC:
- dwarn (" callback has scope 'async'\n");
- /* FIXME: callback_info->free_after_use = TRUE; */
- break;
- default:
- ccroak ("unhandled scope type %d encountered",
- g_arg_info_get_scope (arg_info));
- }
-
- invocation_info->callback_infos =
- g_slist_prepend (invocation_info->callback_infos,
- callback_info);
-
- dwarn (" returning closure %p from info %p\n",
- callback_info->closure, callback_info);
- return callback_info->closure;
-}
-
-static gpointer
-handle_void_arg (SV * sv,
- GPerlI11nInvocationInfo * invocation_info)
-{
- gpointer pointer = NULL;
- gboolean is_user_data = FALSE;
- GSList *l;
- for (l = invocation_info->callback_infos; l != NULL; l = l->next) {
- GPerlI11nCallbackInfo *callback_info = l->data;
- if (callback_info->data_pos == invocation_info->current_pos) {
- is_user_data = TRUE;
- dwarn (" user data for callback %p\n",
- callback_info);
- attach_callback_data (callback_info, sv);
- pointer = callback_info;
- break; /* out of the for loop */
- }
- }
- if (!is_user_data)
- ccroak ("encountered void pointer that is not callback user data");
- return pointer;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* These three are basically copied from pygi's pygi-info.c. :-( */
-
-static gsize
-size_of_type_tag (GITypeTag type_tag)
-{
- switch(type_tag) {
- case GI_TYPE_TAG_BOOLEAN:
- return sizeof (gboolean);
- case GI_TYPE_TAG_INT8:
- case GI_TYPE_TAG_UINT8:
- return sizeof (gint8);
- case GI_TYPE_TAG_INT16:
- case GI_TYPE_TAG_UINT16:
- return sizeof (gint16);
- case GI_TYPE_TAG_INT32:
- case GI_TYPE_TAG_UINT32:
- return sizeof (gint32);
- case GI_TYPE_TAG_INT64:
- case GI_TYPE_TAG_UINT64:
- return sizeof (gint64);
- case GI_TYPE_TAG_FLOAT:
- return sizeof (gfloat);
- case GI_TYPE_TAG_DOUBLE:
- return sizeof (gdouble);
- case GI_TYPE_TAG_GTYPE:
- return sizeof (GType);
- case GI_TYPE_TAG_UNICHAR:
- return sizeof (gunichar);
-
- case GI_TYPE_TAG_VOID:
- case GI_TYPE_TAG_UTF8:
- case GI_TYPE_TAG_FILENAME:
- case GI_TYPE_TAG_ARRAY:
- case GI_TYPE_TAG_INTERFACE:
- case GI_TYPE_TAG_GLIST:
- case GI_TYPE_TAG_GSLIST:
- case GI_TYPE_TAG_GHASH:
- case GI_TYPE_TAG_ERROR:
- ccroak ("Unable to determine the size of '%s'",
- g_type_tag_to_string (type_tag));
- break;
- }
-
- return 0;
-}
-
-static gsize
-size_of_interface (GITypeInfo *type_info)
-{
- gsize size = 0;
-
- GIBaseInfo *info;
- GIInfoType info_type;
-
- info = g_type_info_get_interface (type_info);
- info_type = g_base_info_get_type (info);
-
- switch (info_type) {
- case GI_INFO_TYPE_STRUCT:
- if (g_type_info_is_pointer (type_info)) {
- size = sizeof (gpointer);
- } else {
- /* FIXME: Remove this workaround once
- * gobject-introspection is fixed:
- * <https://bugzilla.gnome.org/show_bug.cgi?id=657040>. */
- GType type = g_registered_type_info_get_g_type (info);
- if (type == G_TYPE_VALUE) {
- size = sizeof (GValue);
- } else {
- size = g_struct_info_get_size ((GIStructInfo *) info);
- }
- }
- break;
-
- case GI_INFO_TYPE_UNION:
- if (g_type_info_is_pointer (type_info)) {
- size = sizeof (gpointer);
- } else {
- size = g_union_info_get_size ((GIUnionInfo *) info);
- }
- break;
-
- case GI_INFO_TYPE_ENUM:
- case GI_INFO_TYPE_FLAGS:
- if (g_type_info_is_pointer (type_info)) {
- size = sizeof (gpointer);
- } else {
- GITypeTag type_tag;
- type_tag = g_enum_info_get_storage_type ((GIEnumInfo *) info);
- size = size_of_type_tag (type_tag);
- }
- break;
-
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_OBJECT:
- case GI_INFO_TYPE_INTERFACE:
- case GI_INFO_TYPE_CALLBACK:
- size = sizeof (gpointer);
- break;
-
- default:
- g_assert_not_reached ();
- break;
- }
-
- g_base_info_unref (info);
-
- return size;
-}
-
-static gsize
-size_of_type_info (GITypeInfo *type_info)
-{
- GITypeTag type_tag;
-
- type_tag = g_type_info_get_tag (type_info);
- switch (type_tag) {
- case GI_TYPE_TAG_BOOLEAN:
- case GI_TYPE_TAG_INT8:
- case GI_TYPE_TAG_UINT8:
- case GI_TYPE_TAG_INT16:
- case GI_TYPE_TAG_UINT16:
- case GI_TYPE_TAG_INT32:
- case GI_TYPE_TAG_UINT32:
- case GI_TYPE_TAG_INT64:
- case GI_TYPE_TAG_UINT64:
- case GI_TYPE_TAG_FLOAT:
- case GI_TYPE_TAG_DOUBLE:
- case GI_TYPE_TAG_GTYPE:
- case GI_TYPE_TAG_UNICHAR:
- if (g_type_info_is_pointer (type_info)) {
- return sizeof (gpointer);
- } else {
- return size_of_type_tag (type_tag);
- }
-
- case GI_TYPE_TAG_INTERFACE:
- return size_of_interface (type_info);
-
- case GI_TYPE_TAG_ARRAY:
- case GI_TYPE_TAG_VOID:
- case GI_TYPE_TAG_UTF8:
- case GI_TYPE_TAG_FILENAME:
- case GI_TYPE_TAG_GLIST:
- case GI_TYPE_TAG_GSLIST:
- case GI_TYPE_TAG_GHASH:
- case GI_TYPE_TAG_ERROR:
- return sizeof (gpointer);
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static SV *
-get_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer)
-{
- GITypeInfo *field_type;
- GIBaseInfo *interface_info;
- GIArgument value;
- SV *sv = NULL;
-
- field_type = g_field_info_get_type (field_info);
- interface_info = g_type_info_get_interface (field_type);
-
- /* This case is not handled by g_field_info_set_field. */
- if (!g_type_info_is_pointer (field_type) &&
- g_type_info_get_tag (field_type) == GI_TYPE_TAG_INTERFACE &&
- g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT)
- {
- gsize offset;
- offset = g_field_info_get_offset (field_info);
- value.v_pointer = mem + offset;
- sv = arg_to_sv (&value,
- field_type,
- GI_TRANSFER_NOTHING,
- NULL);
- } else if (g_field_info_get_field (field_info, mem, &value)) {
- sv = arg_to_sv (&value,
- field_type,
- transfer,
- NULL);
- } else {
- ccroak ("Could not get field '%s'",
- g_base_info_get_name (field_info));
- }
-
- if (interface_info)
- g_base_info_unref (interface_info);
- g_base_info_unref ((GIBaseInfo *) field_type);
-
- return sv;
-}
-
-static void
-set_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer, SV *value)
-{
- GITypeInfo *field_type;
- GIBaseInfo *interface_info;
- GIArgument arg;
-
- field_type = g_field_info_get_type (field_info);
- interface_info = g_type_info_get_interface (field_type);
-
- /* FIXME: No GIArgInfo and no
- * GPerlI11nInvocationInfo here. What if the
- * struct contains an object pointer, or a
- * callback field? And is it OK to always
- * allow undef? */
-
- /* This case is not handled by g_field_info_set_field. */
- if (!g_type_info_is_pointer (field_type) &&
- g_type_info_get_tag (field_type) == GI_TYPE_TAG_INTERFACE &&
- g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT)
- {
- gsize offset;
- gssize size;
- /* Enforce GI_TRANSFER_NOTHING since we will copy into the
- * memory that has already been allocated inside 'mem' */
- sv_to_arg (value, &arg, NULL, field_type,
- GI_TRANSFER_NOTHING, TRUE, NULL);
- offset = g_field_info_get_offset (field_info);
- size = g_struct_info_get_size (interface_info);
- g_memmove (mem + offset, arg.v_pointer, size);
- } else {
- sv_to_arg (value, &arg, NULL, field_type,
- transfer, TRUE, NULL);
- if (!g_field_info_set_field (field_info, mem, &arg))
- ccroak ("Could not set field '%s'",
- g_base_info_get_name (field_info));
- }
-
- if (interface_info)
- g_base_info_unref (interface_info);
- g_base_info_unref (field_type);
-}
-
-static SV *
-struct_to_sv (GIBaseInfo* info,
- GIInfoType info_type,
- gpointer pointer,
- gboolean own)
-{
- HV *hv;
-
- dwarn ("%s: pointer %p\n", G_STRFUNC, pointer);
-
- if (pointer == NULL) {
- return &PL_sv_undef;
- }
-
- hv = newHV ();
-
- switch (info_type) {
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- {
- gint i, n_fields =
- g_struct_info_get_n_fields ((GIStructInfo *) info);
- for (i = 0; i < n_fields; i++) {
- GIFieldInfo *field_info;
- SV *sv;
- field_info =
- g_struct_info_get_field ((GIStructInfo *) info, i);
- /* FIXME: Check GIFieldInfoFlags. */
- /* FIXME: Is it right to use GI_TRANSFER_NOTHING
- * here? */
- sv = get_field (field_info, pointer,
- GI_TRANSFER_NOTHING);
- if (gperl_sv_is_defined (sv)) {
- const gchar *name;
- name = g_base_info_get_name (
- (GIBaseInfo *) field_info);
- gperl_hv_take_sv (hv, name, strlen (name), sv);
- }
- g_base_info_unref ((GIBaseInfo *) field_info);
- }
- break;
- }
-
- case GI_INFO_TYPE_UNION:
- ccroak ("%s: unions not handled yet", G_STRFUNC);
-
- default:
- ccroak ("%s: unhandled info type %d", G_STRFUNC, info_type);
- }
-
- if (own) {
- /* FIXME: Is it correct to just call g_free here? What if the
- * thing was allocated via GSlice? */
- g_free (pointer);
- }
-
- return newRV_noinc ((SV *) hv);
-}
-
-static gpointer
-sv_to_struct (GITransfer transfer,
- GIBaseInfo * info,
- GIInfoType info_type,
- SV * sv)
-{
- HV *hv;
- gsize size = 0;
- GITransfer field_transfer;
- gpointer pointer = NULL;
-
- dwarn ("%s: sv %p\n", G_STRFUNC, sv);
-
- if (!gperl_sv_is_hash_ref (sv))
- ccroak ("need a hash ref to convert to struct of type %s",
- g_base_info_get_name (info));
- hv = (HV *) SvRV (sv);
-
- switch (info_type) {
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- size = g_struct_info_get_size ((GIStructInfo *) info);
- break;
- case GI_INFO_TYPE_UNION:
- size = g_union_info_get_size ((GIStructInfo *) info);
- break;
- default:
- g_assert_not_reached ();
- }
-
- dwarn (" size: %d\n", size);
-
- field_transfer = GI_TRANSFER_NOTHING;
- dwarn (" transfer: %d\n", transfer);
- switch (transfer) {
- case GI_TRANSFER_EVERYTHING:
- field_transfer = GI_TRANSFER_EVERYTHING;
- /* fall through */
- case GI_TRANSFER_CONTAINER:
- /* FIXME: What if there's a special allocator for the record?
- * Like GSlice? */
- pointer = g_malloc0 (size);
- break;
-
- default:
- pointer = gperl_alloc_temp (size);
- break;
- }
-
- switch (info_type) {
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- {
- gint i, n_fields =
- g_struct_info_get_n_fields ((GIStructInfo *) info);
- for (i = 0; i < n_fields; i++) {
- GIFieldInfo *field_info;
- const gchar *field_name;
- SV **svp;
- field_info = g_struct_info_get_field (
- (GIStructInfo *) info, i);
- /* FIXME: Check GIFieldInfoFlags. */
- field_name = g_base_info_get_name (
- (GIBaseInfo *) field_info);
- svp = hv_fetch (hv, field_name, strlen (field_name), 0);
- if (svp && gperl_sv_is_defined (*svp)) {
- set_field (field_info, pointer,
- field_transfer, *svp);
- }
- g_base_info_unref ((GIBaseInfo *) field_info);
- }
- break;
- }
-
- case GI_INFO_TYPE_UNION:
- ccroak ("%s: unions not handled yet", G_STRFUNC);
-
- default:
- ccroak ("%s: unhandled info type %d", G_STRFUNC, info_type);
- }
-
- return pointer;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static SV *
-array_to_sv (GITypeInfo *info,
- gpointer pointer,
- GITransfer transfer,
- GPerlI11nInvocationInfo *iinfo)
-{
- GITypeInfo *param_info;
- gboolean is_zero_terminated;
- gsize item_size;
- GITransfer item_transfer;
- gssize length, i;
- AV *av;
-
- if (pointer == NULL) {
- return &PL_sv_undef;
- }
-
- is_zero_terminated = g_type_info_is_zero_terminated (info);
- param_info = g_type_info_get_param_type (info, 0);
- item_size = size_of_type_info (param_info);
-
- /* FIXME: What about an array containing arrays of strings, where the
- * outer array is GI_TRANSFER_EVERYTHING but the inner arrays are
- * GI_TRANSFER_CONTAINER? */
- item_transfer = transfer == GI_TRANSFER_EVERYTHING
- ? GI_TRANSFER_EVERYTHING
- : GI_TRANSFER_NOTHING;
-
- if (is_zero_terminated) {
- length = g_strv_length (pointer);
- } else {
- length = g_type_info_get_array_fixed_size (info);
- if (length < 0) {
- guint length_pos = g_type_info_get_array_length (info);
- g_assert (iinfo != NULL);
- /* FIXME: Is it OK to always use v_size here? */
- length = iinfo->aux_args[length_pos].v_size;
- }
- }
-
- if (length < 0) {
- ccroak ("Could not determine the length of the array");
- }
-
- av = newAV ();
-
- dwarn (" C array: pointer %p, length %d, item size %d, "
- "param_info %p with type tag %d (%s)\n",
- pointer,
- length,
- item_size,
- param_info,
- g_type_info_get_tag (param_info),
- g_type_tag_to_string (g_type_info_get_tag (param_info)));
-
- for (i = 0; i < length; i++) {
- GIArgument *arg;
- SV *value;
- arg = pointer + i * item_size;
- value = arg_to_sv (arg, param_info, item_transfer, iinfo);
- if (value)
- av_push (av, value);
- }
-
- if (transfer >= GI_TRANSFER_CONTAINER)
- g_free (pointer);
-
- g_base_info_unref ((GIBaseInfo *) param_info);
-
- return newRV_noinc ((SV *) av);
-}
-
-static gpointer
-sv_to_array (GITransfer transfer,
- GITypeInfo *type_info,
- SV *sv,
- GPerlI11nInvocationInfo *iinfo)
-{
- AV *av;
- GITransfer item_transfer;
- GITypeInfo *param_info;
- GITypeTag param_tag;
- gint i, length, length_pos;
- GPerlI11nArrayInfo *array_info = NULL;
- GArray *array;
- gboolean is_zero_terminated = FALSE;
- gsize item_size;
- gboolean need_struct_value_semantics;
-
- dwarn ("%s: sv %p\n", G_STRFUNC, sv);
-
- /* Add an array info entry even before the undef check so that the
- * corresponding length arg is set to zero later by
- * handle_automatic_arg. */
- length_pos = g_type_info_get_array_length (type_info);
- if (length_pos >= 0) {
- array_info = g_new0 (GPerlI11nArrayInfo, 1);
- array_info->length_pos = length_pos;
- array_info->length = 0;
- iinfo->array_infos = g_slist_prepend (iinfo->array_infos, array_info);
- }
-
- if (sv == &PL_sv_undef)
- return NULL;
-
- if (!gperl_sv_is_array_ref (sv))
- ccroak ("need an array ref to convert to GArray");
-
- av = (AV *) SvRV (sv);
-
- item_transfer = transfer == GI_TRANSFER_CONTAINER
- ? GI_TRANSFER_NOTHING
- : transfer;
-
- param_info = g_type_info_get_param_type (type_info, 0);
- param_tag = g_type_info_get_tag (param_info);
- dwarn (" GArray: param_info %p with type tag %d (%s) and transfer %d\n",
- param_info, param_tag,
- g_type_tag_to_string (g_type_info_get_tag (param_info)),
- transfer);
-
- is_zero_terminated = g_type_info_is_zero_terminated (type_info);
- item_size = size_of_type_info (param_info);
- length = av_len (av) + 1;
- array = g_array_sized_new (is_zero_terminated, FALSE, item_size, length);
-
- /* Arrays containing non-basic types as non-pointers need to be treated
- * specially. Prime example: GValue *values = g_new0 (GValue, n);
- */
- need_struct_value_semantics =
- /* is a compound type, and... */
- !G_TYPE_TAG_IS_BASIC (param_tag) &&
- /* ... a non-pointer is wanted */
- !g_type_info_is_pointer (param_info);
- for (i = 0; i < length; i++) {
- SV **svp;
- svp = av_fetch (av, i, 0);
- if (svp && gperl_sv_is_defined (*svp)) {
- GIArgument arg;
-
- dwarn (" converting SV %p\n", *svp);
- /* FIXME: Is it OK to always allow undef here? */
- sv_to_arg (*svp, &arg, NULL, param_info,
- item_transfer, TRUE, NULL);
-
- if (need_struct_value_semantics) {
- /* Copy from the memory area pointed to by
- * arg.v_pointer. */
- g_array_insert_vals (array, i, arg.v_pointer, 1);
- } else {
- /* Copy from &arg, i.e. the memory area that is
- * arg. */
- g_array_insert_val (array, i, arg);
- }
- }
- }
+static gpointer handle_callback_arg (GIArgInfo * arg_info, GITypeInfo * type_info, SV * sv, GPerlI11nInvocationInfo * invocation_info);
+static gpointer handle_void_arg (SV * sv, GPerlI11nInvocationInfo * invocation_info);
- dwarn (" -> array %p of size %d\n", array, array->len);
+static SV * struct_to_sv (GIBaseInfo* info, GIInfoType info_type, gpointer pointer, gboolean own);
+static gpointer sv_to_struct (GITransfer transfer, GIBaseInfo * info, GIInfoType info_type, SV * sv);
- if (length_pos >= 0) {
- array_info->length = length;
- }
+static SV * array_to_sv (GITypeInfo *info, gpointer pointer, GITransfer transfer, GPerlI11nInvocationInfo *iinfo);
+static gpointer sv_to_array (GITransfer transfer, GITypeInfo *type_info, SV *sv, GPerlI11nInvocationInfo *iinfo);
- g_base_info_unref ((GIBaseInfo *) param_info);
+static SV * glist_to_sv (GITypeInfo* info, gpointer pointer, GITransfer transfer);
+static gpointer sv_to_glist (GITransfer transfer, GITypeInfo * type_info, SV * sv);
- return g_array_free (array, FALSE);
-}
+static SV * ghash_to_sv (GITypeInfo *info, gpointer pointer, GITransfer transfer);
+static gpointer sv_to_ghash (GITransfer transfer, GITypeInfo *type_info, SV *sv);
-/* ------------------------------------------------------------------------- */
+static void raw_to_arg (gpointer raw, GIArgument *arg, GITypeInfo *info);
+static void arg_to_raw (GIArgument *arg, gpointer raw, GITypeInfo *info);
-static SV *
-glist_to_sv (GITypeInfo* info,
- gpointer pointer,
- GITransfer transfer)
-{
- GITypeInfo *param_info;
- GITransfer item_transfer;
- gboolean is_slist;
- GSList *i;
- AV *av;
- SV *value;
-
- if (pointer == NULL) {
- return &PL_sv_undef;
- }
+/* sizes */
+static gsize size_of_type_tag (GITypeTag type_tag);
+static gsize size_of_interface (GITypeInfo *type_info);
+static gsize size_of_type_info (GITypeInfo *type_info);
- /* FIXME: What about an array containing arrays of strings, where the
- * outer array is GI_TRANSFER_EVERYTHING but the inner arrays are
- * GI_TRANSFER_CONTAINER? */
- item_transfer = transfer == GI_TRANSFER_EVERYTHING
- ? GI_TRANSFER_EVERYTHING
- : GI_TRANSFER_NOTHING;
-
- param_info = g_type_info_get_param_type (info, 0);
- dwarn (" G(S)List: pointer %p, param_info %p with type tag %d (%s)\n",
- pointer,
- param_info,
- g_type_info_get_tag (param_info),
- g_type_tag_to_string (g_type_info_get_tag (param_info)));
-
- is_slist = GI_TYPE_TAG_GSLIST == g_type_info_get_tag (info);
-
- av = newAV ();
- for (i = pointer; i; i = i->next) {
- GIArgument arg = {0,};
- dwarn (" converting pointer %p\n", i->data);
- arg.v_pointer = i->data;
- value = arg_to_sv (&arg, param_info, item_transfer, NULL);
- if (value)
- av_push (av, value);
- }
+/* fields */
+static void store_fields (HV *fields, GIBaseInfo *info, GIInfoType info_type);
+static SV * get_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer);
+static void set_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer, SV *value);
- if (transfer >= GI_TRANSFER_CONTAINER) {
- if (is_slist)
- g_slist_free (pointer);
- else
- g_list_free (pointer);
- }
+/* methods */
+static void store_methods (HV *namespaced_functions, GIBaseInfo *info, GIInfoType info_type);
- g_base_info_unref ((GIBaseInfo *) param_info);
-
- return newRV_noinc ((SV *) av);
-}
-
-static gpointer
-sv_to_glist (GITransfer transfer, GITypeInfo * type_info, SV * sv)
-{
- AV *av;
- GITransfer item_transfer;
- gpointer list = NULL;
- GITypeInfo *param_info;
- gboolean is_slist;
- gint i, length;
-
- dwarn ("%s: sv %p\n", G_STRFUNC, sv);
-
- if (sv == &PL_sv_undef)
- return NULL;
-
- if (!gperl_sv_is_array_ref (sv))
- ccroak ("need an array ref to convert to GList");
- av = (AV *) SvRV (sv);
-
- item_transfer = GI_TRANSFER_NOTHING;
- switch (transfer) {
- case GI_TRANSFER_EVERYTHING:
- item_transfer = GI_TRANSFER_EVERYTHING;
- break;
- case GI_TRANSFER_CONTAINER:
- /* nothing special to do */
- break;
- case GI_TRANSFER_NOTHING:
- /* FIXME: need to free list after call */
- break;
- }
+/* interface vfuncs */
+static void generic_interface_init (gpointer iface, gpointer data);
+static void generic_interface_finalize (gpointer iface, gpointer data);
- param_info = g_type_info_get_param_type (type_info, 0);
- dwarn (" G(S)List: param_info %p with type tag %d (%s) and transfer %d\n",
- param_info,
- g_type_info_get_tag (param_info),
- g_type_tag_to_string (g_type_info_get_tag (param_info)),
- transfer);
-
- is_slist = GI_TYPE_TAG_GSLIST == g_type_info_get_tag (type_info);
-
- length = av_len (av) + 1;
- for (i = 0; i < length; i++) {
- SV **svp;
- svp = av_fetch (av, i, 0);
- if (svp && gperl_sv_is_defined (*svp)) {
- GIArgument arg;
- dwarn (" converting SV %p\n", *svp);
- /* FIXME: Is it OK to always allow undef here? */
- sv_to_arg (*svp, &arg, NULL, param_info,
- item_transfer, TRUE, NULL);
- /* ENHANCEME: Could use g_[s]list_prepend and
- * later _reverse for efficiency. */
- if (is_slist)
- list = g_slist_append (list, arg.v_pointer);
- else
- list = g_list_append (list, arg.v_pointer);
- }
- }
-
- dwarn (" -> list %p of length %d\n", list, g_list_length (list));
-
- g_base_info_unref ((GIBaseInfo *) param_info);
+/* object vfuncs */
+static void generic_class_init (GIObjectInfo *info, const gchar *target_package, gpointer class);
- return list;
-}
-
-static SV *
-ghash_to_sv (GITypeInfo *info,
- gpointer pointer,
- GITransfer transfer)
-{
- GITypeInfo *key_param_info, *value_param_info;
-#ifdef NOISY
- GITypeTag key_type_tag, value_type_tag;
-#endif
- gpointer key_p, value_p;
- GITransfer item_transfer;
- GHashTableIter iter;
- HV *hv;
-
- if (pointer == NULL) {
- return &PL_sv_undef;
- }
-
- item_transfer = transfer == GI_TRANSFER_EVERYTHING
- ? GI_TRANSFER_EVERYTHING
- : GI_TRANSFER_NOTHING;
+/* FIXME: this is not safe if we want to support unloading G:O:I. */
+#define VFUNC_TARGET_PACKAGE_QUARK g_quark_from_static_string ("__gperl_vfunc_target_package")
+#define VFUNC_PERL_TYPE_QUARK g_quark_from_static_string ("__gperl_vfunc_perl_type")
- key_param_info = g_type_info_get_param_type (info, 0);
- value_param_info = g_type_info_get_param_type (info, 1);
+/* misc. */
+#define ccroak(...) call_carp_croak (form (__VA_ARGS__));
+static void call_carp_croak (const char *msg);
+/* #define NOISY */
#ifdef NOISY
- key_type_tag = g_type_info_get_tag (key_param_info);
- value_type_tag = g_type_info_get_tag (value_param_info);
-#endif
-
- dwarn (" GHashTable: pointer %p\n"
- " key type tag %d (%s)\n"
- " value type tag %d (%s)\n",
- pointer,
- key_type_tag, g_type_tag_to_string (key_type_tag),
- value_type_tag, g_type_tag_to_string (value_type_tag));
-
- hv = newHV ();
-
- g_hash_table_iter_init (&iter, pointer);
- while (g_hash_table_iter_next (&iter, &key_p, &value_p)) {
- GIArgument arg = { 0, };
- SV *key_sv, *value_sv;
-
- dwarn (" converting key pointer %p\n", key_p);
- arg.v_pointer = key_p;
- key_sv = arg_to_sv (&arg, key_param_info, item_transfer, NULL);
- if (key_sv == NULL)
- break;
-
- dwarn (" converting value pointer %p\n", value_p);
- arg.v_pointer = value_p;
- value_sv = arg_to_sv (&arg, value_param_info, item_transfer, NULL);
- if (value_sv == NULL)
- break;
-
- (void) hv_store_ent (hv, key_sv, value_sv, 0);
- }
-
- g_base_info_unref ((GIBaseInfo *) key_param_info);
- g_base_info_unref ((GIBaseInfo *) value_param_info);
-
- return newRV_noinc ((SV *) hv);
-}
-
-static gpointer
-sv_to_ghash (GITransfer transfer,
- GITypeInfo *type_info,
- SV *sv)
-{
- HV *hv;
- HE *he;
- GITransfer item_transfer;
- gpointer hash;
- GITypeInfo *key_param_info, *value_param_info;
- GITypeTag key_type_tag;
- GHashFunc hash_func;
- GEqualFunc equal_func;
- I32 n_keys;
-
- dwarn ("%s: sv %p\n", G_STRFUNC, sv);
-
- if (sv == &PL_sv_undef)
- return NULL;
-
- if (!gperl_sv_is_hash_ref (sv))
- ccroak ("need an hash ref to convert to GHashTable");
-
- hv = (HV *) SvRV (sv);
-
- item_transfer = GI_TRANSFER_NOTHING;
- switch (transfer) {
- case GI_TRANSFER_EVERYTHING:
- item_transfer = GI_TRANSFER_EVERYTHING;
- break;
- case GI_TRANSFER_CONTAINER:
- /* nothing special to do */
- break;
- case GI_TRANSFER_NOTHING:
- /* FIXME: need to free hash after call */
- break;
- }
-
- key_param_info = g_type_info_get_param_type (type_info, 0);
- value_param_info = g_type_info_get_param_type (type_info, 1);
-
- key_type_tag = g_type_info_get_tag (key_param_info);
-
- switch (key_type_tag)
- {
- case GI_TYPE_TAG_FILENAME:
- case GI_TYPE_TAG_UTF8:
- hash_func = g_str_hash;
- equal_func = g_str_equal;
- break;
-
- default:
- hash_func = NULL;
- equal_func = NULL;
- break;
- }
-
- dwarn (" GHashTable with transfer %d\n"
- " key_param_info %p with type tag %d (%s)\n"
- " value_param_info %p with type tag %d (%s)\n",
- transfer,
- key_param_info,
- g_type_info_get_tag (key_param_info),
- g_type_tag_to_string (g_type_info_get_tag (key_param_info)),
- value_param_info,
- g_type_info_get_tag (value_param_info),
- g_type_tag_to_string (g_type_info_get_tag (value_param_info)));
-
- hash = g_hash_table_new (hash_func, equal_func);
-
- n_keys = hv_iterinit (hv);
- if (n_keys == 0)
- goto out;
-
- while ((he = hv_iternext (hv)) != NULL) {
- SV *sv;
- GIArgument arg = { 0, };
- gpointer key_p, value_p;
-
- key_p = value_p = NULL;
-
- sv = hv_iterkeysv (he);
- if (sv && gperl_sv_is_defined (sv)) {
- dwarn (" converting key SV %p\n", sv);
- /* FIXME: Is it OK to always allow undef here? */
- sv_to_arg (sv, &arg, NULL, key_param_info,
- item_transfer, TRUE, NULL);
- key_p = arg.v_pointer;
- }
-
- sv = hv_iterval (hv, he);
- if (sv && gperl_sv_is_defined (sv)) {
- dwarn (" converting value SV %p\n", sv);
- sv_to_arg (sv, &arg, NULL, key_param_info,
- item_transfer, TRUE, NULL);
- value_p = arg.v_pointer;
- }
-
- if (key_p != NULL && value_p != NULL)
- g_hash_table_insert (hash, key_p, value_p);
- }
-
-out:
- dwarn (" -> hash %p of size %d\n", hash, g_hash_table_size (hash));
-
- g_base_info_unref ((GIBaseInfo *) key_param_info);
- g_base_info_unref ((GIBaseInfo *) value_param_info);
-
- return hash;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void
-sv_to_interface (GIArgInfo * arg_info,
- GITypeInfo * type_info,
- SV * sv,
- GIArgument * arg,
- GPerlI11nInvocationInfo * invocation_info)
-{
- GIBaseInfo *interface;
- GIInfoType info_type;
-
- interface = g_type_info_get_interface (type_info);
- if (!interface)
- ccroak ("Could not convert sv %p to pointer", sv);
- info_type = g_base_info_get_type (interface);
-
- dwarn (" interface %p (%s) of type %d\n",
- interface, g_base_info_get_name (interface), info_type);
-
- switch (info_type) {
- case GI_INFO_TYPE_OBJECT:
- case GI_INFO_TYPE_INTERFACE:
- /* FIXME: Check transfer setting. */
- arg->v_pointer = gperl_get_object (sv);
- break;
-
- case GI_INFO_TYPE_UNION:
- case GI_INFO_TYPE_STRUCT:
- case GI_INFO_TYPE_BOXED:
- {
- /* FIXME: What about pass-by-value here? */
- GType type = g_registered_type_info_get_g_type (
- (GIRegisteredTypeInfo *) interface);
- if (!type || type == G_TYPE_NONE) {
- GITransfer transfer = arg_info
- ? g_arg_info_get_ownership_transfer (arg_info)
- : GI_TRANSFER_NOTHING;
- dwarn (" unboxed type\n");
- arg->v_pointer = sv_to_struct (transfer,
- interface,
- info_type,
- sv);
- } else if (type == G_TYPE_CLOSURE) {
- /* FIXME: User cannot supply user data. */
- dwarn (" closure type\n");
- arg->v_pointer = gperl_closure_new (sv, NULL, FALSE);
- } else if (type == G_TYPE_VALUE) {
- dwarn (" value type\n");
- arg->v_pointer = SvGValueWrapper (sv);
- if (!arg->v_pointer)
- ccroak ("Cannot convert arbitrary SV to GValue");
- } else {
- dwarn (" boxed type: %s (%d)\n",
- g_type_name (type), type);
- /* FIXME: Check transfer setting. */
- arg->v_pointer = gperl_get_boxed_check (sv, type);
- }
- break;
- }
-
- case GI_INFO_TYPE_ENUM:
- {
- GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
- /* FIXME: Check storage type? */
- arg->v_long = gperl_convert_enum (type, sv);
- break;
- }
-
- case GI_INFO_TYPE_FLAGS:
- {
- GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
- /* FIXME: Check storage type? */
- arg->v_long = gperl_convert_flags (type, sv);
- break;
- }
-
- case GI_INFO_TYPE_CALLBACK:
- arg->v_pointer = handle_callback_arg (arg_info, type_info, sv,
- invocation_info);
- break;
-
- default:
- ccroak ("sv_to_interface: Don't know how to handle info type %d", info_type);
- }
-
- g_base_info_unref ((GIBaseInfo *) interface);
-}
-
-static SV *
-interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own)
-{
- GIBaseInfo *interface;
- GIInfoType info_type;
- SV *sv = NULL;
-
- dwarn (" interface_to_sv: arg %p, info %p\n",
- arg, info);
-
- interface = g_type_info_get_interface (info);
- if (!interface)
- ccroak ("Could not convert arg %p to SV", arg);
- info_type = g_base_info_get_type (interface);
- dwarn (" info type: %d (%s)\n", info_type, g_info_type_to_string (info_type));
-
- switch (info_type) {
- case GI_INFO_TYPE_OBJECT:
- case GI_INFO_TYPE_INTERFACE:
- sv = gperl_new_object (arg->v_pointer, own);
- break;
-
- case GI_INFO_TYPE_UNION:
- case GI_INFO_TYPE_STRUCT:
- case GI_INFO_TYPE_BOXED:
- {
- /* FIXME: What about pass-by-value here? */
- GType type;
- type = g_registered_type_info_get_g_type (
- (GIRegisteredTypeInfo *) interface);
- if (!type || type == G_TYPE_NONE) {
- dwarn (" unboxed type\n");
- sv = struct_to_sv (interface, info_type, arg->v_pointer, own);
- } else if (type == G_TYPE_VALUE) {
- dwarn (" value type\n");
- sv = gperl_sv_from_value (arg->v_pointer);
- /* FIXME: Check 'own'. */
- } else {
- dwarn (" boxed type: %d (%s)\n",
- type, g_type_name (type));
- sv = gperl_new_boxed (arg->v_pointer, type, own);
- }
- break;
- }
-
- case GI_INFO_TYPE_ENUM:
- {
- GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
- /* FIXME: Is it right to just use v_long here? */
- sv = gperl_convert_back_enum (type, arg->v_long);
- break;
- }
-
- case GI_INFO_TYPE_FLAGS:
- {
- GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
- /* FIXME: Is it right to just use v_long here? */
- sv = gperl_convert_back_flags (type, arg->v_long);
- break;
- }
-
- default:
- ccroak ("interface_to_sv: Don't know how to handle info type %d", info_type);
- }
-
- g_base_info_unref ((GIBaseInfo *) interface);
-
- return sv;
-}
-
-static gpointer
-instance_sv_to_pointer (GICallableInfo *info, SV *sv)
-{
- // We do *not* own container.
- GIBaseInfo *container = g_base_info_get_container (info);
- GIInfoType info_type = g_base_info_get_type (container);
- gpointer pointer = NULL;
-
- /* FIXME: Much of this code is duplicated in sv_to_interface. */
-
- dwarn (" instance_sv_to_pointer: container name: %s, info type: %d\n",
- g_base_info_get_name (container),
- info_type);
-
- switch (info_type) {
- case GI_INFO_TYPE_OBJECT:
- case GI_INFO_TYPE_INTERFACE:
- pointer = gperl_get_object (sv);
- dwarn (" -> object pointer: %p\n", pointer);
- break;
-
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- case GI_INFO_TYPE_UNION:
- {
- GType type = g_registered_type_info_get_g_type (
- (GIRegisteredTypeInfo *) container);
- if (!type || type == G_TYPE_NONE) {
- dwarn (" unboxed type\n");
- pointer = sv_to_struct (GI_TRANSFER_NOTHING,
- container,
- info_type,
- sv);
- } else {
- dwarn (" boxed type: %s (%d)\n",
- g_type_name (type), type);
- pointer = gperl_get_boxed_check (sv, type);
- }
- dwarn (" -> boxed pointer: %p\n", pointer);
- break;
- }
-
- default:
- ccroak ("instance_sv_to_pointer: Don't know how to handle info type %d", info_type);
- }
-
- return pointer;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* transfer and may_be_null can be gotten from arg_info, but sv_to_arg is also
- * called from places which don't have access to a GIArgInfo. */
-static void
-sv_to_arg (SV * sv,
- GIArgument * arg,
- GIArgInfo * arg_info,
- GITypeInfo * type_info,
- GITransfer transfer,
- gboolean may_be_null,
- GPerlI11nInvocationInfo * invocation_info)
-{
- GITypeTag tag = g_type_info_get_tag (type_info);
-
- memset (arg, 0, sizeof (GIArgument));
-
- if (!gperl_sv_is_defined (sv))
- /* Interfaces and void types need to be able to handle undef
- * separately. */
- if (!may_be_null && tag != GI_TYPE_TAG_INTERFACE
- && tag != GI_TYPE_TAG_VOID)
- ccroak ("undefined value for mandatory argument '%s' encountered",
- g_base_info_get_name ((GIBaseInfo *) arg_info));
-
- switch (tag) {
- case GI_TYPE_TAG_VOID:
- arg->v_pointer = handle_void_arg (sv, invocation_info);
- break;
-
- case GI_TYPE_TAG_BOOLEAN:
- arg->v_boolean = SvTRUE (sv);
- break;
-
- case GI_TYPE_TAG_INT8:
- arg->v_int8 = (gint8) SvIV (sv);
- break;
-
- case GI_TYPE_TAG_UINT8:
- arg->v_uint8 = (guint8) SvUV (sv);
- break;
-
- case GI_TYPE_TAG_INT16:
- arg->v_int16 = (gint16) SvIV (sv);
- break;
-
- case GI_TYPE_TAG_UINT16:
- arg->v_uint16 = (guint16) SvUV (sv);
- break;
-
- case GI_TYPE_TAG_INT32:
- arg->v_int32 = (gint32) SvIV (sv);
- break;
-
- case GI_TYPE_TAG_UINT32:
- arg->v_uint32 = (guint32) SvUV (sv);
- break;
-
- case GI_TYPE_TAG_INT64:
- arg->v_int64 = SvGInt64 (sv);
- break;
-
- case GI_TYPE_TAG_UINT64:
- arg->v_uint64 = SvGUInt64 (sv);
- break;
-
- case GI_TYPE_TAG_FLOAT:
- arg->v_float = (gfloat) SvNV (sv);
- break;
-
- case GI_TYPE_TAG_DOUBLE:
- arg->v_double = SvNV (sv);
- break;
-
- case GI_TYPE_TAG_UNICHAR:
- arg->v_uint32 = g_utf8_get_char (SvGChar (sv));
- break;
-
- case GI_TYPE_TAG_GTYPE:
- /* GType == gsize */
- arg->v_size = gperl_type_from_package (SvPV_nolen (sv));
- if (!arg->v_size)
- arg->v_size = g_type_from_name (SvPV_nolen (sv));
- break;
-
- case GI_TYPE_TAG_ARRAY:
- arg->v_pointer = sv_to_array (transfer, type_info, sv, invocation_info);
- break;
-
- case GI_TYPE_TAG_INTERFACE:
- dwarn (" type %p -> interface\n", type_info);
- sv_to_interface (arg_info, type_info, sv, arg,
- invocation_info);
- break;
-
- case GI_TYPE_TAG_GLIST:
- case GI_TYPE_TAG_GSLIST:
- arg->v_pointer = sv_to_glist (transfer, type_info, sv);
- break;
-
- case GI_TYPE_TAG_GHASH:
- arg->v_pointer = sv_to_ghash (transfer, type_info, sv);
- break;
-
- case GI_TYPE_TAG_ERROR:
- ccroak ("FIXME - A GError as an in/inout arg? Should never happen!");
- break;
-
- case GI_TYPE_TAG_UTF8:
- arg->v_string = gperl_sv_is_defined (sv) ? SvGChar (sv) : NULL;
- if (transfer >= GI_TRANSFER_CONTAINER)
- arg->v_string = g_strdup (arg->v_string);
- break;
-
- case GI_TYPE_TAG_FILENAME:
- /* FIXME: Is it correct to use gperl_filename_from_sv here? */
- arg->v_string = gperl_sv_is_defined (sv) ? gperl_filename_from_sv (sv) : NULL;
- if (transfer >= GI_TRANSFER_CONTAINER)
- arg->v_string = g_strdup (arg->v_string);
- break;
-
- default:
- ccroak ("Unhandled info tag %d in sv_to_arg", tag);
- }
-}
-
-static SV *
-arg_to_sv (GIArgument * arg,
- GITypeInfo * info,
- GITransfer transfer,
- GPerlI11nInvocationInfo *iinfo)
-{
- GITypeTag tag = g_type_info_get_tag (info);
- gboolean own = transfer >= GI_TRANSFER_CONTAINER;
-
- dwarn (" arg_to_sv: info %p with type tag %d (%s)\n",
- info, tag, g_type_tag_to_string (tag));
-
- switch (tag) {
- case GI_TYPE_TAG_VOID:
- dwarn (" argument with no type information -> undef\n");
- return &PL_sv_undef;
-
- case GI_TYPE_TAG_BOOLEAN:
- return boolSV (arg->v_boolean);
-
- case GI_TYPE_TAG_INT8:
- return newSViv (arg->v_int8);
-
- case GI_TYPE_TAG_UINT8:
- return newSVuv (arg->v_uint8);
-
- case GI_TYPE_TAG_INT16:
- return newSViv (arg->v_int16);
-
- case GI_TYPE_TAG_UINT16:
- return newSVuv (arg->v_uint16);
-
- case GI_TYPE_TAG_INT32:
- return newSViv (arg->v_int32);
-
- case GI_TYPE_TAG_UINT32:
- return newSVuv (arg->v_uint32);
-
- case GI_TYPE_TAG_INT64:
- return newSVGInt64 (arg->v_int64);
-
- case GI_TYPE_TAG_UINT64:
- return newSVGUInt64 (arg->v_uint64);
-
- case GI_TYPE_TAG_FLOAT:
- return newSVnv (arg->v_float);
-
- case GI_TYPE_TAG_DOUBLE:
- return newSVnv (arg->v_double);
-
- case GI_TYPE_TAG_UNICHAR:
- {
- SV *sv;
- gchar buffer[6];
- gint length = g_unichar_to_utf8 (arg->v_uint32, buffer);
- sv = newSVpv (buffer, length);
- SvUTF8_on (sv);
- return sv;
- }
-
- case GI_TYPE_TAG_GTYPE: {
- /* GType == gsize */
- const char *package = gperl_package_from_type (arg->v_size);
- if (!package)
- package = g_type_name (arg->v_size);
- return newSVpv (package, PL_na);
- }
-
- case GI_TYPE_TAG_ARRAY:
- return array_to_sv (info, arg->v_pointer, transfer, iinfo);
-
- case GI_TYPE_TAG_INTERFACE:
- return interface_to_sv (info, arg, own);
-
- case GI_TYPE_TAG_GLIST:
- case GI_TYPE_TAG_GSLIST:
- return glist_to_sv (info, arg->v_pointer, transfer);
-
- case GI_TYPE_TAG_GHASH:
- return ghash_to_sv (info, arg->v_pointer, transfer);
-
- case GI_TYPE_TAG_ERROR:
- ccroak ("FIXME - GI_TYPE_TAG_ERROR");
- break;
-
- case GI_TYPE_TAG_UTF8:
- {
- SV *sv = newSVGChar (arg->v_string);
- if (own)
- g_free (arg->v_string);
- return sv;
- }
-
- case GI_TYPE_TAG_FILENAME:
- {
- /* FIXME: Is it correct to use gperl_sv_from_filename here? */
- SV *sv = gperl_sv_from_filename (arg->v_string);
- if (own)
- g_free (arg->v_string);
- return sv;
- }
-
- default:
- ccroak ("Unhandled info tag %d in arg_to_sv", tag);
- }
-
- return NULL;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void
-handle_automatic_arg (guint pos,
- GIArgument * arg,
- GPerlI11nInvocationInfo * invocation_info)
-{
- GSList *l;
-
- /* array length */
- for (l = invocation_info->array_infos; l != NULL; l = l->next) {
- GPerlI11nArrayInfo *ainfo = l->data;
- if (pos == ainfo->length_pos) {
- dwarn (" setting automatic arg %d (array length) to %d\n",
- pos, ainfo->length);
- /* FIXME: Is it OK to always use v_size here? */
- arg->v_size = ainfo->length;
- return;
- }
- }
-
- /* callback destroy notify */
- for (l = invocation_info->callback_infos; l != NULL; l = l->next) {
- GPerlI11nCallbackInfo *cinfo = l->data;
- if (pos == cinfo->notify_pos) {
- dwarn (" setting automatic arg %d (destroy notify for calllback %p)\n",
- pos, cinfo);
- arg->v_pointer = release_callback;
- return;
- }
- }
-
- ccroak ("Could not handle automatic arg %d", pos);
-}
-
-/* ------------------------------------------------------------------------- */
-
-#define CAST_RAW(raw, type) (*((type *) raw))
-
-static void
-raw_to_arg (gpointer raw, GIArgument *arg, GITypeInfo *info)
-{
- GITypeTag tag = g_type_info_get_tag (info);
-
- switch (tag) {
- case GI_TYPE_TAG_VOID:
- /* do nothing */
- break;
-
- case GI_TYPE_TAG_BOOLEAN:
- arg->v_boolean = CAST_RAW (raw, gboolean);
- break;
-
- case GI_TYPE_TAG_INT8:
- arg->v_int8 = CAST_RAW (raw, gint8);
- break;
-
- case GI_TYPE_TAG_UINT8:
- arg->v_uint8 = CAST_RAW (raw, guint8);
- break;
-
- case GI_TYPE_TAG_INT16:
- arg->v_int16 = CAST_RAW (raw, gint16);
- break;
-
- case GI_TYPE_TAG_UINT16:
- arg->v_uint16 = CAST_RAW (raw, guint16);
- break;
-
- case GI_TYPE_TAG_INT32:
- arg->v_int32 = CAST_RAW (raw, gint32);
- break;
-
- case GI_TYPE_TAG_UINT32:
- arg->v_uint32 = CAST_RAW (raw, guint32);
- break;
-
- case GI_TYPE_TAG_INT64:
- arg->v_int64 = CAST_RAW (raw, gint64);
- break;
-
- case GI_TYPE_TAG_UINT64:
- arg->v_uint64 = CAST_RAW (raw, guint64);
- break;
-
- case GI_TYPE_TAG_FLOAT:
- arg->v_float = CAST_RAW (raw, gfloat);
- break;
-
- case GI_TYPE_TAG_DOUBLE:
- arg->v_double = CAST_RAW (raw, gdouble);
- break;
-
- case GI_TYPE_TAG_GTYPE:
- arg->v_size = CAST_RAW (raw, GType);
- break;
-
- case GI_TYPE_TAG_ARRAY:
- case GI_TYPE_TAG_INTERFACE:
- case GI_TYPE_TAG_GLIST:
- case GI_TYPE_TAG_GSLIST:
- case GI_TYPE_TAG_GHASH:
- case GI_TYPE_TAG_ERROR:
- arg->v_pointer = * (gpointer *) raw;
- break;
-
- case GI_TYPE_TAG_UTF8:
- case GI_TYPE_TAG_FILENAME:
- arg->v_string = * (gchar **) raw;
- break;
-
- default:
- ccroak ("Unhandled info tag %d in raw_to_arg", tag);
- }
-}
-
-static void
-arg_to_raw (GIArgument *arg, gpointer raw, GITypeInfo *info)
-{
- GITypeTag tag = g_type_info_get_tag (info);
-
- switch (tag) {
- case GI_TYPE_TAG_VOID:
- /* do nothing */
- break;
-
- case GI_TYPE_TAG_BOOLEAN:
- * (gboolean *) raw = arg->v_boolean;
- break;
-
- case GI_TYPE_TAG_INT8:
- * (gint8 *) raw = arg->v_int8;
- break;
-
- case GI_TYPE_TAG_UINT8:
- * (guint8 *) raw = arg->v_uint8;
- break;
-
- case GI_TYPE_TAG_INT16:
- * (gint16 *) raw = arg->v_int16;
- break;
-
- case GI_TYPE_TAG_UINT16:
- * (guint16 *) raw = arg->v_uint16;
- break;
-
- case GI_TYPE_TAG_INT32:
- * (gint32 *) raw = arg->v_int32;
- break;
-
- case GI_TYPE_TAG_UINT32:
- * (guint32 *) raw = arg->v_uint32;
- break;
-
- case GI_TYPE_TAG_INT64:
- * (gint64 *) raw = arg->v_int64;
- break;
-
- case GI_TYPE_TAG_UINT64:
- * (guint64 *) raw = arg->v_uint64;
- break;
-
- case GI_TYPE_TAG_FLOAT:
- * (gfloat *) raw = arg->v_float;
- break;
-
- case GI_TYPE_TAG_DOUBLE:
- * (gdouble *) raw = arg->v_double;
- break;
-
- case GI_TYPE_TAG_GTYPE:
- * (GType *) raw = arg->v_size;
- break;
-
- case GI_TYPE_TAG_ARRAY:
- case GI_TYPE_TAG_INTERFACE:
- case GI_TYPE_TAG_GLIST:
- case GI_TYPE_TAG_GSLIST:
- case GI_TYPE_TAG_GHASH:
- case GI_TYPE_TAG_ERROR:
- * (gpointer *) raw = arg->v_pointer;
- break;
-
- case GI_TYPE_TAG_UTF8:
- case GI_TYPE_TAG_FILENAME:
- * (gchar **) raw = arg->v_string;
- break;
-
- default:
- ccroak ("Unhandled info tag %d in arg_to_raw", tag);
- }
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* FIXME: this is not safe if we want to support unloading G:O:I. */
-#define VFUNC_TARGET_PACKAGE_QUARK g_quark_from_static_string ("__gperl_vfunc_target_package")
-#define VFUNC_PERL_TYPE_QUARK g_quark_from_static_string ("__gperl_vfunc_perl_type")
-
-static GPerlI11nCallbackInfo *
-create_callback_closure (GITypeInfo *cb_type, SV *code)
-{
- GPerlI11nCallbackInfo *info;
-
- info = g_new0 (GPerlI11nCallbackInfo, 1);
- info->interface =
- (GICallableInfo *) g_type_info_get_interface (cb_type);
- info->cif = g_new0 (ffi_cif, 1);
- info->closure =
- g_callable_info_prepare_closure (info->interface, info->cif,
- invoke_callback, info);
- /* FIXME: This should most likely use SvREFCNT_inc instead of
- * newSVsv. */
- info->code = newSVsv (code);
- info->sub_name = NULL;
- info->package_name = NULL;
-
-#ifdef PERL_IMPLICIT_CONTEXT
- info->priv = aTHX;
-#endif
-
- return info;
-}
-
-static void
-attach_callback_data (GPerlI11nCallbackInfo *info, SV *data)
-{
- info->data = newSVsv (data);
-}
-
-/* assumes ownership of sub_name and package_name */
-static GPerlI11nCallbackInfo *
-create_callback_closure_for_named_sub (GITypeInfo *cb_type, gchar *sub_name, gchar *package_name)
-{
- GPerlI11nCallbackInfo *info;
-
- info = g_new0 (GPerlI11nCallbackInfo, 1);
- info->interface =
- (GICallableInfo *) g_type_info_get_interface (cb_type);
- info->cif = g_new0 (ffi_cif, 1);
- info->closure =
- g_callable_info_prepare_closure (info->interface, info->cif,
- invoke_callback, info);
- info->sub_name = sub_name;
- info->package_name = package_name;
- info->code = NULL;
- info->data = NULL;
-
-#ifdef PERL_IMPLICIT_CONTEXT
- info->priv = aTHX;
+# define dwarn(...) warn(__VA_ARGS__)
+#else
+# define dwarn(...)
#endif
- return info;
-}
-
-static void
-invoke_callback (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata)
-{
- GPerlI11nCallbackInfo *info;
- GICallableInfo *cb_interface;
- int n_args, i;
- int in_inout;
- GITypeInfo *return_type;
- gboolean have_return_type;
- int n_return_values, n_returned;
- I32 context;
- dGPERL_CALLBACK_MARSHAL_SP;
-
- PERL_UNUSED_VAR (cif);
-
- /* unwrap callback info struct from userdata */
- info = (GPerlI11nCallbackInfo *) userdata;
- cb_interface = (GICallableInfo *) info->interface;
-
- /* set perl context */
- GPERL_CALLBACK_MARSHAL_INIT (info);
-
- ENTER;
- SAVETMPS;
-
- PUSHMARK (SP);
-
- /* find arguments; use type information from interface to find in and
- * in-out args and their types, count in-out and out args, and find
- * suitable converters; push in and in-out arguments onto the perl
- * stack */
- in_inout = 0;
- n_args = g_callable_info_get_n_args (cb_interface);
- for (i = 0; i < n_args; i++) {
- GIArgInfo *arg_info = g_callable_info_get_arg (cb_interface, i);
- GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
- GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
- GIDirection direction = g_arg_info_get_direction (arg_info);
-
- /* the closure argument, which we handle separately, is marked
- * by having get_closure == i */
- if (g_arg_info_get_closure (arg_info) == i) {
- g_base_info_unref ((GIBaseInfo *) arg_info);
- g_base_info_unref ((GIBaseInfo *) arg_type);
- continue;
- }
-
- dwarn ("arg info: %p\n"
- " direction: %d\n"
- " is return value: %d\n"
- " is optional: %d\n"
- " may be null: %d\n"
- " transfer: %d\n",
- arg_info,
- g_arg_info_get_direction (arg_info),
- g_arg_info_is_return_value (arg_info),
- g_arg_info_is_optional (arg_info),
- g_arg_info_may_be_null (arg_info),
- g_arg_info_get_ownership_transfer (arg_info));
-
- dwarn ("arg type: %p\n"
- " is pointer: %d\n"
- " tag: %d\n",
- arg_type,
- g_type_info_is_pointer (arg_type),
- g_type_info_get_tag (arg_type));
-
- if (direction == GI_DIRECTION_IN ||
- direction == GI_DIRECTION_INOUT)
- {
- GIArgument arg;
- raw_to_arg (args[i], &arg, arg_type);
- XPUSHs (sv_2mortal (arg_to_sv (&arg, arg_type, transfer, NULL)));
- }
-
- if (direction == GI_DIRECTION_INOUT ||
- direction == GI_DIRECTION_OUT)
- {
- in_inout++;
- }
-
- g_base_info_unref ((GIBaseInfo *) arg_info);
- g_base_info_unref ((GIBaseInfo *) arg_type);
- }
-
- /* push user data onto the Perl stack */
- if (info->data)
- XPUSHs (sv_2mortal (SvREFCNT_inc (info->data)));
-
- PUTBACK;
-
- /* put the target package name into the invocant so that the vfunc
- * fallback code knows whose parent to chain up to. */
- if (info->package_name) {
- GObject *object = * (GObject **) args[0];
- g_assert (G_IS_OBJECT (object));
- g_object_set_qdata (object, VFUNC_TARGET_PACKAGE_QUARK, info->package_name);
- }
-
- /* determine suitable Perl call context; return_type is freed further
- * below */
- return_type = g_callable_info_get_return_type (cb_interface);
- have_return_type =
- GI_TYPE_TAG_VOID != g_type_info_get_tag (return_type);
-
- context = G_VOID | G_DISCARD;
- if (have_return_type) {
- context = in_inout > 0
- ? G_ARRAY
- : G_SCALAR;
- } else {
- if (in_inout == 1) {
- context = G_SCALAR;
- } else if (in_inout > 1) {
- context = G_ARRAY;
- }
- }
-
- /* do the call, demand #in-out+#out+#return-value return values */
- n_return_values = have_return_type
- ? in_inout + 1
- : in_inout;
- n_returned = info->sub_name
- ? call_method (info->sub_name, context)
- : call_sv (info->code, context);
- if (n_return_values != 0 && n_returned != n_return_values) {
- ccroak ("callback returned %d values "
- "but is supposed to return %d values",
- n_returned, n_return_values);
- }
-
- if (info->package_name) {
- GObject *object = * (GObject **) args[0];
- g_assert (G_IS_OBJECT (object));
- g_object_set_qdata (object, VFUNC_TARGET_PACKAGE_QUARK, NULL);
- }
-
- SPAGAIN;
-
- /* convert in-out and out values and stuff them back into args */
- if (in_inout > 0) {
- SV **returned_values;
- int out_index;
-
- returned_values = g_new0 (SV *, in_inout);
-
- /* pop scalars off the stack and put them into the array;
- * reverse the order since POPs pops items off of the end of
- * the stack. */
- for (i = 0; i < in_inout; i++) {
- /* FIXME: Does this leak the sv? Should we check the
- * transfer setting? */
- returned_values[in_inout - i - 1] = newSVsv (POPs);
- }
-
- out_index = 0;
- for (i = 0; i < n_args; i++) {
- GIArgInfo *arg_info = g_callable_info_get_arg (cb_interface, i);
- GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
- GIDirection direction = g_arg_info_get_direction (arg_info);
-
- if (direction == GI_DIRECTION_INOUT ||
- direction == GI_DIRECTION_OUT)
- {
- GIArgument tmp_arg;
- GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
- gboolean may_be_null = g_arg_info_may_be_null (arg_info);
- sv_to_arg (returned_values[out_index], &tmp_arg,
- arg_info, arg_type,
- transfer, may_be_null, NULL);
- arg_to_raw (&tmp_arg, args[i], arg_type);
- out_index++;
- }
- }
-
- g_free (returned_values);
- }
-
- /* store return value in resp, if any */
- if (have_return_type) {
- GIArgument arg;
- GITypeInfo *type_info;
- GITransfer transfer;
- gboolean may_be_null;
-
- type_info = g_callable_info_get_return_type (cb_interface);
- transfer = g_callable_info_get_caller_owns (cb_interface);
- may_be_null = g_callable_info_may_return_null (cb_interface);
-
- dwarn ("ret type: %p\n"
- " is pointer: %d\n"
- " tag: %d\n",
- type_info,
- g_type_info_is_pointer (type_info),
- g_type_info_get_tag (type_info));
-
- /* FIXME: Does this leak the sv? */
- sv_to_arg (newSVsv (POPs), &arg, NULL, type_info,
- transfer, may_be_null, NULL);
- arg_to_raw (&arg, resp, type_info);
-
- g_base_info_unref ((GIBaseInfo *) type_info);
- }
-
- PUTBACK;
-
- g_base_info_unref ((GIBaseInfo *) return_type);
-
- FREETMPS;
- LEAVE;
-
- /* FIXME: We can't just free everything here because ffi will use parts
- * of this after we've returned.
- *
- * if (info->free_after_use) {
- * release_callback (info);
- * }
- *
- * Gjs uses a global list of callback infos instead and periodically
- * frees unused ones.
- */
-}
-
-static void
-release_callback (gpointer data)
-{
- GPerlI11nCallbackInfo *info = data;
- dwarn ("releasing callback info %p\n", info);
-
- if (info->cif)
- g_free (info->cif);
-
- if (info->closure)
- g_callable_info_free_closure (info->interface, info->closure);
-
- if (info->interface)
- g_base_info_unref ((GIBaseInfo*) info->interface);
-
- if (info->code)
- SvREFCNT_dec (info->code);
- if (info->data)
- SvREFCNT_dec (info->data);
- if (info->sub_name)
- g_free (info->sub_name);
- if (info->package_name)
- g_free (info->package_name);
-
- g_free (info);
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void
-store_methods (HV *namespaced_functions, GIBaseInfo *info, GIInfoType info_type)
-{
- const gchar *namespace;
- AV *av;
- gint i;
-
- namespace = g_base_info_get_name (info);
- av = newAV ();
-
- switch (info_type) {
- case GI_INFO_TYPE_OBJECT:
- {
- gint n_methods = g_object_info_get_n_methods (
- (GIObjectInfo *) info);
- for (i = 0; i < n_methods; i++) {
- GIFunctionInfo *function_info =
- g_object_info_get_method (
- (GIObjectInfo *) info, i);
- const gchar *function_name =
- g_base_info_get_name (
- (GIBaseInfo *) function_info);
- av_push (av, newSVpv (function_name, PL_na));
- g_base_info_unref ((GIBaseInfo *) function_info);
- }
- break;
- }
-
- case GI_INFO_TYPE_INTERFACE:
- {
- gint n_methods = g_interface_info_get_n_methods (
- (GIInterfaceInfo *) info);
- for (i = 0; i < n_methods; i++) {
- GIFunctionInfo *function_info =
- g_interface_info_get_method (
- (GIInterfaceInfo *) info, i);
- const gchar *function_name =
- g_base_info_get_name (
- (GIBaseInfo *) function_info);
- av_push (av, newSVpv (function_name, PL_na));
- g_base_info_unref ((GIBaseInfo *) function_info);
- }
- break;
- }
-
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- {
- gint n_methods = g_struct_info_get_n_methods (
- (GIStructInfo *) info);
- for (i = 0; i < n_methods; i++) {
- GIFunctionInfo *function_info =
- g_struct_info_get_method (
- (GIStructInfo *) info, i);
- const gchar *function_name =
- g_base_info_get_name (
- (GIBaseInfo *) function_info);
- av_push (av, newSVpv (function_name, PL_na));
- g_base_info_unref ((GIBaseInfo *) function_info);
- }
- break;
- }
-
- case GI_INFO_TYPE_UNION:
- {
- gint n_methods = g_union_info_get_n_methods ((GIUnionInfo *) info);
- for (i = 0; i < n_methods; i++) {
- GIFunctionInfo *function_info;
- const gchar *function_name;
-
- function_info = g_union_info_get_method ((GIUnionInfo *) info, i);
- function_name = g_base_info_get_name ((GIBaseInfo *) function_info);
-
- av_push (av, newSVpv (function_name, PL_na));
- g_base_info_unref ((GIBaseInfo *) function_info);
- }
- break;
- }
-
- default:
- ccroak ("store_methods: unsupported info type %d", info_type);
- }
-
- gperl_hv_take_sv (namespaced_functions, namespace, strlen (namespace),
- newRV_noinc ((SV *) av));
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void
-store_vfuncs (HV *objects_with_vfuncs, GIObjectInfo *info)
-{
- const gchar *object_name;
- AV *vfuncs_av;
- gint n_vfuncs, i;
-
- n_vfuncs = g_object_info_get_n_vfuncs (info);
- if (n_vfuncs <= 0)
- return;
-
- object_name = g_base_info_get_name (info);
- vfuncs_av = newAV ();
-
- for (i = 0; i < n_vfuncs; i++) {
- GIVFuncInfo *vfunc_info =
- g_object_info_get_vfunc (info, i);
- const gchar *vfunc_name =
- g_base_info_get_name (vfunc_info);
- gchar *vfunc_perl_name = g_ascii_strup (vfunc_name, -1);
- AV *vfunc_av = newAV ();
- av_push (vfunc_av, newSVpv (vfunc_name, PL_na));
- av_push (vfunc_av, newSVpv (vfunc_perl_name, PL_na));
- av_push (vfuncs_av, newRV_noinc ((SV *) vfunc_av));
- g_free (vfunc_perl_name);
- g_base_info_unref (vfunc_info);
- }
-
- gperl_hv_take_sv (objects_with_vfuncs, object_name, strlen (object_name),
- newRV_noinc ((SV *) vfuncs_av));
-}
-
/* ------------------------------------------------------------------------- */
-static void
-store_fields (HV *fields, GIBaseInfo *info, GIInfoType info_type)
-{
- const gchar *namespace;
- AV *av;
- gint i;
-
- namespace = g_base_info_get_name (info);
- av = newAV ();
-
- switch (info_type) {
- case GI_INFO_TYPE_BOXED:
- case GI_INFO_TYPE_STRUCT:
- {
- gint n_fields = g_struct_info_get_n_fields (
- (GIStructInfo *) info);
- for (i = 0; i < n_fields; i++) {
- GIFieldInfo *field_info;
- const gchar *field_name;
- field_info = g_struct_info_get_field ((GIStructInfo *) info, i);
- field_name = g_base_info_get_name ((GIBaseInfo *) field_info);
- av_push (av, newSVpv (field_name, PL_na));
- g_base_info_unref ((GIBaseInfo *) field_info);
- }
- break;
- }
-
- case GI_INFO_TYPE_UNION:
- {
- gint n_fields = g_union_info_get_n_fields ((GIUnionInfo *) info);
- for (i = 0; i < n_fields; i++) {
- GIFieldInfo *field_info;
- const gchar *field_name;
- field_info = g_union_info_get_field ((GIUnionInfo *) info, i);
- field_name = g_base_info_get_name ((GIBaseInfo *) field_info);
- av_push (av, newSVpv (field_name, PL_na));
- g_base_info_unref ((GIBaseInfo *) field_info);
- }
- break;
- }
-
- default:
- ccroak ("store_fields: unsupported info type %d", info_type);
- }
-
- gperl_hv_take_sv (fields, namespace, strlen (namespace),
- newRV_noinc ((SV *) av));
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void
-prepare_invocation_info (GPerlI11nInvocationInfo *iinfo,
- GICallableInfo *info,
- IV items,
- UV internal_stack_offset)
-{
- gboolean is_vfunc;
- guint i;
-
- is_vfunc = GI_IS_VFUNC_INFO (info);
-
- iinfo->stack_offset = internal_stack_offset;
-
- iinfo->is_constructor = is_vfunc
- ? FALSE
- : g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR;
- if (iinfo->is_constructor) {
- iinfo->stack_offset++;
- }
-
- iinfo->n_given_args = items - iinfo->stack_offset;
-
- iinfo->n_invoke_args = iinfo->n_args =
- g_callable_info_get_n_args ((GICallableInfo *) info);
-
- /* FIXME: can a vfunc not throw? */
- iinfo->throws = is_vfunc
- ? FALSE
- : g_function_info_get_flags (info) & GI_FUNCTION_THROWS;
- if (iinfo->throws) {
- iinfo->n_invoke_args++;
- }
-
- if (is_vfunc) {
- iinfo->is_method = TRUE;
- } else {
- iinfo->is_method =
- (g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD)
- && !iinfo->is_constructor;
- }
- if (iinfo->is_method) {
- iinfo->n_invoke_args++;
- }
-
- dwarn ("invoke: %s\n"
- " n_args: %d, n_invoke_args: %d, n_given_args: %d\n"
- " is_constructor: %d, is_method: %d\n",
- is_vfunc ? g_base_info_get_name (info) : g_function_info_get_symbol (info),
- iinfo->n_args, iinfo->n_invoke_args, iinfo->n_given_args,
- iinfo->is_constructor, iinfo->is_method);
-
- iinfo->return_type_info =
- g_callable_info_get_return_type ((GICallableInfo *) info);
- iinfo->has_return_value =
- GI_TYPE_TAG_VOID != g_type_info_get_tag (iinfo->return_type_info);
- iinfo->return_type_ffi = g_type_info_get_ffi_type (iinfo->return_type_info);
- iinfo->return_type_transfer = g_callable_info_get_caller_owns ((GICallableInfo *) info);
-
- /* allocate enough space for all args in both the out and in lists.
- * we'll only use as much as we need. since function argument lists
- * are typically small, this shouldn't be a big problem. */
- if (iinfo->n_invoke_args) {
- gint n = iinfo->n_invoke_args;
- iinfo->in_args = gperl_alloc_temp (sizeof (GIArgument) * n);
- iinfo->out_args = gperl_alloc_temp (sizeof (GIArgument) * n);
- iinfo->out_arg_infos = gperl_alloc_temp (sizeof (GITypeInfo*) * n);
- iinfo->arg_types = gperl_alloc_temp (sizeof (ffi_type *) * n);
- iinfo->args = gperl_alloc_temp (sizeof (gpointer) * n);
- iinfo->aux_args = gperl_alloc_temp (sizeof (GIArgument) * n);
- iinfo->is_automatic_arg = gperl_alloc_temp (sizeof (gboolean) * n);
- }
-
- iinfo->method_offset = iinfo->is_method ? 1 : 0;
- iinfo->dynamic_stack_offset = 0;
-
- /* Make a first pass to mark args that are filled in automatically, and
- * thus have no counterpart on the Perl side. */
- for (i = 0 ; i < iinfo->n_args ; i++) {
- GIArgInfo * arg_info =
- g_callable_info_get_arg ((GICallableInfo *) info, i);
- GITypeInfo * arg_type = g_arg_info_get_type (arg_info);
- GITypeTag arg_tag = g_type_info_get_tag (arg_type);
-
- if (arg_tag == GI_TYPE_TAG_ARRAY) {
- gint pos = g_type_info_get_array_length (arg_type);
- if (pos >= 0) {
- dwarn (" pos %d is automatic (array length)\n", pos);
- iinfo->is_automatic_arg[pos] = TRUE;
- }
- }
-
- else if (arg_tag == GI_TYPE_TAG_INTERFACE) {
- GIBaseInfo * interface = g_type_info_get_interface (arg_type);
- GIInfoType info_type = g_base_info_get_type (interface);
- if (info_type == GI_INFO_TYPE_CALLBACK) {
- gint pos = g_arg_info_get_destroy (arg_info);
- if (pos >= 0) {
- dwarn (" pos %d is automatic (callback destroy notify)\n", pos);
- iinfo->is_automatic_arg[pos] = TRUE;
- }
- }
- g_base_info_unref ((GIBaseInfo *) interface);
- }
-
- g_base_info_unref ((GIBaseInfo *) arg_type);
- g_base_info_unref ((GIBaseInfo *) arg_info);
- }
-
- /* If the return value is an array which comes with an outbound length
- * arg, then mark that length arg as automatic, too. */
- if (g_type_info_get_tag (iinfo->return_type_info) == GI_TYPE_TAG_ARRAY) {
- gint pos = g_type_info_get_array_length (iinfo->return_type_info);
- if (pos >= 0) {
- GIArgInfo * arg_info =
- g_callable_info_get_arg ((GICallableInfo *) info, pos);
- if (GI_DIRECTION_OUT == g_arg_info_get_direction (arg_info)) {
- dwarn (" pos %d is automatic (array length)\n", pos);
- iinfo->is_automatic_arg[pos] = TRUE;
- }
- }
- }
-
- /* We need to undo the special handling that GInitiallyUnowned
- * descendants receive from gobject-introspection: values of this type
- * are always marked transfer=none, even for constructors. */
- if (iinfo->is_constructor &&
- g_type_info_get_tag (iinfo->return_type_info) == GI_TYPE_TAG_INTERFACE)
- {
- GIBaseInfo * interface = g_type_info_get_interface (iinfo->return_type_info);
- if (GI_IS_REGISTERED_TYPE_INFO (interface) &&
- g_type_is_a (g_registered_type_info_get_g_type (interface),
- G_TYPE_INITIALLY_UNOWNED))
- {
- iinfo->return_type_transfer = GI_TRANSFER_EVERYTHING;
- }
- g_base_info_unref ((GIBaseInfo *) interface);
- }
-}
-
-static void
-clear_invocation_info (GPerlI11nInvocationInfo *iinfo)
-{
- g_slist_free (iinfo->free_after_call);
-
- /* The actual callback infos might be needed later, so we cannot free
- * them here. */
- g_slist_free (iinfo->callback_infos);
-
- g_slist_foreach (iinfo->array_infos, (GFunc) g_free, NULL);
- g_slist_free (iinfo->array_infos);
-
- g_base_info_unref ((GIBaseInfo *) iinfo->return_type_info);
-}
-
-/* ------------------------------------------------------------------------- */
-
-static gpointer
-allocate_out_mem (GITypeInfo *arg_type)
-{
- GIBaseInfo *interface_info;
- GIInfoType type;
-
- interface_info = g_type_info_get_interface (arg_type);
- g_assert (interface_info);
- type = g_base_info_get_type (interface_info);
- g_base_info_unref (interface_info);
-
- switch (type) {
- case GI_INFO_TYPE_STRUCT:
- {
- /* No plain g_struct_info_get_size (interface_info) here so
- * that we get the GValue override. */
- gsize size = size_of_interface (arg_type);
- return g_malloc0 (size);
- }
- default:
- g_assert_not_reached ();
- return NULL;
- }
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* caller owns returned info */
-static GIFieldInfo *
-find_field (GIStructInfo *struct_info, const gchar *name)
-{
- gint n, i;
- n = g_struct_info_get_n_fields (struct_info);
- for (i = 0; i < n; i++) {
- GIFieldInfo *field_info =
- g_struct_info_get_field (struct_info, i);
- if (strEQ (g_base_info_get_name (field_info), name)) {
- return field_info;
- }
- g_base_info_unref (field_info);
- }
- return NULL;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void
-generic_interface_init (gpointer iface, gpointer data)
-{
- GIInterfaceInfo *info = data;
- GIStructInfo *struct_info;
- gint n, i;
- struct_info = g_interface_info_get_iface_struct (info);
- n = g_interface_info_get_n_vfuncs (info);
- for (i = 0; i < n; i++) {
- GIVFuncInfo *vfunc_info;
- const gchar *vfunc_name;
- GIFieldInfo *field_info;
- gint field_offset;
- GITypeInfo *field_type_info;
- gchar *perl_method_name;
- GPerlI11nCallbackInfo *callback_info;
-
- vfunc_info = g_interface_info_get_vfunc (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 = find_field (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_callback_closure_for_named_sub (
- field_type_info, perl_method_name, NULL);
- dwarn ("installing vfunc %s as %s at offset %d (vs. %d) inside %p\n",
- vfunc_name, perl_method_name,
- field_offset, g_vfunc_info_get_offset (vfunc_info),
- iface);
- G_STRUCT_MEMBER (gpointer, iface, field_offset) = callback_info->closure;
-
- g_base_info_unref (field_type_info);
- g_base_info_unref (field_info);
- g_base_info_unref (vfunc_info);
- }
- g_base_info_unref (struct_info);
-}
-
-static void
-generic_interface_finalize (gpointer iface, gpointer data)
-{
- GIInterfaceInfo *info = data;
- PERL_UNUSED_VAR (iface);
- dwarn ("releasing interface info\n");
- g_base_info_unref ((GIBaseInfo *) info);
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void
-generic_class_init (GIObjectInfo *info, const gchar *target_package, gpointer class)
-{
- GIStructInfo *struct_info;
- gint n, i;
- struct_info = g_object_info_get_class_struct (info);
- n = g_object_info_get_n_vfuncs (info);
- for (i = 0; i < n; i++) {
- GIVFuncInfo *vfunc_info;
- const gchar *vfunc_name;
- GIFieldInfo *field_info;
- gint field_offset;
- GITypeInfo *field_type_info;
- gchar *perl_method_name;
- GPerlI11nCallbackInfo *callback_info;
-
- vfunc_info = g_object_info_get_vfunc (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 = find_field (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_callback_closure_for_named_sub (
- field_type_info, perl_method_name, g_strdup (target_package));
- dwarn ("installing vfunc %s as %s at offset %d (vs. %d) inside %p\n",
- vfunc_name, perl_method_name,
- field_offset, g_vfunc_info_get_offset (vfunc_info),
- class);
- G_STRUCT_MEMBER (gpointer, class, field_offset) = callback_info->closure;
-
- g_base_info_unref (field_type_info);
- g_base_info_unref (field_info);
- g_base_info_unref (vfunc_info);
- }
- g_base_info_unref (struct_info);
-}
-
-/* ------------------------------------------------------------------------- */
-
-void
-invoke_callable (GICallableInfo *info,
- gpointer func_pointer,
- SV **sp, I32 ax, SV **mark, I32 items, /* these correspond to dXSARGS */
- UV internal_stack_offset)
-{
- ffi_cif cif;
- gpointer instance = NULL;
- guint i;
- GPerlI11nInvocationInfo iinfo = {0,};
- guint n_return_values;
- GIArgument return_value;
- GError * local_error = NULL;
- gpointer local_error_address = &local_error;
-
- prepare_invocation_info (&iinfo, info, items, internal_stack_offset);
-
- if (iinfo.is_method) {
- instance = instance_sv_to_pointer (info, ST (0 + iinfo.stack_offset));
- iinfo.arg_types[0] = &ffi_type_pointer;
- iinfo.args[0] = &instance;
- }
-
- for (i = 0 ; i < iinfo.n_args ; i++) {
- GIArgInfo * arg_info;
- GITypeInfo * arg_type;
- GITransfer transfer;
- gboolean may_be_null;
- gint perl_stack_pos, ffi_stack_pos;
- SV *current_sv;
-
- arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
- /* In case of out and in-out args, arg_type is unref'ed after
- * the function has been invoked */
- arg_type = g_arg_info_get_type (arg_info);
- transfer = g_arg_info_get_ownership_transfer (arg_info);
- may_be_null = g_arg_info_may_be_null (arg_info);
- perl_stack_pos = i
- + iinfo.method_offset
- + iinfo.stack_offset
- + iinfo.dynamic_stack_offset;
- ffi_stack_pos = i
- + iinfo.method_offset;
-
- /* FIXME: Is this right? I'm confused about the relation of
- * the numbers in g_callable_info_get_arg and
- * g_arg_info_get_closure and g_arg_info_get_destroy. We used
- * to add method_offset, but that stopped being correct at some
- * point. */
- iinfo.current_pos = i; /* + method_offset; */
-
- dwarn (" arg %d, tag: %d (%s), is_pointer: %d, is_automatic: %d\n",
- i,
- g_type_info_get_tag (arg_type),
- g_type_tag_to_string (g_type_info_get_tag (arg_type)),
- g_type_info_is_pointer (arg_type),
- iinfo.is_automatic_arg[i]);
-
- /* FIXME: Generate a proper usage message if the user did not
- * supply enough arguments. */
- current_sv = perl_stack_pos < items ? ST (perl_stack_pos) : &PL_sv_undef;
-
- switch (g_arg_info_get_direction (arg_info)) {
- case GI_DIRECTION_IN:
- if (iinfo.is_automatic_arg[i]) {
- iinfo.dynamic_stack_offset--;
-#if GI_CHECK_VERSION (1, 29, 0)
- } else if (g_arg_info_is_skip (arg_info)) {
- iinfo.dynamic_stack_offset--;
-#endif
- } else {
- sv_to_arg (current_sv,
- &iinfo.in_args[i], arg_info, arg_type,
- transfer, may_be_null, &iinfo);
- }
- iinfo.arg_types[ffi_stack_pos] =
- g_type_info_get_ffi_type (arg_type);
- iinfo.args[ffi_stack_pos] = &iinfo.in_args[i];
- g_base_info_unref ((GIBaseInfo *) arg_type);
- break;
-
- case GI_DIRECTION_OUT:
- if (g_arg_info_is_caller_allocates (arg_info)) {
- iinfo.aux_args[i].v_pointer =
- allocate_out_mem (arg_type);
- iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
- iinfo.args[ffi_stack_pos] = &iinfo.aux_args[i];
- } else {
- iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
- iinfo.args[ffi_stack_pos] = &iinfo.out_args[i];
- }
- iinfo.out_arg_infos[i] = arg_type;
- iinfo.arg_types[ffi_stack_pos] = &ffi_type_pointer;
- /* Adjust the dynamic stack offset so that this out
- * argument doesn't inadvertedly eat up an in argument. */
- iinfo.dynamic_stack_offset--;
- break;
-
- case GI_DIRECTION_INOUT:
- iinfo.in_args[i].v_pointer =
- iinfo.out_args[i].v_pointer =
- &iinfo.aux_args[i];
- if (iinfo.is_automatic_arg[i]) {
- iinfo.dynamic_stack_offset--;
-#if GI_CHECK_VERSION (1, 29, 0)
- } else if (g_arg_info_is_skip (arg_info)) {
- iinfo.dynamic_stack_offset--;
-#endif
- } else {
- /* We pass iinfo.in_args[i].v_pointer here,
- * not &iinfo.in_args[i], so that the value
- * pointed to is filled from the SV. */
- sv_to_arg (current_sv,
- iinfo.in_args[i].v_pointer, arg_info, arg_type,
- transfer, may_be_null, &iinfo);
- }
- iinfo.out_arg_infos[i] = arg_type;
- iinfo.arg_types[ffi_stack_pos] = &ffi_type_pointer;
- iinfo.args[ffi_stack_pos] = &iinfo.in_args[i];
- break;
- }
-
- g_base_info_unref ((GIBaseInfo *) arg_info);
- }
-
- /* do another pass to handle automatic args */
- for (i = 0 ; i < iinfo.n_args ; i++) {
- GIArgInfo * arg_info;
- if (!iinfo.is_automatic_arg[i])
- continue;
- arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
- switch (g_arg_info_get_direction (arg_info)) {
- case GI_DIRECTION_IN:
- handle_automatic_arg (i, &iinfo.in_args[i], &iinfo);
- break;
- case GI_DIRECTION_INOUT:
- handle_automatic_arg (i, &iinfo.aux_args[i], &iinfo);
- break;
- case GI_DIRECTION_OUT:
- /* handled later */
- break;
- }
- g_base_info_unref ((GIBaseInfo *) arg_info);
- }
-
- if (iinfo.throws) {
- iinfo.args[iinfo.n_invoke_args - 1] = &local_error_address;
- iinfo.arg_types[iinfo.n_invoke_args - 1] = &ffi_type_pointer;
- }
-
- /* prepare and call the function */
- if (FFI_OK != ffi_prep_cif (&cif, FFI_DEFAULT_ABI, iinfo.n_invoke_args,
- iinfo.return_type_ffi, iinfo.arg_types))
- {
- clear_invocation_info (&iinfo);
- ccroak ("Could not prepare a call interface");
- }
-
- ffi_call (&cif, func_pointer, &return_value, iinfo.args);
-
- /* free call-scoped callback infos */
- g_slist_foreach (iinfo.free_after_call,
- (GFunc) release_callback, NULL);
-
- if (local_error) {
- gperl_croak_gerror (NULL, local_error);
- }
-
- /*
- * handle return values
- */
- n_return_values = 0;
-
- /* place return value and output args on the stack */
- if (iinfo.has_return_value
-#if GI_CHECK_VERSION (1, 29, 0)
- && !g_callable_info_skip_return ((GICallableInfo *) info)
-#endif
- )
- {
- SV *value = arg_to_sv (&return_value,
- iinfo.return_type_info,
- iinfo.return_type_transfer,
- &iinfo);
- if (value) {
- XPUSHs (sv_2mortal (value));
- n_return_values++;
- }
- }
-
- /* out args */
- for (i = 0 ; i < iinfo.n_args ; i++) {
- GIArgInfo * arg_info;
- if (iinfo.is_automatic_arg[i])
- continue;
- arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
-#if GI_CHECK_VERSION (1, 29, 0)
- if (g_arg_info_is_skip (arg_info)) {
- g_base_info_unref ((GIBaseInfo *) arg_info);
- continue;
- }
-#endif
- switch (g_arg_info_get_direction (arg_info)) {
- case GI_DIRECTION_OUT:
- case GI_DIRECTION_INOUT:
- {
- GITransfer transfer;
- SV *sv;
- /* If we allocated the memory ourselves, we always own it. */
- transfer = g_arg_info_is_caller_allocates (arg_info)
- ? GI_TRANSFER_CONTAINER
- : g_arg_info_get_ownership_transfer (arg_info);
- sv = arg_to_sv (iinfo.out_args[i].v_pointer,
- iinfo.out_arg_infos[i],
- transfer,
- &iinfo);
- if (sv) {
- XPUSHs (sv_2mortal (sv));
- n_return_values++;
- }
- g_base_info_unref ((GIBaseInfo*) iinfo.out_arg_infos[i]);
- break;
- }
-
- default:
- break;
- }
- g_base_info_unref ((GIBaseInfo *) arg_info);
- }
-
- clear_invocation_info (&iinfo);
-
- dwarn (" number of return values: %d\n", n_return_values);
-
- PUTBACK;
-}
+#include "gperl-i11n-callback.c"
+#include "gperl-i11n-croak.c"
+#include "gperl-i11n-field.c"
+#include "gperl-i11n-gvalue.c"
+#include "gperl-i11n-info.c"
+#include "gperl-i11n-invoke-c.c"
+#include "gperl-i11n-invoke-info.c"
+#include "gperl-i11n-invoke-perl.c"
+#include "gperl-i11n-marshal-arg.c"
+#include "gperl-i11n-marshal-array.c"
+#include "gperl-i11n-marshal-callback.c"
+#include "gperl-i11n-marshal-hash.c"
+#include "gperl-i11n-marshal-interface.c"
+#include "gperl-i11n-marshal-list.c"
+#include "gperl-i11n-marshal-raw.c"
+#include "gperl-i11n-marshal-struct.c"
+#include "gperl-i11n-method.c"
+#include "gperl-i11n-size.c"
+#include "gperl-i11n-vfunc-interface.c"
+#include "gperl-i11n-vfunc-object.c"
/* ------------------------------------------------------------------------- */
@@ -3230,7 +581,7 @@ _invoke_parent_vfunc (class, basename, object_name, vfunc_name, ...)
vfunc_info = g_object_info_find_vfunc (info, vfunc_name);
g_assert (vfunc_info);
/* FIXME: g_vfunc_info_get_offset does not seem to work here. */
- field_info = find_field (struct_info, vfunc_name);
+ field_info = get_field_info (struct_info, vfunc_name);
g_assert (field_info);
field_offset = g_field_info_get_offset (field_info);
func_pointer = G_STRUCT_MEMBER (gpointer, klass, field_offset);
diff --git a/MANIFEST b/MANIFEST
index 455d570..68b8646 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,4 +1,24 @@
GObjectIntrospection.xs
+gperl-i11n-callback.c
+gperl-i11n-croak.c
+gperl-i11n-field.c
+gperl-i11n-gvalue.c
+gperl-i11n-info.c
+gperl-i11n-invoke-c.c
+gperl-i11n-invoke-info.c
+gperl-i11n-invoke-perl.c
+gperl-i11n-marshal-arg.c
+gperl-i11n-marshal-array.c
+gperl-i11n-marshal-callback.c
+gperl-i11n-marshal-hash.c
+gperl-i11n-marshal-interface.c
+gperl-i11n-marshal-list.c
+gperl-i11n-marshal-raw.c
+gperl-i11n-marshal-struct.c
+gperl-i11n-method.c
+gperl-i11n-size.c
+gperl-i11n-vfunc-interface.c
+gperl-i11n-vfunc-object.c
lib/Glib/Object/Introspection.pm
LICENSE
Makefile.PL
@@ -9,6 +29,7 @@ perl-Glib-Object-Introspection.doap
README
t/00-basic-types.t
t/arrays.t
+t/boxed.t
t/cairo-integration.t
t/callbacks.t
t/closures.t
@@ -16,5 +37,8 @@ t/constants.t
t/enums.t
t/hashes.t
t/inc/setup.pl
+t/interface-implementation.t
t/objects.t
+t/structs.t
t/values.t
+t/vfunc-implementation.t
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
index 604bf16..3f775b6 100644
--- a/MANIFEST.SKIP
+++ b/MANIFEST.SKIP
@@ -8,5 +8,5 @@ build
Makefile$
Makefile\.old$
MYMETA\..*$
-\.c$
+GObjectIntrospection.c$
\.o$
diff --git a/gperl-i11n-callback.c b/gperl-i11n-callback.c
new file mode 100644
index 0000000..7b712c1
--- /dev/null
+++ b/gperl-i11n-callback.c
@@ -0,0 +1,82 @@
+static GPerlI11nCallbackInfo *
+create_callback_closure (GITypeInfo *cb_type, SV *code)
+{
+ GPerlI11nCallbackInfo *info;
+
+ info = g_new0 (GPerlI11nCallbackInfo, 1);
+ info->interface =
+ (GICallableInfo *) g_type_info_get_interface (cb_type);
+ info->cif = g_new0 (ffi_cif, 1);
+ info->closure =
+ g_callable_info_prepare_closure (info->interface, info->cif,
+ invoke_callback, info);
+ /* FIXME: This should most likely use SvREFCNT_inc instead of
+ * newSVsv. */
+ info->code = newSVsv (code);
+ info->sub_name = NULL;
+ info->package_name = NULL;
+
+#ifdef PERL_IMPLICIT_CONTEXT
+ info->priv = aTHX;
+#endif
+
+ return info;
+}
+
+static void
+attach_callback_data (GPerlI11nCallbackInfo *info, SV *data)
+{
+ info->data = newSVsv (data);
+}
+
+/* assumes ownership of sub_name and package_name */
+static GPerlI11nCallbackInfo *
+create_callback_closure_for_named_sub (GITypeInfo *cb_type, gchar *sub_name, gchar *package_name)
+{
+ GPerlI11nCallbackInfo *info;
+
+ info = g_new0 (GPerlI11nCallbackInfo, 1);
+ info->interface =
+ (GICallableInfo *) g_type_info_get_interface (cb_type);
+ info->cif = g_new0 (ffi_cif, 1);
+ info->closure =
+ g_callable_info_prepare_closure (info->interface, info->cif,
+ invoke_callback, info);
+ info->sub_name = sub_name;
+ info->package_name = package_name;
+ info->code = NULL;
+ info->data = NULL;
+
+#ifdef PERL_IMPLICIT_CONTEXT
+ info->priv = aTHX;
+#endif
+
+ return info;
+}
+
+static void
+release_callback (gpointer data)
+{
+ GPerlI11nCallbackInfo *info = data;
+ dwarn ("releasing callback info %p\n", info);
+
+ if (info->cif)
+ g_free (info->cif);
+
+ if (info->closure)
+ g_callable_info_free_closure (info->interface, info->closure);
+
+ if (info->interface)
+ g_base_info_unref ((GIBaseInfo*) info->interface);
+
+ if (info->code)
+ SvREFCNT_dec (info->code);
+ if (info->data)
+ SvREFCNT_dec (info->data);
+ if (info->sub_name)
+ g_free (info->sub_name);
+ if (info->package_name)
+ g_free (info->package_name);
+
+ g_free (info);
+}
diff --git a/gperl-i11n-croak.c b/gperl-i11n-croak.c
new file mode 100644
index 0000000..c2c7c04
--- /dev/null
+++ b/gperl-i11n-croak.c
@@ -0,0 +1,20 @@
+/* Call Carp's croak() so that errors are reported at their location in the
+ * user's program, not in Introspection.pm. Adapted from
+ * <http://www.perlmonks.org/?node_id=865159>. */
+static void
+call_carp_croak (const char *msg)
+{
+ dSP;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK (SP);
+ XPUSHs (sv_2mortal (newSVpv(msg, PL_na)));
+ PUTBACK;
+
+ call_pv("Carp::croak", G_VOID | G_DISCARD);
+
+ FREETMPS;
+ LEAVE;
+}
diff --git a/gperl-i11n-field.c b/gperl-i11n-field.c
new file mode 100644
index 0000000..12042f4
--- /dev/null
+++ b/gperl-i11n-field.c
@@ -0,0 +1,131 @@
+static void
+store_fields (HV *fields, GIBaseInfo *info, GIInfoType info_type)
+{
+ const gchar *namespace;
+ AV *av;
+ gint i;
+
+ namespace = g_base_info_get_name (info);
+ av = newAV ();
+
+ switch (info_type) {
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ {
+ gint n_fields = g_struct_info_get_n_fields (
+ (GIStructInfo *) info);
+ for (i = 0; i < n_fields; i++) {
+ GIFieldInfo *field_info;
+ const gchar *field_name;
+ field_info = g_struct_info_get_field ((GIStructInfo *) info, i);
+ field_name = g_base_info_get_name ((GIBaseInfo *) field_info);
+ av_push (av, newSVpv (field_name, PL_na));
+ g_base_info_unref ((GIBaseInfo *) field_info);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_UNION:
+ {
+ gint n_fields = g_union_info_get_n_fields ((GIUnionInfo *) info);
+ for (i = 0; i < n_fields; i++) {
+ GIFieldInfo *field_info;
+ const gchar *field_name;
+ field_info = g_union_info_get_field ((GIUnionInfo *) info, i);
+ field_name = g_base_info_get_name ((GIBaseInfo *) field_info);
+ av_push (av, newSVpv (field_name, PL_na));
+ g_base_info_unref ((GIBaseInfo *) field_info);
+ }
+ break;
+ }
+
+ default:
+ ccroak ("store_fields: unsupported info type %d", info_type);
+ }
+
+ gperl_hv_take_sv (fields, namespace, strlen (namespace),
+ newRV_noinc ((SV *) av));
+}
+
+static SV *
+get_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer)
+{
+ GITypeInfo *field_type;
+ GIBaseInfo *interface_info;
+ GIArgument value;
+ SV *sv = NULL;
+
+ field_type = g_field_info_get_type (field_info);
+ interface_info = g_type_info_get_interface (field_type);
+
+ /* This case is not handled by g_field_info_set_field. */
+ if (!g_type_info_is_pointer (field_type) &&
+ g_type_info_get_tag (field_type) == GI_TYPE_TAG_INTERFACE &&
+ g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT)
+ {
+ gsize offset;
+ offset = g_field_info_get_offset (field_info);
+ value.v_pointer = mem + offset;
+ sv = arg_to_sv (&value,
+ field_type,
+ GI_TRANSFER_NOTHING,
+ NULL);
+ } else if (g_field_info_get_field (field_info, mem, &value)) {
+ sv = arg_to_sv (&value,
+ field_type,
+ transfer,
+ NULL);
+ } else {
+ ccroak ("Could not get field '%s'",
+ g_base_info_get_name (field_info));
+ }
+
+ if (interface_info)
+ g_base_info_unref (interface_info);
+ g_base_info_unref ((GIBaseInfo *) field_type);
+
+ return sv;
+}
+
+static void
+set_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer, SV *value)
+{
+ GITypeInfo *field_type;
+ GIBaseInfo *interface_info;
+ GIArgument arg;
+
+ field_type = g_field_info_get_type (field_info);
+ interface_info = g_type_info_get_interface (field_type);
+
+ /* FIXME: No GIArgInfo and no
+ * GPerlI11nInvocationInfo here. What if the
+ * struct contains an object pointer, or a
+ * callback field? And is it OK to always
+ * allow undef? */
+
+ /* This case is not handled by g_field_info_set_field. */
+ if (!g_type_info_is_pointer (field_type) &&
+ g_type_info_get_tag (field_type) == GI_TYPE_TAG_INTERFACE &&
+ g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT)
+ {
+ gsize offset;
+ gssize size;
+ /* Enforce GI_TRANSFER_NOTHING since we will copy into the
+ * memory that has already been allocated inside 'mem' */
+ sv_to_arg (value, &arg, NULL, field_type,
+ GI_TRANSFER_NOTHING, TRUE, NULL);
+ offset = g_field_info_get_offset (field_info);
+ size = g_struct_info_get_size (interface_info);
+ g_memmove (mem + offset, arg.v_pointer, size);
+ } else {
+ sv_to_arg (value, &arg, NULL, field_type,
+ transfer, TRUE, NULL);
+ if (!g_field_info_set_field (field_info, mem, &arg))
+ ccroak ("Could not set field '%s'",
+ g_base_info_get_name (field_info));
+ }
+
+ if (interface_info)
+ g_base_info_unref (interface_info);
+ g_base_info_unref (field_type);
+}
diff --git a/gperl-i11n-gvalue.c b/gperl-i11n-gvalue.c
new file mode 100644
index 0000000..53ba10c
--- /dev/null
+++ b/gperl-i11n-gvalue.c
@@ -0,0 +1,19 @@
+/* Semi-private package for marshalling into GValues. */
+#define GVALUE_WRAPPER_PACKAGE "Glib::Object::Introspection::GValueWrapper"
+
+static GValue *
+SvGValueWrapper (SV *sv)
+{
+ return sv_derived_from (sv, GVALUE_WRAPPER_PACKAGE)
+ ? INT2PTR (GValue*, SvIV (SvRV (sv)))
+ : NULL;
+}
+
+static SV *
+newSVGValueWrapper (GValue *v)
+{
+ SV *sv;
+ sv = newSV (0);
+ sv_setref_pv (sv, GVALUE_WRAPPER_PACKAGE, v);
+ return sv;
+}
diff --git a/gperl-i11n-info.c b/gperl-i11n-info.c
new file mode 100644
index 0000000..cad4a59
--- /dev/null
+++ b/gperl-i11n-info.c
@@ -0,0 +1,111 @@
+/* Caller owns return value */
+static GIFunctionInfo *
+get_function_info (GIRepository *repository,
+ const gchar *basename,
+ const gchar *namespace,
+ const gchar *method)
+{
+ dwarn ("%s: %s, %s, %s\n", G_STRFUNC, basename, namespace, method);
+
+ if (namespace) {
+ GIFunctionInfo *function_info = NULL;
+ GIBaseInfo *namespace_info = g_irepository_find_by_name (
+ repository, basename, namespace);
+ if (!namespace_info)
+ ccroak ("Can't find information for namespace %s",
+ namespace);
+
+ switch (g_base_info_get_type (namespace_info)) {
+ case GI_INFO_TYPE_OBJECT:
+ function_info = g_object_info_find_method (
+ (GIObjectInfo *) namespace_info,
+ method);
+ break;
+ case GI_INFO_TYPE_INTERFACE:
+ function_info = g_interface_info_find_method (
+ (GIInterfaceInfo *) namespace_info,
+ method);
+ break;
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ function_info = g_struct_info_find_method (
+ (GIStructInfo *) namespace_info,
+ method);
+ break;
+ case GI_INFO_TYPE_UNION:
+ function_info = g_union_info_find_method (
+ (GIUnionInfo *) namespace_info,
+ method);
+ break;
+ default:
+ ccroak ("Base info for namespace %s has incorrect type",
+ namespace);
+ }
+
+ if (!function_info)
+ ccroak ("Can't find information for method "
+ "%s::%s", namespace, method);
+
+ g_base_info_unref (namespace_info);
+
+ return function_info;
+ } else {
+ GIBaseInfo *method_info = g_irepository_find_by_name (
+ repository, basename, method);
+
+ if (!method_info)
+ ccroak ("Can't find information for method %s", method);
+
+ switch (g_base_info_get_type (method_info)) {
+ case GI_INFO_TYPE_FUNCTION:
+ return (GIFunctionInfo *) method_info;
+ default:
+ ccroak ("Base info for method %s has incorrect type",
+ method);
+ }
+ }
+
+ return NULL;
+}
+
+/* Caller owns return value */
+static GIFieldInfo *
+get_field_info (GIBaseInfo *info, const gchar *field_name)
+{
+ GIInfoType info_type;
+ info_type = g_base_info_get_type (info);
+ switch (info_type) {
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ {
+ gint n_fields, i;
+ n_fields = g_struct_info_get_n_fields ((GIStructInfo *) info);
+ for (i = 0; i < n_fields; i++) {
+ GIFieldInfo *field_info;
+ field_info = g_struct_info_get_field ((GIStructInfo *) info, i);
+ if (0 == strcmp (field_name, g_base_info_get_name (field_info))) {
+ return field_info;
+ }
+ g_base_info_unref (field_info);
+ }
+ break;
+ }
+ case GI_INFO_TYPE_UNION:
+ {
+ gint n_fields, i;
+ n_fields = g_union_info_get_n_fields ((GIStructInfo *) info);
+ for (i = 0; i < n_fields; i++) {
+ GIFieldInfo *field_info;
+ field_info = g_union_info_get_field ((GIStructInfo *) info, i);
+ if (0 == strcmp (field_name, g_base_info_get_name (field_info))) {
+ return field_info;
+ }
+ g_base_info_unref (field_info);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return NULL;
+}
diff --git a/gperl-i11n-invoke-c.c b/gperl-i11n-invoke-c.c
new file mode 100644
index 0000000..aa4c853
--- /dev/null
+++ b/gperl-i11n-invoke-c.c
@@ -0,0 +1,238 @@
+void
+invoke_callable (GICallableInfo *info,
+ gpointer func_pointer,
+ SV **sp, I32 ax, SV **mark, I32 items, /* these correspond to dXSARGS */
+ UV internal_stack_offset)
+{
+ ffi_cif cif;
+ gpointer instance = NULL;
+ guint i;
+ GPerlI11nInvocationInfo iinfo = {0,};
+ guint n_return_values;
+ GIArgument return_value;
+ GError * local_error = NULL;
+ gpointer local_error_address = &local_error;
+
+ PERL_UNUSED_VAR (mark);
+
+ prepare_invocation_info (&iinfo, info, items, internal_stack_offset);
+
+ if (iinfo.is_method) {
+ instance = instance_sv_to_pointer (info, ST (0 + iinfo.stack_offset));
+ iinfo.arg_types[0] = &ffi_type_pointer;
+ iinfo.args[0] = &instance;
+ }
+
+ for (i = 0 ; i < iinfo.n_args ; i++) {
+ GIArgInfo * arg_info;
+ GITypeInfo * arg_type;
+ GITransfer transfer;
+ gboolean may_be_null;
+ gint perl_stack_pos, ffi_stack_pos;
+ SV *current_sv;
+
+ arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
+ /* In case of out and in-out args, arg_type is unref'ed after
+ * the function has been invoked */
+ arg_type = g_arg_info_get_type (arg_info);
+ transfer = g_arg_info_get_ownership_transfer (arg_info);
+ may_be_null = g_arg_info_may_be_null (arg_info);
+ perl_stack_pos = i
+ + iinfo.method_offset
+ + iinfo.stack_offset
+ + iinfo.dynamic_stack_offset;
+ ffi_stack_pos = i
+ + iinfo.method_offset;
+
+ /* FIXME: Is this right? I'm confused about the relation of
+ * the numbers in g_callable_info_get_arg and
+ * g_arg_info_get_closure and g_arg_info_get_destroy. We used
+ * to add method_offset, but that stopped being correct at some
+ * point. */
+ iinfo.current_pos = i; /* + method_offset; */
+
+ dwarn (" arg %d, tag: %d (%s), is_pointer: %d, is_automatic: %d\n",
+ i,
+ g_type_info_get_tag (arg_type),
+ g_type_tag_to_string (g_type_info_get_tag (arg_type)),
+ g_type_info_is_pointer (arg_type),
+ iinfo.is_automatic_arg[i]);
+
+ /* FIXME: Generate a proper usage message if the user did not
+ * supply enough arguments. */
+ current_sv = perl_stack_pos < items ? ST (perl_stack_pos) : &PL_sv_undef;
+
+ switch (g_arg_info_get_direction (arg_info)) {
+ case GI_DIRECTION_IN:
+ if (iinfo.is_automatic_arg[i]) {
+ iinfo.dynamic_stack_offset--;
+#if GI_CHECK_VERSION (1, 29, 0)
+ } else if (g_arg_info_is_skip (arg_info)) {
+ iinfo.dynamic_stack_offset--;
+#endif
+ } else {
+ sv_to_arg (current_sv,
+ &iinfo.in_args[i], arg_info, arg_type,
+ transfer, may_be_null, &iinfo);
+ }
+ iinfo.arg_types[ffi_stack_pos] =
+ g_type_info_get_ffi_type (arg_type);
+ iinfo.args[ffi_stack_pos] = &iinfo.in_args[i];
+ g_base_info_unref ((GIBaseInfo *) arg_type);
+ break;
+
+ case GI_DIRECTION_OUT:
+ if (g_arg_info_is_caller_allocates (arg_info)) {
+ iinfo.aux_args[i].v_pointer =
+ allocate_out_mem (arg_type);
+ iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
+ iinfo.args[ffi_stack_pos] = &iinfo.aux_args[i];
+ } else {
+ iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
+ iinfo.args[ffi_stack_pos] = &iinfo.out_args[i];
+ }
+ iinfo.out_arg_infos[i] = arg_type;
+ iinfo.arg_types[ffi_stack_pos] = &ffi_type_pointer;
+ /* Adjust the dynamic stack offset so that this out
+ * argument doesn't inadvertedly eat up an in argument. */
+ iinfo.dynamic_stack_offset--;
+ break;
+
+ case GI_DIRECTION_INOUT:
+ iinfo.in_args[i].v_pointer =
+ iinfo.out_args[i].v_pointer =
+ &iinfo.aux_args[i];
+ if (iinfo.is_automatic_arg[i]) {
+ iinfo.dynamic_stack_offset--;
+#if GI_CHECK_VERSION (1, 29, 0)
+ } else if (g_arg_info_is_skip (arg_info)) {
+ iinfo.dynamic_stack_offset--;
+#endif
+ } else {
+ /* We pass iinfo.in_args[i].v_pointer here,
+ * not &iinfo.in_args[i], so that the value
+ * pointed to is filled from the SV. */
+ sv_to_arg (current_sv,
+ iinfo.in_args[i].v_pointer, arg_info, arg_type,
+ transfer, may_be_null, &iinfo);
+ }
+ iinfo.out_arg_infos[i] = arg_type;
+ iinfo.arg_types[ffi_stack_pos] = &ffi_type_pointer;
+ iinfo.args[ffi_stack_pos] = &iinfo.in_args[i];
+ break;
+ }
+
+ g_base_info_unref ((GIBaseInfo *) arg_info);
+ }
+
+ /* do another pass to handle automatic args */
+ for (i = 0 ; i < iinfo.n_args ; i++) {
+ GIArgInfo * arg_info;
+ if (!iinfo.is_automatic_arg[i])
+ continue;
+ arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
+ switch (g_arg_info_get_direction (arg_info)) {
+ case GI_DIRECTION_IN:
+ handle_automatic_arg (i, &iinfo.in_args[i], &iinfo);
+ break;
+ case GI_DIRECTION_INOUT:
+ handle_automatic_arg (i, &iinfo.aux_args[i], &iinfo);
+ break;
+ case GI_DIRECTION_OUT:
+ /* handled later */
+ break;
+ }
+ g_base_info_unref ((GIBaseInfo *) arg_info);
+ }
+
+ if (iinfo.throws) {
+ iinfo.args[iinfo.n_invoke_args - 1] = &local_error_address;
+ iinfo.arg_types[iinfo.n_invoke_args - 1] = &ffi_type_pointer;
+ }
+
+ /* prepare and call the function */
+ if (FFI_OK != ffi_prep_cif (&cif, FFI_DEFAULT_ABI, iinfo.n_invoke_args,
+ iinfo.return_type_ffi, iinfo.arg_types))
+ {
+ clear_invocation_info (&iinfo);
+ ccroak ("Could not prepare a call interface");
+ }
+
+ ffi_call (&cif, func_pointer, &return_value, iinfo.args);
+
+ /* free call-scoped callback infos */
+ g_slist_foreach (iinfo.free_after_call,
+ (GFunc) release_callback, NULL);
+
+ if (local_error) {
+ gperl_croak_gerror (NULL, local_error);
+ }
+
+ /*
+ * handle return values
+ */
+ n_return_values = 0;
+
+ /* place return value and output args on the stack */
+ if (iinfo.has_return_value
+#if GI_CHECK_VERSION (1, 29, 0)
+ && !g_callable_info_skip_return ((GICallableInfo *) info)
+#endif
+ )
+ {
+ SV *value = arg_to_sv (&return_value,
+ iinfo.return_type_info,
+ iinfo.return_type_transfer,
+ &iinfo);
+ if (value) {
+ XPUSHs (sv_2mortal (value));
+ n_return_values++;
+ }
+ }
+
+ /* out args */
+ for (i = 0 ; i < iinfo.n_args ; i++) {
+ GIArgInfo * arg_info;
+ if (iinfo.is_automatic_arg[i])
+ continue;
+ arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
+#if GI_CHECK_VERSION (1, 29, 0)
+ if (g_arg_info_is_skip (arg_info)) {
+ g_base_info_unref ((GIBaseInfo *) arg_info);
+ continue;
+ }
+#endif
+ switch (g_arg_info_get_direction (arg_info)) {
+ case GI_DIRECTION_OUT:
+ case GI_DIRECTION_INOUT:
+ {
+ GITransfer transfer;
+ SV *sv;
+ /* If we allocated the memory ourselves, we always own it. */
+ transfer = g_arg_info_is_caller_allocates (arg_info)
+ ? GI_TRANSFER_CONTAINER
+ : g_arg_info_get_ownership_transfer (arg_info);
+ sv = arg_to_sv (iinfo.out_args[i].v_pointer,
+ iinfo.out_arg_infos[i],
+ transfer,
+ &iinfo);
+ if (sv) {
+ XPUSHs (sv_2mortal (sv));
+ n_return_values++;
+ }
+ g_base_info_unref ((GIBaseInfo*) iinfo.out_arg_infos[i]);
+ break;
+ }
+
+ default:
+ break;
+ }
+ g_base_info_unref ((GIBaseInfo *) arg_info);
+ }
+
+ clear_invocation_info (&iinfo);
+
+ dwarn (" number of return values: %d\n", n_return_values);
+
+ PUTBACK;
+}
diff --git a/gperl-i11n-invoke-info.c b/gperl-i11n-invoke-info.c
new file mode 100644
index 0000000..1836733
--- /dev/null
+++ b/gperl-i11n-invoke-info.c
@@ -0,0 +1,211 @@
+static void
+prepare_invocation_info (GPerlI11nInvocationInfo *iinfo,
+ GICallableInfo *info,
+ IV items,
+ UV internal_stack_offset)
+{
+ gboolean is_vfunc;
+ guint i;
+
+ is_vfunc = GI_IS_VFUNC_INFO (info);
+
+ iinfo->stack_offset = internal_stack_offset;
+
+ iinfo->is_constructor = is_vfunc
+ ? FALSE
+ : g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR;
+ if (iinfo->is_constructor) {
+ iinfo->stack_offset++;
+ }
+
+ iinfo->n_given_args = items - iinfo->stack_offset;
+
+ iinfo->n_invoke_args = iinfo->n_args =
+ g_callable_info_get_n_args ((GICallableInfo *) info);
+
+ /* FIXME: can a vfunc not throw? */
+ iinfo->throws = is_vfunc
+ ? FALSE
+ : g_function_info_get_flags (info) & GI_FUNCTION_THROWS;
+ if (iinfo->throws) {
+ iinfo->n_invoke_args++;
+ }
+
+ if (is_vfunc) {
+ iinfo->is_method = TRUE;
+ } else {
+ iinfo->is_method =
+ (g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD)
+ && !iinfo->is_constructor;
+ }
+ if (iinfo->is_method) {
+ iinfo->n_invoke_args++;
+ }
+
+ dwarn ("invoke: %s\n"
+ " n_args: %d, n_invoke_args: %d, n_given_args: %d\n"
+ " is_constructor: %d, is_method: %d\n",
+ is_vfunc ? g_base_info_get_name (info) : g_function_info_get_symbol (info),
+ iinfo->n_args, iinfo->n_invoke_args, iinfo->n_given_args,
+ iinfo->is_constructor, iinfo->is_method);
+
+ iinfo->return_type_info =
+ g_callable_info_get_return_type ((GICallableInfo *) info);
+ iinfo->has_return_value =
+ GI_TYPE_TAG_VOID != g_type_info_get_tag (iinfo->return_type_info);
+ iinfo->return_type_ffi = g_type_info_get_ffi_type (iinfo->return_type_info);
+ iinfo->return_type_transfer = g_callable_info_get_caller_owns ((GICallableInfo *) info);
+
+ /* allocate enough space for all args in both the out and in lists.
+ * we'll only use as much as we need. since function argument lists
+ * are typically small, this shouldn't be a big problem. */
+ if (iinfo->n_invoke_args) {
+ gint n = iinfo->n_invoke_args;
+ iinfo->in_args = gperl_alloc_temp (sizeof (GIArgument) * n);
+ iinfo->out_args = gperl_alloc_temp (sizeof (GIArgument) * n);
+ iinfo->out_arg_infos = gperl_alloc_temp (sizeof (GITypeInfo*) * n);
+ iinfo->arg_types = gperl_alloc_temp (sizeof (ffi_type *) * n);
+ iinfo->args = gperl_alloc_temp (sizeof (gpointer) * n);
+ iinfo->aux_args = gperl_alloc_temp (sizeof (GIArgument) * n);
+ iinfo->is_automatic_arg = gperl_alloc_temp (sizeof (gboolean) * n);
+ }
+
+ iinfo->method_offset = iinfo->is_method ? 1 : 0;
+ iinfo->dynamic_stack_offset = 0;
+
+ /* Make a first pass to mark args that are filled in automatically, and
+ * thus have no counterpart on the Perl side. */
+ for (i = 0 ; i < iinfo->n_args ; i++) {
+ GIArgInfo * arg_info =
+ g_callable_info_get_arg ((GICallableInfo *) info, i);
+ GITypeInfo * arg_type = g_arg_info_get_type (arg_info);
+ GITypeTag arg_tag = g_type_info_get_tag (arg_type);
+
+ if (arg_tag == GI_TYPE_TAG_ARRAY) {
+ gint pos = g_type_info_get_array_length (arg_type);
+ if (pos >= 0) {
+ dwarn (" pos %d is automatic (array length)\n", pos);
+ iinfo->is_automatic_arg[pos] = TRUE;
+ }
+ }
+
+ else if (arg_tag == GI_TYPE_TAG_INTERFACE) {
+ GIBaseInfo * interface = g_type_info_get_interface (arg_type);
+ GIInfoType info_type = g_base_info_get_type (interface);
+ if (info_type == GI_INFO_TYPE_CALLBACK) {
+ gint pos = g_arg_info_get_destroy (arg_info);
+ if (pos >= 0) {
+ dwarn (" pos %d is automatic (callback destroy notify)\n", pos);
+ iinfo->is_automatic_arg[pos] = TRUE;
+ }
+ }
+ g_base_info_unref ((GIBaseInfo *) interface);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) arg_type);
+ g_base_info_unref ((GIBaseInfo *) arg_info);
+ }
+
+ /* If the return value is an array which comes with an outbound length
+ * arg, then mark that length arg as automatic, too. */
+ if (g_type_info_get_tag (iinfo->return_type_info) == GI_TYPE_TAG_ARRAY) {
+ gint pos = g_type_info_get_array_length (iinfo->return_type_info);
+ if (pos >= 0) {
+ GIArgInfo * arg_info =
+ g_callable_info_get_arg ((GICallableInfo *) info, pos);
+ if (GI_DIRECTION_OUT == g_arg_info_get_direction (arg_info)) {
+ dwarn (" pos %d is automatic (array length)\n", pos);
+ iinfo->is_automatic_arg[pos] = TRUE;
+ }
+ }
+ }
+
+ /* We need to undo the special handling that GInitiallyUnowned
+ * descendants receive from gobject-introspection: values of this type
+ * are always marked transfer=none, even for constructors. */
+ if (iinfo->is_constructor &&
+ g_type_info_get_tag (iinfo->return_type_info) == GI_TYPE_TAG_INTERFACE)
+ {
+ GIBaseInfo * interface = g_type_info_get_interface (iinfo->return_type_info);
+ if (GI_IS_REGISTERED_TYPE_INFO (interface) &&
+ g_type_is_a (g_registered_type_info_get_g_type (interface),
+ G_TYPE_INITIALLY_UNOWNED))
+ {
+ iinfo->return_type_transfer = GI_TRANSFER_EVERYTHING;
+ }
+ g_base_info_unref ((GIBaseInfo *) interface);
+ }
+}
+
+static void
+clear_invocation_info (GPerlI11nInvocationInfo *iinfo)
+{
+ g_slist_free (iinfo->free_after_call);
+
+ /* The actual callback infos might be needed later, so we cannot free
+ * them here. */
+ g_slist_free (iinfo->callback_infos);
+
+ g_slist_foreach (iinfo->array_infos, (GFunc) g_free, NULL);
+ g_slist_free (iinfo->array_infos);
+
+ g_base_info_unref ((GIBaseInfo *) iinfo->return_type_info);
+}
+
+static gpointer
+allocate_out_mem (GITypeInfo *arg_type)
+{
+ GIBaseInfo *interface_info;
+ GIInfoType type;
+
+ interface_info = g_type_info_get_interface (arg_type);
+ g_assert (interface_info);
+ type = g_base_info_get_type (interface_info);
+ g_base_info_unref (interface_info);
+
+ switch (type) {
+ case GI_INFO_TYPE_STRUCT:
+ {
+ /* No plain g_struct_info_get_size (interface_info) here so
+ * that we get the GValue override. */
+ gsize size = size_of_interface (arg_type);
+ return g_malloc0 (size);
+ }
+ default:
+ g_assert_not_reached ();
+ return NULL;
+ }
+}
+
+static void
+handle_automatic_arg (guint pos,
+ GIArgument * arg,
+ GPerlI11nInvocationInfo * invocation_info)
+{
+ GSList *l;
+
+ /* array length */
+ for (l = invocation_info->array_infos; l != NULL; l = l->next) {
+ GPerlI11nArrayInfo *ainfo = l->data;
+ if (pos == ainfo->length_pos) {
+ dwarn (" setting automatic arg %d (array length) to %d\n",
+ pos, ainfo->length);
+ /* FIXME: Is it OK to always use v_size here? */
+ arg->v_size = ainfo->length;
+ return;
+ }
+ }
+
+ /* callback destroy notify */
+ for (l = invocation_info->callback_infos; l != NULL; l = l->next) {
+ GPerlI11nCallbackInfo *cinfo = l->data;
+ if (pos == cinfo->notify_pos) {
+ dwarn (" setting automatic arg %d (destroy notify for calllback %p)\n",
+ pos, cinfo);
+ arg->v_pointer = release_callback;
+ return;
+ }
+ }
+
+ ccroak ("Could not handle automatic arg %d", pos);
+}
diff --git a/gperl-i11n-invoke-perl.c b/gperl-i11n-invoke-perl.c
new file mode 100644
index 0000000..a31a033
--- /dev/null
+++ b/gperl-i11n-invoke-perl.c
@@ -0,0 +1,222 @@
+static void
+invoke_callback (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata)
+{
+ GPerlI11nCallbackInfo *info;
+ GICallableInfo *cb_interface;
+ int n_args, i;
+ int in_inout;
+ GITypeInfo *return_type;
+ gboolean have_return_type;
+ int n_return_values, n_returned;
+ I32 context;
+ dGPERL_CALLBACK_MARSHAL_SP;
+
+ PERL_UNUSED_VAR (cif);
+
+ /* unwrap callback info struct from userdata */
+ info = (GPerlI11nCallbackInfo *) userdata;
+ cb_interface = (GICallableInfo *) info->interface;
+
+ /* set perl context */
+ GPERL_CALLBACK_MARSHAL_INIT (info);
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK (SP);
+
+ /* find arguments; use type information from interface to find in and
+ * in-out args and their types, count in-out and out args, and find
+ * suitable converters; push in and in-out arguments onto the perl
+ * stack */
+ in_inout = 0;
+ n_args = g_callable_info_get_n_args (cb_interface);
+ for (i = 0; i < n_args; i++) {
+ GIArgInfo *arg_info = g_callable_info_get_arg (cb_interface, i);
+ GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
+ GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
+ GIDirection direction = g_arg_info_get_direction (arg_info);
+
+ /* the closure argument, which we handle separately, is marked
+ * by having get_closure == i */
+ if (g_arg_info_get_closure (arg_info) == i) {
+ g_base_info_unref ((GIBaseInfo *) arg_info);
+ g_base_info_unref ((GIBaseInfo *) arg_type);
+ continue;
+ }
+
+ dwarn ("arg info: %p\n"
+ " direction: %d\n"
+ " is return value: %d\n"
+ " is optional: %d\n"
+ " may be null: %d\n"
+ " transfer: %d\n",
+ arg_info,
+ g_arg_info_get_direction (arg_info),
+ g_arg_info_is_return_value (arg_info),
+ g_arg_info_is_optional (arg_info),
+ g_arg_info_may_be_null (arg_info),
+ g_arg_info_get_ownership_transfer (arg_info));
+
+ dwarn ("arg type: %p\n"
+ " is pointer: %d\n"
+ " tag: %d\n",
+ arg_type,
+ g_type_info_is_pointer (arg_type),
+ g_type_info_get_tag (arg_type));
+
+ if (direction == GI_DIRECTION_IN ||
+ direction == GI_DIRECTION_INOUT)
+ {
+ GIArgument arg;
+ raw_to_arg (args[i], &arg, arg_type);
+ XPUSHs (sv_2mortal (arg_to_sv (&arg, arg_type, transfer, NULL)));
+ }
+
+ if (direction == GI_DIRECTION_INOUT ||
+ direction == GI_DIRECTION_OUT)
+ {
+ in_inout++;
+ }
+
+ g_base_info_unref ((GIBaseInfo *) arg_info);
+ g_base_info_unref ((GIBaseInfo *) arg_type);
+ }
+
+ /* push user data onto the Perl stack */
+ if (info->data)
+ XPUSHs (sv_2mortal (SvREFCNT_inc (info->data)));
+
+ PUTBACK;
+
+ /* put the target package name into the invocant so that the vfunc
+ * fallback code knows whose parent to chain up to. */
+ if (info->package_name) {
+ GObject *object = * (GObject **) args[0];
+ g_assert (G_IS_OBJECT (object));
+ g_object_set_qdata (object, VFUNC_TARGET_PACKAGE_QUARK, info->package_name);
+ }
+
+ /* determine suitable Perl call context; return_type is freed further
+ * below */
+ return_type = g_callable_info_get_return_type (cb_interface);
+ have_return_type =
+ GI_TYPE_TAG_VOID != g_type_info_get_tag (return_type);
+
+ context = G_VOID | G_DISCARD;
+ if (have_return_type) {
+ context = in_inout > 0
+ ? G_ARRAY
+ : G_SCALAR;
+ } else {
+ if (in_inout == 1) {
+ context = G_SCALAR;
+ } else if (in_inout > 1) {
+ context = G_ARRAY;
+ }
+ }
+
+ /* do the call, demand #in-out+#out+#return-value return values */
+ n_return_values = have_return_type
+ ? in_inout + 1
+ : in_inout;
+ n_returned = info->sub_name
+ ? call_method (info->sub_name, context)
+ : call_sv (info->code, context);
+ if (n_return_values != 0 && n_returned != n_return_values) {
+ ccroak ("callback returned %d values "
+ "but is supposed to return %d values",
+ n_returned, n_return_values);
+ }
+
+ if (info->package_name) {
+ GObject *object = * (GObject **) args[0];
+ g_assert (G_IS_OBJECT (object));
+ g_object_set_qdata (object, VFUNC_TARGET_PACKAGE_QUARK, NULL);
+ }
+
+ SPAGAIN;
+
+ /* convert in-out and out values and stuff them back into args */
+ if (in_inout > 0) {
+ SV **returned_values;
+ int out_index;
+
+ returned_values = g_new0 (SV *, in_inout);
+
+ /* pop scalars off the stack and put them into the array;
+ * reverse the order since POPs pops items off of the end of
+ * the stack. */
+ for (i = 0; i < in_inout; i++) {
+ /* FIXME: Does this leak the sv? Should we check the
+ * transfer setting? */
+ returned_values[in_inout - i - 1] = newSVsv (POPs);
+ }
+
+ out_index = 0;
+ for (i = 0; i < n_args; i++) {
+ GIArgInfo *arg_info = g_callable_info_get_arg (cb_interface, i);
+ GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
+ GIDirection direction = g_arg_info_get_direction (arg_info);
+
+ if (direction == GI_DIRECTION_INOUT ||
+ direction == GI_DIRECTION_OUT)
+ {
+ GIArgument tmp_arg;
+ GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
+ gboolean may_be_null = g_arg_info_may_be_null (arg_info);
+ sv_to_arg (returned_values[out_index], &tmp_arg,
+ arg_info, arg_type,
+ transfer, may_be_null, NULL);
+ arg_to_raw (&tmp_arg, args[i], arg_type);
+ out_index++;
+ }
+ }
+
+ g_free (returned_values);
+ }
+
+ /* store return value in resp, if any */
+ if (have_return_type) {
+ GIArgument arg;
+ GITypeInfo *type_info;
+ GITransfer transfer;
+ gboolean may_be_null;
+
+ type_info = g_callable_info_get_return_type (cb_interface);
+ transfer = g_callable_info_get_caller_owns (cb_interface);
+ may_be_null = g_callable_info_may_return_null (cb_interface);
+
+ dwarn ("ret type: %p\n"
+ " is pointer: %d\n"
+ " tag: %d\n",
+ type_info,
+ g_type_info_is_pointer (type_info),
+ g_type_info_get_tag (type_info));
+
+ /* FIXME: Does this leak the sv? */
+ sv_to_arg (newSVsv (POPs), &arg, NULL, type_info,
+ transfer, may_be_null, NULL);
+ arg_to_raw (&arg, resp, type_info);
+
+ g_base_info_unref ((GIBaseInfo *) type_info);
+ }
+
+ PUTBACK;
+
+ g_base_info_unref ((GIBaseInfo *) return_type);
+
+ FREETMPS;
+ LEAVE;
+
+ /* FIXME: We can't just free everything here because ffi will use parts
+ * of this after we've returned.
+ *
+ * if (info->free_after_use) {
+ * release_callback (info);
+ * }
+ *
+ * Gjs uses a global list of callback infos instead and periodically
+ * frees unused ones.
+ */
+}
diff --git a/gperl-i11n-marshal-arg.c b/gperl-i11n-marshal-arg.c
new file mode 100644
index 0000000..7bdd272
--- /dev/null
+++ b/gperl-i11n-marshal-arg.c
@@ -0,0 +1,232 @@
+/* transfer and may_be_null can be gotten from arg_info, but sv_to_arg is also
+ * called from places which don't have access to a GIArgInfo. */
+static void
+sv_to_arg (SV * sv,
+ GIArgument * arg,
+ GIArgInfo * arg_info,
+ GITypeInfo * type_info,
+ GITransfer transfer,
+ gboolean may_be_null,
+ GPerlI11nInvocationInfo * invocation_info)
+{
+ GITypeTag tag = g_type_info_get_tag (type_info);
+
+ memset (arg, 0, sizeof (GIArgument));
+
+ if (!gperl_sv_is_defined (sv))
+ /* Interfaces and void types need to be able to handle undef
+ * separately. */
+ if (!may_be_null && tag != GI_TYPE_TAG_INTERFACE
+ && tag != GI_TYPE_TAG_VOID)
+ ccroak ("undefined value for mandatory argument '%s' encountered",
+ g_base_info_get_name ((GIBaseInfo *) arg_info));
+
+ switch (tag) {
+ case GI_TYPE_TAG_VOID:
+ arg->v_pointer = handle_void_arg (sv, invocation_info);
+ break;
+
+ case GI_TYPE_TAG_BOOLEAN:
+ arg->v_boolean = SvTRUE (sv);
+ break;
+
+ case GI_TYPE_TAG_INT8:
+ arg->v_int8 = (gint8) SvIV (sv);
+ break;
+
+ case GI_TYPE_TAG_UINT8:
+ arg->v_uint8 = (guint8) SvUV (sv);
+ break;
+
+ case GI_TYPE_TAG_INT16:
+ arg->v_int16 = (gint16) SvIV (sv);
+ break;
+
+ case GI_TYPE_TAG_UINT16:
+ arg->v_uint16 = (guint16) SvUV (sv);
+ break;
+
+ case GI_TYPE_TAG_INT32:
+ arg->v_int32 = (gint32) SvIV (sv);
+ break;
+
+ case GI_TYPE_TAG_UINT32:
+ arg->v_uint32 = (guint32) SvUV (sv);
+ break;
+
+ case GI_TYPE_TAG_INT64:
+ arg->v_int64 = SvGInt64 (sv);
+ break;
+
+ case GI_TYPE_TAG_UINT64:
+ arg->v_uint64 = SvGUInt64 (sv);
+ break;
+
+ case GI_TYPE_TAG_FLOAT:
+ arg->v_float = (gfloat) SvNV (sv);
+ break;
+
+ case GI_TYPE_TAG_DOUBLE:
+ arg->v_double = SvNV (sv);
+ break;
+
+ case GI_TYPE_TAG_UNICHAR:
+ arg->v_uint32 = g_utf8_get_char (SvGChar (sv));
+ break;
+
+ case GI_TYPE_TAG_GTYPE:
+ /* GType == gsize */
+ arg->v_size = gperl_type_from_package (SvPV_nolen (sv));
+ if (!arg->v_size)
+ arg->v_size = g_type_from_name (SvPV_nolen (sv));
+ break;
+
+ case GI_TYPE_TAG_ARRAY:
+ arg->v_pointer = sv_to_array (transfer, type_info, sv, invocation_info);
+ break;
+
+ case GI_TYPE_TAG_INTERFACE:
+ dwarn (" type %p -> interface\n", type_info);
+ sv_to_interface (arg_info, type_info, sv, arg,
+ invocation_info);
+ break;
+
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ arg->v_pointer = sv_to_glist (transfer, type_info, sv);
+ break;
+
+ case GI_TYPE_TAG_GHASH:
+ arg->v_pointer = sv_to_ghash (transfer, type_info, sv);
+ break;
+
+ case GI_TYPE_TAG_ERROR:
+ ccroak ("FIXME - A GError as an in/inout arg? Should never happen!");
+ break;
+
+ case GI_TYPE_TAG_UTF8:
+ arg->v_string = gperl_sv_is_defined (sv) ? SvGChar (sv) : NULL;
+ if (transfer >= GI_TRANSFER_CONTAINER)
+ arg->v_string = g_strdup (arg->v_string);
+ break;
+
+ case GI_TYPE_TAG_FILENAME:
+ /* FIXME: Is it correct to use gperl_filename_from_sv here? */
+ arg->v_string = gperl_sv_is_defined (sv) ? gperl_filename_from_sv (sv) : NULL;
+ if (transfer >= GI_TRANSFER_CONTAINER)
+ arg->v_string = g_strdup (arg->v_string);
+ break;
+
+ default:
+ ccroak ("Unhandled info tag %d in sv_to_arg", tag);
+ }
+}
+
+static SV *
+arg_to_sv (GIArgument * arg,
+ GITypeInfo * info,
+ GITransfer transfer,
+ GPerlI11nInvocationInfo *iinfo)
+{
+ GITypeTag tag = g_type_info_get_tag (info);
+ gboolean own = transfer >= GI_TRANSFER_CONTAINER;
+
+ dwarn (" arg_to_sv: info %p with type tag %d (%s)\n",
+ info, tag, g_type_tag_to_string (tag));
+
+ switch (tag) {
+ case GI_TYPE_TAG_VOID:
+ dwarn (" argument with no type information -> undef\n");
+ return &PL_sv_undef;
+
+ case GI_TYPE_TAG_BOOLEAN:
+ return boolSV (arg->v_boolean);
+
+ case GI_TYPE_TAG_INT8:
+ return newSViv (arg->v_int8);
+
+ case GI_TYPE_TAG_UINT8:
+ return newSVuv (arg->v_uint8);
+
+ case GI_TYPE_TAG_INT16:
+ return newSViv (arg->v_int16);
+
+ case GI_TYPE_TAG_UINT16:
+ return newSVuv (arg->v_uint16);
+
+ case GI_TYPE_TAG_INT32:
+ return newSViv (arg->v_int32);
+
+ case GI_TYPE_TAG_UINT32:
+ return newSVuv (arg->v_uint32);
+
+ case GI_TYPE_TAG_INT64:
+ return newSVGInt64 (arg->v_int64);
+
+ case GI_TYPE_TAG_UINT64:
+ return newSVGUInt64 (arg->v_uint64);
+
+ case GI_TYPE_TAG_FLOAT:
+ return newSVnv (arg->v_float);
+
+ case GI_TYPE_TAG_DOUBLE:
+ return newSVnv (arg->v_double);
+
+ case GI_TYPE_TAG_UNICHAR:
+ {
+ SV *sv;
+ gchar buffer[6];
+ gint length = g_unichar_to_utf8 (arg->v_uint32, buffer);
+ sv = newSVpv (buffer, length);
+ SvUTF8_on (sv);
+ return sv;
+ }
+
+ case GI_TYPE_TAG_GTYPE: {
+ /* GType == gsize */
+ const char *package = gperl_package_from_type (arg->v_size);
+ if (!package)
+ package = g_type_name (arg->v_size);
+ return newSVpv (package, PL_na);
+ }
+
+ case GI_TYPE_TAG_ARRAY:
+ return array_to_sv (info, arg->v_pointer, transfer, iinfo);
+
+ case GI_TYPE_TAG_INTERFACE:
+ return interface_to_sv (info, arg, own);
+
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ return glist_to_sv (info, arg->v_pointer, transfer);
+
+ case GI_TYPE_TAG_GHASH:
+ return ghash_to_sv (info, arg->v_pointer, transfer);
+
+ case GI_TYPE_TAG_ERROR:
+ ccroak ("FIXME - GI_TYPE_TAG_ERROR");
+ break;
+
+ case GI_TYPE_TAG_UTF8:
+ {
+ SV *sv = newSVGChar (arg->v_string);
+ if (own)
+ g_free (arg->v_string);
+ return sv;
+ }
+
+ case GI_TYPE_TAG_FILENAME:
+ {
+ /* FIXME: Is it correct to use gperl_sv_from_filename here? */
+ SV *sv = gperl_sv_from_filename (arg->v_string);
+ if (own)
+ g_free (arg->v_string);
+ return sv;
+ }
+
+ default:
+ ccroak ("Unhandled info tag %d in arg_to_sv", tag);
+ }
+
+ return NULL;
+}
diff --git a/gperl-i11n-marshal-array.c b/gperl-i11n-marshal-array.c
new file mode 100644
index 0000000..15405f7
--- /dev/null
+++ b/gperl-i11n-marshal-array.c
@@ -0,0 +1,167 @@
+static SV *
+array_to_sv (GITypeInfo *info,
+ gpointer pointer,
+ GITransfer transfer,
+ GPerlI11nInvocationInfo *iinfo)
+{
+ GITypeInfo *param_info;
+ gboolean is_zero_terminated;
+ gsize item_size;
+ GITransfer item_transfer;
+ gssize length, i;
+ AV *av;
+
+ if (pointer == NULL) {
+ return &PL_sv_undef;
+ }
+
+ is_zero_terminated = g_type_info_is_zero_terminated (info);
+ param_info = g_type_info_get_param_type (info, 0);
+ item_size = size_of_type_info (param_info);
+
+ /* FIXME: What about an array containing arrays of strings, where the
+ * outer array is GI_TRANSFER_EVERYTHING but the inner arrays are
+ * GI_TRANSFER_CONTAINER? */
+ item_transfer = transfer == GI_TRANSFER_EVERYTHING
+ ? GI_TRANSFER_EVERYTHING
+ : GI_TRANSFER_NOTHING;
+
+ if (is_zero_terminated) {
+ length = g_strv_length (pointer);
+ } else {
+ length = g_type_info_get_array_fixed_size (info);
+ if (length < 0) {
+ guint length_pos = g_type_info_get_array_length (info);
+ g_assert (iinfo != NULL);
+ /* FIXME: Is it OK to always use v_size here? */
+ length = iinfo->aux_args[length_pos].v_size;
+ }
+ }
+
+ if (length < 0) {
+ ccroak ("Could not determine the length of the array");
+ }
+
+ av = newAV ();
+
+ dwarn (" C array: pointer %p, length %d, item size %d, "
+ "param_info %p with type tag %d (%s)\n",
+ pointer,
+ length,
+ item_size,
+ param_info,
+ g_type_info_get_tag (param_info),
+ g_type_tag_to_string (g_type_info_get_tag (param_info)));
+
+ for (i = 0; i < length; i++) {
+ GIArgument *arg;
+ SV *value;
+ arg = pointer + i * item_size;
+ value = arg_to_sv (arg, param_info, item_transfer, iinfo);
+ if (value)
+ av_push (av, value);
+ }
+
+ if (transfer >= GI_TRANSFER_CONTAINER)
+ g_free (pointer);
+
+ g_base_info_unref ((GIBaseInfo *) param_info);
+
+ return newRV_noinc ((SV *) av);
+}
+
+static gpointer
+sv_to_array (GITransfer transfer,
+ GITypeInfo *type_info,
+ SV *sv,
+ GPerlI11nInvocationInfo *iinfo)
+{
+ AV *av;
+ GITransfer item_transfer;
+ GITypeInfo *param_info;
+ GITypeTag param_tag;
+ gint i, length, length_pos;
+ GPerlI11nArrayInfo *array_info = NULL;
+ GArray *array;
+ gboolean is_zero_terminated = FALSE;
+ gsize item_size;
+ gboolean need_struct_value_semantics;
+
+ dwarn ("%s: sv %p\n", G_STRFUNC, sv);
+
+ /* Add an array info entry even before the undef check so that the
+ * corresponding length arg is set to zero later by
+ * handle_automatic_arg. */
+ length_pos = g_type_info_get_array_length (type_info);
+ if (length_pos >= 0) {
+ array_info = g_new0 (GPerlI11nArrayInfo, 1);
+ array_info->length_pos = length_pos;
+ array_info->length = 0;
+ iinfo->array_infos = g_slist_prepend (iinfo->array_infos, array_info);
+ }
+
+ if (sv == &PL_sv_undef)
+ return NULL;
+
+ if (!gperl_sv_is_array_ref (sv))
+ ccroak ("need an array ref to convert to GArray");
+
+ av = (AV *) SvRV (sv);
+
+ item_transfer = transfer == GI_TRANSFER_CONTAINER
+ ? GI_TRANSFER_NOTHING
+ : transfer;
+
+ param_info = g_type_info_get_param_type (type_info, 0);
+ param_tag = g_type_info_get_tag (param_info);
+ dwarn (" GArray: param_info %p with type tag %d (%s) and transfer %d\n",
+ param_info, param_tag,
+ g_type_tag_to_string (g_type_info_get_tag (param_info)),
+ transfer);
+
+ is_zero_terminated = g_type_info_is_zero_terminated (type_info);
+ item_size = size_of_type_info (param_info);
+ length = av_len (av) + 1;
+ array = g_array_sized_new (is_zero_terminated, FALSE, item_size, length);
+
+ /* Arrays containing non-basic types as non-pointers need to be treated
+ * specially. Prime example: GValue *values = g_new0 (GValue, n);
+ */
+ need_struct_value_semantics =
+ /* is a compound type, and... */
+ !G_TYPE_TAG_IS_BASIC (param_tag) &&
+ /* ... a non-pointer is wanted */
+ !g_type_info_is_pointer (param_info);
+ for (i = 0; i < length; i++) {
+ SV **svp;
+ svp = av_fetch (av, i, 0);
+ if (svp && gperl_sv_is_defined (*svp)) {
+ GIArgument arg;
+
+ dwarn (" converting SV %p\n", *svp);
+ /* FIXME: Is it OK to always allow undef here? */
+ sv_to_arg (*svp, &arg, NULL, param_info,
+ item_transfer, TRUE, NULL);
+
+ if (need_struct_value_semantics) {
+ /* Copy from the memory area pointed to by
+ * arg.v_pointer. */
+ g_array_insert_vals (array, i, arg.v_pointer, 1);
+ } else {
+ /* Copy from &arg, i.e. the memory area that is
+ * arg. */
+ g_array_insert_val (array, i, arg);
+ }
+ }
+ }
+
+ dwarn (" -> array %p of size %d\n", array, array->len);
+
+ if (length_pos >= 0) {
+ array_info->length = length;
+ }
+
+ g_base_info_unref ((GIBaseInfo *) param_info);
+
+ return g_array_free (array, FALSE);
+}
diff --git a/gperl-i11n-marshal-callback.c b/gperl-i11n-marshal-callback.c
new file mode 100644
index 0000000..4759a9c
--- /dev/null
+++ b/gperl-i11n-marshal-callback.c
@@ -0,0 +1,82 @@
+static gpointer
+handle_callback_arg (GIArgInfo * arg_info,
+ GITypeInfo * type_info,
+ SV * sv,
+ GPerlI11nInvocationInfo * invocation_info)
+{
+ GPerlI11nCallbackInfo *callback_info;
+
+ GSList *l;
+ for (l = invocation_info->callback_infos; l != NULL; l = l->next) {
+ GPerlI11nCallbackInfo *callback_info = l->data;
+ if (invocation_info->current_pos == callback_info->notify_pos) {
+ dwarn (" destroy notify for callback %p\n",
+ callback_info);
+ /* Decrease the dynamic stack offset so that this
+ * destroy notify callback doesn't consume any Perl
+ * value from the stack. */
+ invocation_info->dynamic_stack_offset--;
+ return release_callback;
+ }
+ }
+
+ callback_info = create_callback_closure (type_info, sv);
+ callback_info->data_pos = g_arg_info_get_closure (arg_info);
+ callback_info->notify_pos = g_arg_info_get_destroy (arg_info);
+ callback_info->free_after_use = FALSE;
+
+ dwarn (" callback data at %d, destroy at %d\n",
+ callback_info->data_pos, callback_info->notify_pos);
+
+ switch (g_arg_info_get_scope (arg_info)) {
+ case GI_SCOPE_TYPE_CALL:
+ dwarn (" callback has scope 'call'\n");
+ invocation_info->free_after_call
+ = g_slist_prepend (invocation_info->free_after_call,
+ callback_info);
+ break;
+ case GI_SCOPE_TYPE_NOTIFIED:
+ dwarn (" callback has scope 'notified'\n");
+ /* This case is already taken care of by the notify
+ * stuff above */
+ break;
+ case GI_SCOPE_TYPE_ASYNC:
+ dwarn (" callback has scope 'async'\n");
+ /* FIXME: callback_info->free_after_use = TRUE; */
+ break;
+ default:
+ ccroak ("unhandled scope type %d encountered",
+ g_arg_info_get_scope (arg_info));
+ }
+
+ invocation_info->callback_infos =
+ g_slist_prepend (invocation_info->callback_infos,
+ callback_info);
+
+ dwarn (" returning closure %p from info %p\n",
+ callback_info->closure, callback_info);
+ return callback_info->closure;
+}
+
+static gpointer
+handle_void_arg (SV * sv,
+ GPerlI11nInvocationInfo * invocation_info)
+{
+ gpointer pointer = NULL;
+ gboolean is_user_data = FALSE;
+ GSList *l;
+ for (l = invocation_info->callback_infos; l != NULL; l = l->next) {
+ GPerlI11nCallbackInfo *callback_info = l->data;
+ if (callback_info->data_pos == invocation_info->current_pos) {
+ is_user_data = TRUE;
+ dwarn (" user data for callback %p\n",
+ callback_info);
+ attach_callback_data (callback_info, sv);
+ pointer = callback_info;
+ break; /* out of the for loop */
+ }
+ }
+ if (!is_user_data)
+ ccroak ("encountered void pointer that is not callback user data");
+ return pointer;
+}
diff --git a/gperl-i11n-marshal-hash.c b/gperl-i11n-marshal-hash.c
new file mode 100644
index 0000000..bd664f2
--- /dev/null
+++ b/gperl-i11n-marshal-hash.c
@@ -0,0 +1,175 @@
+static SV *
+ghash_to_sv (GITypeInfo *info,
+ gpointer pointer,
+ GITransfer transfer)
+{
+ GITypeInfo *key_param_info, *value_param_info;
+#ifdef NOISY
+ GITypeTag key_type_tag, value_type_tag;
+#endif
+ gpointer key_p, value_p;
+ GITransfer item_transfer;
+ GHashTableIter iter;
+ HV *hv;
+
+ if (pointer == NULL) {
+ return &PL_sv_undef;
+ }
+
+ item_transfer = transfer == GI_TRANSFER_EVERYTHING
+ ? GI_TRANSFER_EVERYTHING
+ : GI_TRANSFER_NOTHING;
+
+ key_param_info = g_type_info_get_param_type (info, 0);
+ value_param_info = g_type_info_get_param_type (info, 1);
+
+#ifdef NOISY
+ key_type_tag = g_type_info_get_tag (key_param_info);
+ value_type_tag = g_type_info_get_tag (value_param_info);
+#endif
+
+ dwarn (" GHashTable: pointer %p\n"
+ " key type tag %d (%s)\n"
+ " value type tag %d (%s)\n",
+ pointer,
+ key_type_tag, g_type_tag_to_string (key_type_tag),
+ value_type_tag, g_type_tag_to_string (value_type_tag));
+
+ hv = newHV ();
+
+ g_hash_table_iter_init (&iter, pointer);
+ while (g_hash_table_iter_next (&iter, &key_p, &value_p)) {
+ GIArgument arg = { 0, };
+ SV *key_sv, *value_sv;
+
+ dwarn (" converting key pointer %p\n", key_p);
+ arg.v_pointer = key_p;
+ key_sv = arg_to_sv (&arg, key_param_info, item_transfer, NULL);
+ if (key_sv == NULL)
+ break;
+
+ dwarn (" converting value pointer %p\n", value_p);
+ arg.v_pointer = value_p;
+ value_sv = arg_to_sv (&arg, value_param_info, item_transfer, NULL);
+ if (value_sv == NULL)
+ break;
+
+ (void) hv_store_ent (hv, key_sv, value_sv, 0);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) key_param_info);
+ g_base_info_unref ((GIBaseInfo *) value_param_info);
+
+ return newRV_noinc ((SV *) hv);
+}
+
+static gpointer
+sv_to_ghash (GITransfer transfer,
+ GITypeInfo *type_info,
+ SV *sv)
+{
+ HV *hv;
+ HE *he;
+ GITransfer item_transfer;
+ gpointer hash;
+ GITypeInfo *key_param_info, *value_param_info;
+ GITypeTag key_type_tag;
+ GHashFunc hash_func;
+ GEqualFunc equal_func;
+ I32 n_keys;
+
+ dwarn ("%s: sv %p\n", G_STRFUNC, sv);
+
+ if (sv == &PL_sv_undef)
+ return NULL;
+
+ if (!gperl_sv_is_hash_ref (sv))
+ ccroak ("need an hash ref to convert to GHashTable");
+
+ hv = (HV *) SvRV (sv);
+
+ item_transfer = GI_TRANSFER_NOTHING;
+ switch (transfer) {
+ case GI_TRANSFER_EVERYTHING:
+ item_transfer = GI_TRANSFER_EVERYTHING;
+ break;
+ case GI_TRANSFER_CONTAINER:
+ /* nothing special to do */
+ break;
+ case GI_TRANSFER_NOTHING:
+ /* FIXME: need to free hash after call */
+ break;
+ }
+
+ key_param_info = g_type_info_get_param_type (type_info, 0);
+ value_param_info = g_type_info_get_param_type (type_info, 1);
+
+ key_type_tag = g_type_info_get_tag (key_param_info);
+
+ switch (key_type_tag)
+ {
+ case GI_TYPE_TAG_FILENAME:
+ case GI_TYPE_TAG_UTF8:
+ hash_func = g_str_hash;
+ equal_func = g_str_equal;
+ break;
+
+ default:
+ hash_func = NULL;
+ equal_func = NULL;
+ break;
+ }
+
+ dwarn (" GHashTable with transfer %d\n"
+ " key_param_info %p with type tag %d (%s)\n"
+ " value_param_info %p with type tag %d (%s)\n",
+ transfer,
+ key_param_info,
+ g_type_info_get_tag (key_param_info),
+ g_type_tag_to_string (g_type_info_get_tag (key_param_info)),
+ value_param_info,
+ g_type_info_get_tag (value_param_info),
+ g_type_tag_to_string (g_type_info_get_tag (value_param_info)));
+
+ hash = g_hash_table_new (hash_func, equal_func);
+
+ n_keys = hv_iterinit (hv);
+ if (n_keys == 0)
+ goto out;
+
+ while ((he = hv_iternext (hv)) != NULL) {
+ SV *sv;
+ GIArgument arg = { 0, };
+ gpointer key_p, value_p;
+
+ key_p = value_p = NULL;
+
+ sv = hv_iterkeysv (he);
+ if (sv && gperl_sv_is_defined (sv)) {
+ dwarn (" converting key SV %p\n", sv);
+ /* FIXME: Is it OK to always allow undef here? */
+ sv_to_arg (sv, &arg, NULL, key_param_info,
+ item_transfer, TRUE, NULL);
+ key_p = arg.v_pointer;
+ }
+
+ sv = hv_iterval (hv, he);
+ if (sv && gperl_sv_is_defined (sv)) {
+ dwarn (" converting value SV %p\n", sv);
+ sv_to_arg (sv, &arg, NULL, key_param_info,
+ item_transfer, TRUE, NULL);
+ value_p = arg.v_pointer;
+ }
+
+ if (key_p != NULL && value_p != NULL)
+ g_hash_table_insert (hash, key_p, value_p);
+ }
+
+out:
+ dwarn (" -> hash %p of size %d\n", hash, g_hash_table_size (hash));
+
+ g_base_info_unref ((GIBaseInfo *) key_param_info);
+ g_base_info_unref ((GIBaseInfo *) value_param_info);
+
+ return hash;
+}
diff --git a/gperl-i11n-marshal-interface.c b/gperl-i11n-marshal-interface.c
new file mode 100644
index 0000000..db76fea
--- /dev/null
+++ b/gperl-i11n-marshal-interface.c
@@ -0,0 +1,205 @@
+static gpointer
+instance_sv_to_pointer (GICallableInfo *info, SV *sv)
+{
+ // We do *not* own container.
+ GIBaseInfo *container = g_base_info_get_container (info);
+ GIInfoType info_type = g_base_info_get_type (container);
+ gpointer pointer = NULL;
+
+ /* FIXME: Much of this code is duplicated in sv_to_interface. */
+
+ dwarn (" instance_sv_to_pointer: container name: %s, info type: %d\n",
+ g_base_info_get_name (container),
+ info_type);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_OBJECT:
+ case GI_INFO_TYPE_INTERFACE:
+ pointer = gperl_get_object (sv);
+ dwarn (" -> object pointer: %p\n", pointer);
+ break;
+
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ case GI_INFO_TYPE_UNION:
+ {
+ GType type = g_registered_type_info_get_g_type (
+ (GIRegisteredTypeInfo *) container);
+ if (!type || type == G_TYPE_NONE) {
+ dwarn (" unboxed type\n");
+ pointer = sv_to_struct (GI_TRANSFER_NOTHING,
+ container,
+ info_type,
+ sv);
+ } else {
+ dwarn (" boxed type: %s (%d)\n",
+ g_type_name (type), type);
+ pointer = gperl_get_boxed_check (sv, type);
+ }
+ dwarn (" -> boxed pointer: %p\n", pointer);
+ break;
+ }
+
+ default:
+ ccroak ("instance_sv_to_pointer: Don't know how to handle info type %d", info_type);
+ }
+
+ return pointer;
+}
+
+static void
+sv_to_interface (GIArgInfo * arg_info,
+ GITypeInfo * type_info,
+ SV * sv,
+ GIArgument * arg,
+ GPerlI11nInvocationInfo * invocation_info)
+{
+ GIBaseInfo *interface;
+ GIInfoType info_type;
+
+ interface = g_type_info_get_interface (type_info);
+ if (!interface)
+ ccroak ("Could not convert sv %p to pointer", sv);
+ info_type = g_base_info_get_type (interface);
+
+ dwarn (" interface %p (%s) of type %d\n",
+ interface, g_base_info_get_name (interface), info_type);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_OBJECT:
+ case GI_INFO_TYPE_INTERFACE:
+ /* FIXME: Check transfer setting. */
+ arg->v_pointer = gperl_get_object (sv);
+ break;
+
+ case GI_INFO_TYPE_UNION:
+ case GI_INFO_TYPE_STRUCT:
+ case GI_INFO_TYPE_BOXED:
+ {
+ /* FIXME: What about pass-by-value here? */
+ GType type = g_registered_type_info_get_g_type (
+ (GIRegisteredTypeInfo *) interface);
+ if (!type || type == G_TYPE_NONE) {
+ GITransfer transfer = arg_info
+ ? g_arg_info_get_ownership_transfer (arg_info)
+ : GI_TRANSFER_NOTHING;
+ dwarn (" unboxed type\n");
+ arg->v_pointer = sv_to_struct (transfer,
+ interface,
+ info_type,
+ sv);
+ } else if (type == G_TYPE_CLOSURE) {
+ /* FIXME: User cannot supply user data. */
+ dwarn (" closure type\n");
+ arg->v_pointer = gperl_closure_new (sv, NULL, FALSE);
+ } else if (type == G_TYPE_VALUE) {
+ dwarn (" value type\n");
+ arg->v_pointer = SvGValueWrapper (sv);
+ if (!arg->v_pointer)
+ ccroak ("Cannot convert arbitrary SV to GValue");
+ } else {
+ dwarn (" boxed type: %s (%d)\n",
+ g_type_name (type), type);
+ /* FIXME: Check transfer setting. */
+ arg->v_pointer = gperl_get_boxed_check (sv, type);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_ENUM:
+ {
+ GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
+ /* FIXME: Check storage type? */
+ arg->v_long = gperl_convert_enum (type, sv);
+ break;
+ }
+
+ case GI_INFO_TYPE_FLAGS:
+ {
+ GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
+ /* FIXME: Check storage type? */
+ arg->v_long = gperl_convert_flags (type, sv);
+ break;
+ }
+
+ case GI_INFO_TYPE_CALLBACK:
+ arg->v_pointer = handle_callback_arg (arg_info, type_info, sv,
+ invocation_info);
+ break;
+
+ default:
+ ccroak ("sv_to_interface: Don't know how to handle info type %d", info_type);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) interface);
+}
+
+static SV *
+interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own)
+{
+ GIBaseInfo *interface;
+ GIInfoType info_type;
+ SV *sv = NULL;
+
+ dwarn (" interface_to_sv: arg %p, info %p\n",
+ arg, info);
+
+ interface = g_type_info_get_interface (info);
+ if (!interface)
+ ccroak ("Could not convert arg %p to SV", arg);
+ info_type = g_base_info_get_type (interface);
+ dwarn (" info type: %d (%s)\n", info_type, g_info_type_to_string (info_type));
+
+ switch (info_type) {
+ case GI_INFO_TYPE_OBJECT:
+ case GI_INFO_TYPE_INTERFACE:
+ sv = gperl_new_object (arg->v_pointer, own);
+ break;
+
+ case GI_INFO_TYPE_UNION:
+ case GI_INFO_TYPE_STRUCT:
+ case GI_INFO_TYPE_BOXED:
+ {
+ /* FIXME: What about pass-by-value here? */
+ GType type;
+ type = g_registered_type_info_get_g_type (
+ (GIRegisteredTypeInfo *) interface);
+ if (!type || type == G_TYPE_NONE) {
+ dwarn (" unboxed type\n");
+ sv = struct_to_sv (interface, info_type, arg->v_pointer, own);
+ } else if (type == G_TYPE_VALUE) {
+ dwarn (" value type\n");
+ sv = gperl_sv_from_value (arg->v_pointer);
+ /* FIXME: Check 'own'. */
+ } else {
+ dwarn (" boxed type: %d (%s)\n",
+ type, g_type_name (type));
+ sv = gperl_new_boxed (arg->v_pointer, type, own);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_ENUM:
+ {
+ GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
+ /* FIXME: Is it right to just use v_long here? */
+ sv = gperl_convert_back_enum (type, arg->v_long);
+ break;
+ }
+
+ case GI_INFO_TYPE_FLAGS:
+ {
+ GType type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) interface);
+ /* FIXME: Is it right to just use v_long here? */
+ sv = gperl_convert_back_flags (type, arg->v_long);
+ break;
+ }
+
+ default:
+ ccroak ("interface_to_sv: Don't know how to handle info type %d", info_type);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) interface);
+
+ return sv;
+}
diff --git a/gperl-i11n-marshal-list.c b/gperl-i11n-marshal-list.c
new file mode 100644
index 0000000..fdc1fb5
--- /dev/null
+++ b/gperl-i11n-marshal-list.c
@@ -0,0 +1,120 @@
+static SV *
+glist_to_sv (GITypeInfo* info,
+ gpointer pointer,
+ GITransfer transfer)
+{
+ GITypeInfo *param_info;
+ GITransfer item_transfer;
+ gboolean is_slist;
+ GSList *i;
+ AV *av;
+ SV *value;
+
+ if (pointer == NULL) {
+ return &PL_sv_undef;
+ }
+
+ /* FIXME: What about an array containing arrays of strings, where the
+ * outer array is GI_TRANSFER_EVERYTHING but the inner arrays are
+ * GI_TRANSFER_CONTAINER? */
+ item_transfer = transfer == GI_TRANSFER_EVERYTHING
+ ? GI_TRANSFER_EVERYTHING
+ : GI_TRANSFER_NOTHING;
+
+ param_info = g_type_info_get_param_type (info, 0);
+ dwarn (" G(S)List: pointer %p, param_info %p with type tag %d (%s)\n",
+ pointer,
+ param_info,
+ g_type_info_get_tag (param_info),
+ g_type_tag_to_string (g_type_info_get_tag (param_info)));
+
+ is_slist = GI_TYPE_TAG_GSLIST == g_type_info_get_tag (info);
+
+ av = newAV ();
+ for (i = pointer; i; i = i->next) {
+ GIArgument arg = {0,};
+ dwarn (" converting pointer %p\n", i->data);
+ arg.v_pointer = i->data;
+ value = arg_to_sv (&arg, param_info, item_transfer, NULL);
+ if (value)
+ av_push (av, value);
+ }
+
+ if (transfer >= GI_TRANSFER_CONTAINER) {
+ if (is_slist)
+ g_slist_free (pointer);
+ else
+ g_list_free (pointer);
+ }
+
+ g_base_info_unref ((GIBaseInfo *) param_info);
+
+ return newRV_noinc ((SV *) av);
+}
+
+static gpointer
+sv_to_glist (GITransfer transfer, GITypeInfo * type_info, SV * sv)
+{
+ AV *av;
+ GITransfer item_transfer;
+ gpointer list = NULL;
+ GITypeInfo *param_info;
+ gboolean is_slist;
+ gint i, length;
+
+ dwarn ("%s: sv %p\n", G_STRFUNC, sv);
+
+ if (sv == &PL_sv_undef)
+ return NULL;
+
+ if (!gperl_sv_is_array_ref (sv))
+ ccroak ("need an array ref to convert to GList");
+ av = (AV *) SvRV (sv);
+
+ item_transfer = GI_TRANSFER_NOTHING;
+ switch (transfer) {
+ case GI_TRANSFER_EVERYTHING:
+ item_transfer = GI_TRANSFER_EVERYTHING;
+ break;
+ case GI_TRANSFER_CONTAINER:
+ /* nothing special to do */
+ break;
+ case GI_TRANSFER_NOTHING:
+ /* FIXME: need to free list after call */
+ break;
+ }
+
+ param_info = g_type_info_get_param_type (type_info, 0);
+ dwarn (" G(S)List: param_info %p with type tag %d (%s) and transfer %d\n",
+ param_info,
+ g_type_info_get_tag (param_info),
+ g_type_tag_to_string (g_type_info_get_tag (param_info)),
+ transfer);
+
+ is_slist = GI_TYPE_TAG_GSLIST == g_type_info_get_tag (type_info);
+
+ length = av_len (av) + 1;
+ for (i = 0; i < length; i++) {
+ SV **svp;
+ svp = av_fetch (av, i, 0);
+ if (svp && gperl_sv_is_defined (*svp)) {
+ GIArgument arg;
+ dwarn (" converting SV %p\n", *svp);
+ /* FIXME: Is it OK to always allow undef here? */
+ sv_to_arg (*svp, &arg, NULL, param_info,
+ item_transfer, TRUE, NULL);
+ /* ENHANCEME: Could use g_[s]list_prepend and
+ * later _reverse for efficiency. */
+ if (is_slist)
+ list = g_slist_append (list, arg.v_pointer);
+ else
+ list = g_list_append (list, arg.v_pointer);
+ }
+ }
+
+ dwarn (" -> list %p of length %d\n", list, g_list_length (list));
+
+ g_base_info_unref ((GIBaseInfo *) param_info);
+
+ return list;
+}
diff --git a/gperl-i11n-marshal-raw.c b/gperl-i11n-marshal-raw.c
new file mode 100644
index 0000000..5c9e05d
--- /dev/null
+++ b/gperl-i11n-marshal-raw.c
@@ -0,0 +1,155 @@
+#define CAST_RAW(raw, type) (*((type *) raw))
+
+static void
+raw_to_arg (gpointer raw, GIArgument *arg, GITypeInfo *info)
+{
+ GITypeTag tag = g_type_info_get_tag (info);
+
+ switch (tag) {
+ case GI_TYPE_TAG_VOID:
+ /* do nothing */
+ break;
+
+ case GI_TYPE_TAG_BOOLEAN:
+ arg->v_boolean = CAST_RAW (raw, gboolean);
+ break;
+
+ case GI_TYPE_TAG_INT8:
+ arg->v_int8 = CAST_RAW (raw, gint8);
+ break;
+
+ case GI_TYPE_TAG_UINT8:
+ arg->v_uint8 = CAST_RAW (raw, guint8);
+ break;
+
+ case GI_TYPE_TAG_INT16:
+ arg->v_int16 = CAST_RAW (raw, gint16);
+ break;
+
+ case GI_TYPE_TAG_UINT16:
+ arg->v_uint16 = CAST_RAW (raw, guint16);
+ break;
+
+ case GI_TYPE_TAG_INT32:
+ arg->v_int32 = CAST_RAW (raw, gint32);
+ break;
+
+ case GI_TYPE_TAG_UINT32:
+ arg->v_uint32 = CAST_RAW (raw, guint32);
+ break;
+
+ case GI_TYPE_TAG_INT64:
+ arg->v_int64 = CAST_RAW (raw, gint64);
+ break;
+
+ case GI_TYPE_TAG_UINT64:
+ arg->v_uint64 = CAST_RAW (raw, guint64);
+ break;
+
+ case GI_TYPE_TAG_FLOAT:
+ arg->v_float = CAST_RAW (raw, gfloat);
+ break;
+
+ case GI_TYPE_TAG_DOUBLE:
+ arg->v_double = CAST_RAW (raw, gdouble);
+ break;
+
+ case GI_TYPE_TAG_GTYPE:
+ arg->v_size = CAST_RAW (raw, GType);
+ break;
+
+ case GI_TYPE_TAG_ARRAY:
+ case GI_TYPE_TAG_INTERFACE:
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ case GI_TYPE_TAG_GHASH:
+ case GI_TYPE_TAG_ERROR:
+ arg->v_pointer = * (gpointer *) raw;
+ break;
+
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ arg->v_string = * (gchar **) raw;
+ break;
+
+ default:
+ ccroak ("Unhandled info tag %d in raw_to_arg", tag);
+ }
+}
+
+static void
+arg_to_raw (GIArgument *arg, gpointer raw, GITypeInfo *info)
+{
+ GITypeTag tag = g_type_info_get_tag (info);
+
+ switch (tag) {
+ case GI_TYPE_TAG_VOID:
+ /* do nothing */
+ break;
+
+ case GI_TYPE_TAG_BOOLEAN:
+ * (gboolean *) raw = arg->v_boolean;
+ break;
+
+ case GI_TYPE_TAG_INT8:
+ * (gint8 *) raw = arg->v_int8;
+ break;
+
+ case GI_TYPE_TAG_UINT8:
+ * (guint8 *) raw = arg->v_uint8;
+ break;
+
+ case GI_TYPE_TAG_INT16:
+ * (gint16 *) raw = arg->v_int16;
+ break;
+
+ case GI_TYPE_TAG_UINT16:
+ * (guint16 *) raw = arg->v_uint16;
+ break;
+
+ case GI_TYPE_TAG_INT32:
+ * (gint32 *) raw = arg->v_int32;
+ break;
+
+ case GI_TYPE_TAG_UINT32:
+ * (guint32 *) raw = arg->v_uint32;
+ break;
+
+ case GI_TYPE_TAG_INT64:
+ * (gint64 *) raw = arg->v_int64;
+ break;
+
+ case GI_TYPE_TAG_UINT64:
+ * (guint64 *) raw = arg->v_uint64;
+ break;
+
+ case GI_TYPE_TAG_FLOAT:
+ * (gfloat *) raw = arg->v_float;
+ break;
+
+ case GI_TYPE_TAG_DOUBLE:
+ * (gdouble *) raw = arg->v_double;
+ break;
+
+ case GI_TYPE_TAG_GTYPE:
+ * (GType *) raw = arg->v_size;
+ break;
+
+ case GI_TYPE_TAG_ARRAY:
+ case GI_TYPE_TAG_INTERFACE:
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ case GI_TYPE_TAG_GHASH:
+ case GI_TYPE_TAG_ERROR:
+ * (gpointer *) raw = arg->v_pointer;
+ break;
+
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ * (gchar **) raw = arg->v_string;
+ break;
+
+ default:
+ ccroak ("Unhandled info tag %d in arg_to_raw", tag);
+ }
+}
diff --git a/gperl-i11n-marshal-struct.c b/gperl-i11n-marshal-struct.c
new file mode 100644
index 0000000..04a9b61
--- /dev/null
+++ b/gperl-i11n-marshal-struct.c
@@ -0,0 +1,142 @@
+static SV *
+struct_to_sv (GIBaseInfo* info,
+ GIInfoType info_type,
+ gpointer pointer,
+ gboolean own)
+{
+ HV *hv;
+
+ dwarn ("%s: pointer %p\n", G_STRFUNC, pointer);
+
+ if (pointer == NULL) {
+ return &PL_sv_undef;
+ }
+
+ hv = newHV ();
+
+ switch (info_type) {
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ {
+ gint i, n_fields =
+ g_struct_info_get_n_fields ((GIStructInfo *) info);
+ for (i = 0; i < n_fields; i++) {
+ GIFieldInfo *field_info;
+ SV *sv;
+ field_info =
+ g_struct_info_get_field ((GIStructInfo *) info, i);
+ /* FIXME: Check GIFieldInfoFlags. */
+ /* FIXME: Is it right to use GI_TRANSFER_NOTHING
+ * here? */
+ sv = get_field (field_info, pointer,
+ GI_TRANSFER_NOTHING);
+ if (gperl_sv_is_defined (sv)) {
+ const gchar *name;
+ name = g_base_info_get_name (
+ (GIBaseInfo *) field_info);
+ gperl_hv_take_sv (hv, name, strlen (name), sv);
+ }
+ g_base_info_unref ((GIBaseInfo *) field_info);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_UNION:
+ ccroak ("%s: unions not handled yet", G_STRFUNC);
+
+ default:
+ ccroak ("%s: unhandled info type %d", G_STRFUNC, info_type);
+ }
+
+ if (own) {
+ /* FIXME: Is it correct to just call g_free here? What if the
+ * thing was allocated via GSlice? */
+ g_free (pointer);
+ }
+
+ return newRV_noinc ((SV *) hv);
+}
+
+static gpointer
+sv_to_struct (GITransfer transfer,
+ GIBaseInfo * info,
+ GIInfoType info_type,
+ SV * sv)
+{
+ HV *hv;
+ gsize size = 0;
+ GITransfer field_transfer;
+ gpointer pointer = NULL;
+
+ dwarn ("%s: sv %p\n", G_STRFUNC, sv);
+
+ if (!gperl_sv_is_hash_ref (sv))
+ ccroak ("need a hash ref to convert to struct of type %s",
+ g_base_info_get_name (info));
+ hv = (HV *) SvRV (sv);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ size = g_struct_info_get_size ((GIStructInfo *) info);
+ break;
+ case GI_INFO_TYPE_UNION:
+ size = g_union_info_get_size ((GIStructInfo *) info);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ dwarn (" size: %d\n", size);
+
+ field_transfer = GI_TRANSFER_NOTHING;
+ dwarn (" transfer: %d\n", transfer);
+ switch (transfer) {
+ case GI_TRANSFER_EVERYTHING:
+ field_transfer = GI_TRANSFER_EVERYTHING;
+ /* fall through */
+ case GI_TRANSFER_CONTAINER:
+ /* FIXME: What if there's a special allocator for the record?
+ * Like GSlice? */
+ pointer = g_malloc0 (size);
+ break;
+
+ default:
+ pointer = gperl_alloc_temp (size);
+ break;
+ }
+
+ switch (info_type) {
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ {
+ gint i, n_fields =
+ g_struct_info_get_n_fields ((GIStructInfo *) info);
+ for (i = 0; i < n_fields; i++) {
+ GIFieldInfo *field_info;
+ const gchar *field_name;
+ SV **svp;
+ field_info = g_struct_info_get_field (
+ (GIStructInfo *) info, i);
+ /* FIXME: Check GIFieldInfoFlags. */
+ field_name = g_base_info_get_name (
+ (GIBaseInfo *) field_info);
+ svp = hv_fetch (hv, field_name, strlen (field_name), 0);
+ if (svp && gperl_sv_is_defined (*svp)) {
+ set_field (field_info, pointer,
+ field_transfer, *svp);
+ }
+ g_base_info_unref ((GIBaseInfo *) field_info);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_UNION:
+ ccroak ("%s: unions not handled yet", G_STRFUNC);
+
+ default:
+ ccroak ("%s: unhandled info type %d", G_STRFUNC, info_type);
+ }
+
+ return pointer;
+}
diff --git a/gperl-i11n-method.c b/gperl-i11n-method.c
new file mode 100644
index 0000000..d8d0d77
--- /dev/null
+++ b/gperl-i11n-method.c
@@ -0,0 +1,120 @@
+static void
+store_methods (HV *namespaced_functions, GIBaseInfo *info, GIInfoType info_type)
+{
+ const gchar *namespace;
+ AV *av;
+ gint i;
+
+ namespace = g_base_info_get_name (info);
+ av = newAV ();
+
+ switch (info_type) {
+ case GI_INFO_TYPE_OBJECT:
+ {
+ gint n_methods = g_object_info_get_n_methods (
+ (GIObjectInfo *) info);
+ for (i = 0; i < n_methods; i++) {
+ GIFunctionInfo *function_info =
+ g_object_info_get_method (
+ (GIObjectInfo *) info, i);
+ const gchar *function_name =
+ g_base_info_get_name (
+ (GIBaseInfo *) function_info);
+ av_push (av, newSVpv (function_name, PL_na));
+ g_base_info_unref ((GIBaseInfo *) function_info);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_INTERFACE:
+ {
+ gint n_methods = g_interface_info_get_n_methods (
+ (GIInterfaceInfo *) info);
+ for (i = 0; i < n_methods; i++) {
+ GIFunctionInfo *function_info =
+ g_interface_info_get_method (
+ (GIInterfaceInfo *) info, i);
+ const gchar *function_name =
+ g_base_info_get_name (
+ (GIBaseInfo *) function_info);
+ av_push (av, newSVpv (function_name, PL_na));
+ g_base_info_unref ((GIBaseInfo *) function_info);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ {
+ gint n_methods = g_struct_info_get_n_methods (
+ (GIStructInfo *) info);
+ for (i = 0; i < n_methods; i++) {
+ GIFunctionInfo *function_info =
+ g_struct_info_get_method (
+ (GIStructInfo *) info, i);
+ const gchar *function_name =
+ g_base_info_get_name (
+ (GIBaseInfo *) function_info);
+ av_push (av, newSVpv (function_name, PL_na));
+ g_base_info_unref ((GIBaseInfo *) function_info);
+ }
+ break;
+ }
+
+ case GI_INFO_TYPE_UNION:
+ {
+ gint n_methods = g_union_info_get_n_methods ((GIUnionInfo *) info);
+ for (i = 0; i < n_methods; i++) {
+ GIFunctionInfo *function_info;
+ const gchar *function_name;
+
+ function_info = g_union_info_get_method ((GIUnionInfo *) info, i);
+ function_name = g_base_info_get_name ((GIBaseInfo *) function_info);
+
+ av_push (av, newSVpv (function_name, PL_na));
+ g_base_info_unref ((GIBaseInfo *) function_info);
+ }
+ break;
+ }
+
+ default:
+ ccroak ("store_methods: unsupported info type %d", info_type);
+ }
+
+ gperl_hv_take_sv (namespaced_functions, namespace, strlen (namespace),
+ newRV_noinc ((SV *) av));
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+store_vfuncs (HV *objects_with_vfuncs, GIObjectInfo *info)
+{
+ const gchar *object_name;
+ AV *vfuncs_av;
+ gint n_vfuncs, i;
+
+ n_vfuncs = g_object_info_get_n_vfuncs (info);
+ if (n_vfuncs <= 0)
+ return;
+
+ object_name = g_base_info_get_name (info);
+ vfuncs_av = newAV ();
+
+ for (i = 0; i < n_vfuncs; i++) {
+ GIVFuncInfo *vfunc_info =
+ g_object_info_get_vfunc (info, i);
+ const gchar *vfunc_name =
+ g_base_info_get_name (vfunc_info);
+ gchar *vfunc_perl_name = g_ascii_strup (vfunc_name, -1);
+ AV *vfunc_av = newAV ();
+ av_push (vfunc_av, newSVpv (vfunc_name, PL_na));
+ av_push (vfunc_av, newSVpv (vfunc_perl_name, PL_na));
+ av_push (vfuncs_av, newRV_noinc ((SV *) vfunc_av));
+ g_free (vfunc_perl_name);
+ g_base_info_unref (vfunc_info);
+ }
+
+ gperl_hv_take_sv (objects_with_vfuncs, object_name, strlen (object_name),
+ newRV_noinc ((SV *) vfuncs_av));
+}
diff --git a/gperl-i11n-size.c b/gperl-i11n-size.c
new file mode 100644
index 0000000..11068be
--- /dev/null
+++ b/gperl-i11n-size.c
@@ -0,0 +1,152 @@
+/* These three are basically copied from pygi's pygi-info.c. :-( */
+
+static gsize
+size_of_type_tag (GITypeTag type_tag)
+{
+ switch(type_tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ return sizeof (gboolean);
+ case GI_TYPE_TAG_INT8:
+ case GI_TYPE_TAG_UINT8:
+ return sizeof (gint8);
+ case GI_TYPE_TAG_INT16:
+ case GI_TYPE_TAG_UINT16:
+ return sizeof (gint16);
+ case GI_TYPE_TAG_INT32:
+ case GI_TYPE_TAG_UINT32:
+ return sizeof (gint32);
+ case GI_TYPE_TAG_INT64:
+ case GI_TYPE_TAG_UINT64:
+ return sizeof (gint64);
+ case GI_TYPE_TAG_FLOAT:
+ return sizeof (gfloat);
+ case GI_TYPE_TAG_DOUBLE:
+ return sizeof (gdouble);
+ case GI_TYPE_TAG_GTYPE:
+ return sizeof (GType);
+ case GI_TYPE_TAG_UNICHAR:
+ return sizeof (gunichar);
+
+ case GI_TYPE_TAG_VOID:
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ case GI_TYPE_TAG_ARRAY:
+ case GI_TYPE_TAG_INTERFACE:
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ case GI_TYPE_TAG_GHASH:
+ case GI_TYPE_TAG_ERROR:
+ ccroak ("Unable to determine the size of '%s'",
+ g_type_tag_to_string (type_tag));
+ break;
+ }
+
+ return 0;
+}
+
+static gsize
+size_of_interface (GITypeInfo *type_info)
+{
+ gsize size = 0;
+
+ GIBaseInfo *info;
+ GIInfoType info_type;
+
+ info = g_type_info_get_interface (type_info);
+ info_type = g_base_info_get_type (info);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_STRUCT:
+ if (g_type_info_is_pointer (type_info)) {
+ size = sizeof (gpointer);
+ } else {
+ /* FIXME: Remove this workaround once
+ * gobject-introspection is fixed:
+ * <https://bugzilla.gnome.org/show_bug.cgi?id=657040>. */
+ GType type = g_registered_type_info_get_g_type (info);
+ if (type == G_TYPE_VALUE) {
+ size = sizeof (GValue);
+ } else {
+ size = g_struct_info_get_size ((GIStructInfo *) info);
+ }
+ }
+ break;
+
+ case GI_INFO_TYPE_UNION:
+ if (g_type_info_is_pointer (type_info)) {
+ size = sizeof (gpointer);
+ } else {
+ size = g_union_info_get_size ((GIUnionInfo *) info);
+ }
+ break;
+
+ case GI_INFO_TYPE_ENUM:
+ case GI_INFO_TYPE_FLAGS:
+ if (g_type_info_is_pointer (type_info)) {
+ size = sizeof (gpointer);
+ } else {
+ GITypeTag type_tag;
+ type_tag = g_enum_info_get_storage_type ((GIEnumInfo *) info);
+ size = size_of_type_tag (type_tag);
+ }
+ break;
+
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_OBJECT:
+ case GI_INFO_TYPE_INTERFACE:
+ case GI_INFO_TYPE_CALLBACK:
+ size = sizeof (gpointer);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_base_info_unref (info);
+
+ return size;
+}
+
+static gsize
+size_of_type_info (GITypeInfo *type_info)
+{
+ GITypeTag type_tag;
+
+ type_tag = g_type_info_get_tag (type_info);
+ switch (type_tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ case GI_TYPE_TAG_INT8:
+ case GI_TYPE_TAG_UINT8:
+ case GI_TYPE_TAG_INT16:
+ case GI_TYPE_TAG_UINT16:
+ case GI_TYPE_TAG_INT32:
+ case GI_TYPE_TAG_UINT32:
+ case GI_TYPE_TAG_INT64:
+ case GI_TYPE_TAG_UINT64:
+ case GI_TYPE_TAG_FLOAT:
+ case GI_TYPE_TAG_DOUBLE:
+ case GI_TYPE_TAG_GTYPE:
+ case GI_TYPE_TAG_UNICHAR:
+ if (g_type_info_is_pointer (type_info)) {
+ return sizeof (gpointer);
+ } else {
+ return size_of_type_tag (type_tag);
+ }
+
+ case GI_TYPE_TAG_INTERFACE:
+ return size_of_interface (type_info);
+
+ case GI_TYPE_TAG_ARRAY:
+ case GI_TYPE_TAG_VOID:
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ case GI_TYPE_TAG_GHASH:
+ case GI_TYPE_TAG_ERROR:
+ return sizeof (gpointer);
+ }
+
+ return 0;
+}
diff --git a/gperl-i11n-vfunc-interface.c b/gperl-i11n-vfunc-interface.c
new file mode 100644
index 0000000..dfa456f
--- /dev/null
+++ b/gperl-i11n-vfunc-interface.c
@@ -0,0 +1,50 @@
+static void
+generic_interface_init (gpointer iface, gpointer data)
+{
+ GIInterfaceInfo *info = data;
+ GIStructInfo *struct_info;
+ gint n, i;
+ struct_info = g_interface_info_get_iface_struct (info);
+ n = g_interface_info_get_n_vfuncs (info);
+ for (i = 0; i < n; i++) {
+ GIVFuncInfo *vfunc_info;
+ const gchar *vfunc_name;
+ GIFieldInfo *field_info;
+ gint field_offset;
+ GITypeInfo *field_type_info;
+ gchar *perl_method_name;
+ GPerlI11nCallbackInfo *callback_info;
+
+ vfunc_info = g_interface_info_get_vfunc (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);
+ field_type_info = g_field_info_get_type (field_info);
+
+ perl_method_name = g_ascii_strup (vfunc_name, -1);
+ callback_info = create_callback_closure_for_named_sub (
+ field_type_info, perl_method_name, NULL);
+ dwarn ("installing vfunc %s as %s at offset %d (vs. %d) inside %p\n",
+ vfunc_name, perl_method_name,
+ field_offset, g_vfunc_info_get_offset (vfunc_info),
+ iface);
+ G_STRUCT_MEMBER (gpointer, iface, field_offset) = callback_info->closure;
+
+ g_base_info_unref (field_type_info);
+ g_base_info_unref (field_info);
+ g_base_info_unref (vfunc_info);
+ }
+ g_base_info_unref (struct_info);
+}
+
+static void
+generic_interface_finalize (gpointer iface, gpointer data)
+{
+ GIInterfaceInfo *info = data;
+ PERL_UNUSED_VAR (iface);
+ dwarn ("releasing interface info\n");
+ g_base_info_unref ((GIBaseInfo *) info);
+}
diff --git a/gperl-i11n-vfunc-object.c b/gperl-i11n-vfunc-object.c
new file mode 100644
index 0000000..ab1adee
--- /dev/null
+++ b/gperl-i11n-vfunc-object.c
@@ -0,0 +1,40 @@
+static void
+generic_class_init (GIObjectInfo *info, const gchar *target_package, gpointer class)
+{
+ GIStructInfo *struct_info;
+ gint n, i;
+ struct_info = g_object_info_get_class_struct (info);
+ n = g_object_info_get_n_vfuncs (info);
+ for (i = 0; i < n; i++) {
+ GIVFuncInfo *vfunc_info;
+ const gchar *vfunc_name;
+ GIFieldInfo *field_info;
+ gint field_offset;
+ GITypeInfo *field_type_info;
+ gchar *perl_method_name;
+ GPerlI11nCallbackInfo *callback_info;
+
+ vfunc_info = g_object_info_get_vfunc (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);
+ field_type_info = g_field_info_get_type (field_info);
+
+ perl_method_name = g_ascii_strup (vfunc_name, -1);
+ callback_info = create_callback_closure_for_named_sub (
+ field_type_info, perl_method_name, g_strdup (target_package));
+ dwarn ("installing vfunc %s as %s at offset %d (vs. %d) inside %p\n",
+ vfunc_name, perl_method_name,
+ field_offset, g_vfunc_info_get_offset (vfunc_info),
+ class);
+ G_STRUCT_MEMBER (gpointer, class, field_offset) = callback_info->closure;
+
+ g_base_info_unref (field_type_info);
+ g_base_info_unref (field_info);
+ g_base_info_unref (vfunc_info);
+ }
+ g_base_info_unref (struct_info);
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]