static
void
_prepare_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo,
GICallableInfo *info,
gpointer *args);
static
void
_clear_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo);
static
void
_fill_ffi_return_value (GITypeInfo *return_info,
gpointer resp,
GIArgument *arg);
static
void
invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata)
{
GPerlI11nPerlCallbackInfo *info;
GICallableInfo *cb_interface;
GPerlI11nPerlInvocationInfo iinfo;
guint args_offset = 0, i;
guint in_inout;
guint n_return_values;
I32 n_returned;
I32 context;
SV *first_sv = NULL, *last_sv = NULL;
dGPERL_CALLBACK_MARSHAL_SP;
PERL_UNUSED_VAR (cif);
info = (GPerlI11nPerlCallbackInfo *) userdata;
cb_interface = (GICallableInfo *) info->interface;
GPERL_CALLBACK_MARSHAL_INIT (info);
ENTER;
SAVETMPS;
_prepare_perl_invocation_info (&iinfo, cb_interface, args);
PUSHMARK (SP);
if
(info->args_converter) {
PUSHMARK (SP);
}
if
(iinfo.base.is_signal) {
SV *instance_sv, *data_sv;
args_offset = 1;
instance_sv = SAVED_STACK_SV (instance_pointer_to_sv (
cb_interface,
CAST_RAW (args[0], gpointer)));
data_sv = info->data ? SvREFCNT_inc (info->data) : NULL;
first_sv = info->swap_data ? data_sv : instance_sv;
last_sv = info->swap_data ? instance_sv : data_sv;
dwarn (
"info->data = %p, info->swap_data = %d\n"
,
info->data, info->swap_data);
dwarn (
"instance = %p, data = %p, first = %p, last = %p\n"
,
instance_sv, data_sv, first_sv, last_sv);
if
(first_sv)
XPUSHs (sv_2mortal (first_sv));
}
in_inout = 0;
for
(i = 0; i < iinfo.base.n_args; i++) {
GIArgInfo *arg_info = &(iinfo.base.arg_infos[i]);
GITypeInfo *arg_type = &(iinfo.base.arg_types[i]);
GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
GIDirection direction = g_arg_info_get_direction (arg_info);
iinfo.base.current_pos = i;
dwarn (
"arg %d: info = %p (%s)\n"
,
i, arg_info, g_base_info_get_name (arg_info));
dwarn (
" dir = %d, is retval = %d, is optional = %d, may be null = %d, transfer = %d\n"
,
direction,
g_arg_info_is_return_value (arg_info),
g_arg_info_is_optional (arg_info),
g_arg_info_may_be_null (arg_info),
transfer);
dwarn (
" arg type = %p, is pointer = %d, tag = %d (%s)\n"
,
arg_type,
g_type_info_is_pointer (arg_type),
g_type_info_get_tag (arg_type),
g_type_tag_to_string (g_type_info_get_tag (arg_type)));
if
(direction == GI_DIRECTION_IN ||
direction == GI_DIRECTION_INOUT)
{
gpointer raw;
GIArgument arg;
SV *sv;
raw = direction == GI_DIRECTION_INOUT
? *((gpointer *) args[i+args_offset])
: args[i+args_offset];
raw_to_arg (raw, &arg, arg_type);
sv = SAVED_STACK_SV (arg_to_sv (&arg,
arg_type,
transfer,
GPERL_I11N_MEMORY_SCOPE_IRRELEVANT,
&iinfo.base));
if
(sv)
XPUSHs (sv_2mortal (sv));
}
if
(direction == GI_DIRECTION_INOUT ||
direction == GI_DIRECTION_OUT)
{
in_inout++;
}
}
if
(last_sv)
XPUSHs (sv_2mortal (last_sv));
PUTBACK;
if
(info->args_converter) {
call_sv (info->args_converter, G_ARRAY);
SPAGAIN;
}
context = G_VOID | G_DISCARD;
if
(iinfo.base.has_return_value) {
context = in_inout > 0
? G_ARRAY
: G_SCALAR;
}
else
{
if
(in_inout == 1) {
context = G_SCALAR;
}
else
if
(in_inout > 1) {
context = G_ARRAY;
}
}
n_return_values = iinfo.base.has_return_value
? 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 < 0 || ((guint) n_returned) != n_return_values)) {
ccroak (
"callback returned %d values "
"but is supposed to return %u values"
,
n_returned, n_return_values);
}
SPAGAIN;
if
(in_inout > 0) {
SV **returned_values;
int
out_index;
returned_values = g_new0 (SV *, in_inout);
for
(i = 0; i < in_inout; i++) {
returned_values[in_inout - i - 1] = POPs;
}
out_index = 0;
for
(i = 0; i < iinfo.base.n_args; i++) {
GIArgInfo *arg_info = &(iinfo.base.arg_infos[i]);
GITypeInfo *arg_type = &(iinfo.base.arg_types[i]);
GIDirection direction = g_arg_info_get_direction (arg_info);
gpointer out_pointer = * (gpointer *) args[i+args_offset];
if
(!out_pointer) {
dwarn (
"skipping out arg %d\n"
, i);
continue
;
}
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 = TRUE;
gboolean is_caller_allocated = g_arg_info_is_caller_allocates (arg_info);
dwarn (
"out/inout arg, pos = %d, is_caller_allocated = %d\n"
,
i, is_caller_allocated);
if
(is_caller_allocated) {
tmp_arg.v_pointer = out_pointer;
}
sv_to_arg (returned_values[out_index], &tmp_arg,
arg_info, arg_type,
transfer, may_be_null, &iinfo.base);
if
(!is_caller_allocated) {
arg_to_raw (&tmp_arg, out_pointer, arg_type);
}
out_index++;
}
}
g_free (returned_values);
}
if
(iinfo.base.has_return_value) {
GIArgument arg;
GITypeInfo *type_info;
GITransfer transfer;
gboolean may_be_null;
type_info = &iinfo.base.return_type_info;
transfer = iinfo.base.return_type_transfer;
may_be_null = g_callable_info_may_return_null (cb_interface);
dwarn (
"return value: type = %p\n"
, type_info);
dwarn (
" is pointer = %d, tag = %d (%s), transfer = %d\n"
,
g_type_info_is_pointer (type_info),
g_type_info_get_tag (type_info),
g_type_tag_to_string (g_type_info_get_tag (type_info)),
transfer);
sv_to_arg (POPs, &arg, NULL, type_info,
transfer, may_be_null, &iinfo.base);
_fill_ffi_return_value (type_info, resp, &arg);
}
PUTBACK;
_clear_perl_invocation_info (&iinfo);
FREETMPS;
LEAVE;
}
#if GI_CHECK_VERSION (1, 33, 10)
static
void
invoke_perl_signal_handler (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata)
{
GClosure *closure = CAST_RAW (args[0], GClosure*);
GValue *return_value = CAST_RAW (args[1], GValue*);
guint n_param_values = CAST_RAW (args[2], guint);
const
GValue *param_values = CAST_RAW (args[3],
const
GValue*);
gpointer invocation_hint = CAST_RAW (args[4], gpointer);
gpointer marshal_data = CAST_RAW (args[5], gpointer);
GPerlI11nPerlSignalInfo *signal_info = userdata;
GPerlClosure *perl_closure = (GPerlClosure *) closure;
GPerlI11nPerlCallbackInfo *cb_info;
GCClosure c_closure;
PERL_UNUSED_VAR (cif);
PERL_UNUSED_VAR (resp);
PERL_UNUSED_VAR (marshal_data);
dwarn (
"%s, n_args = %d\n"
,
g_base_info_get_name (signal_info->interface),
g_callable_info_get_n_args (signal_info->interface));
cb_info = create_perl_callback_closure (signal_info->interface,
perl_closure->callback);
attach_perl_callback_data (cb_info, perl_closure->data);
cb_info->swap_data = GPERL_CLOSURE_SWAP_DATA (perl_closure);
if
(signal_info->args_converter)
cb_info->args_converter = SvREFCNT_inc (signal_info->args_converter);
c_closure.closure = *closure;
#if GI_CHECK_VERSION (1, 72, 0)
c_closure.callback = g_callable_info_get_closure_native_address (signal_info->interface, cb_info->closure);
#else
c_closure.callback = cb_info->closure;
#endif
gi_cclosure_marshal_generic ((GClosure *) &c_closure,
return_value,
n_param_values, param_values,
invocation_hint,
NULL
);
release_perl_callback (cb_info);
}
#endif
static
void
_prepare_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo,
GICallableInfo *info,
gpointer *args)
{
guint i;
prepare_invocation_info ((GPerlI11nInvocationInfo *) iinfo, info);
dwarn (
"%s, n_args = %d\n"
,
g_base_info_get_name (info),
g_callable_info_get_n_args (info));
for
(i = 0 ; i < iinfo->base.n_args ; i++) {
GITypeInfo *arg_type = &(iinfo->base.arg_types[i]);
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) {
GITypeInfo *length_arg_type;
guint args_pos = iinfo->base.is_signal ? pos+1 : pos;
length_arg_type = &(iinfo->base.arg_types[pos]);
raw_to_arg (args[args_pos], &iinfo->base.aux_args[pos], length_arg_type);
dwarn (
" pos %d is array length => %"
G_GSIZE_FORMAT
"\n"
,
pos, iinfo->base.aux_args[pos].v_size);
}
}
}
}
static
void
_clear_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo)
{
clear_invocation_info ((GPerlI11nInvocationInfo *) iinfo);
}
static
void
_fill_ffi_return_value (GITypeInfo *return_info,
gpointer resp,
GIArgument *arg)
{
if
(!resp)
return
;
switch
(g_type_info_get_tag (return_info)) {
case
GI_TYPE_TAG_BOOLEAN:
*((ffi_sarg *) resp) = arg->v_boolean;
break
;
case
GI_TYPE_TAG_INT8:
*((ffi_sarg *) resp) = arg->v_int8;
break
;
case
GI_TYPE_TAG_UINT8:
*((ffi_arg *) resp) = arg->v_uint8;
break
;
case
GI_TYPE_TAG_INT16:
*((ffi_sarg *) resp) = arg->v_int16;
break
;
case
GI_TYPE_TAG_UINT16:
*((ffi_arg *) resp) = arg->v_uint16;
break
;
case
GI_TYPE_TAG_INT32:
*((ffi_sarg *) resp) = arg->v_int32;
break
;
case
GI_TYPE_TAG_UINT32:
*((ffi_arg *) resp) = arg->v_uint32;
break
;
case
GI_TYPE_TAG_INT64:
*((ffi_sarg *) resp) = arg->v_int64;
break
;
case
GI_TYPE_TAG_UINT64:
*((ffi_arg *) resp) = arg->v_uint64;
break
;
case
GI_TYPE_TAG_FLOAT:
*((gfloat *) resp) = arg->v_float;
break
;
case
GI_TYPE_TAG_DOUBLE:
*((gdouble *) resp) = arg->v_double;
break
;
case
GI_TYPE_TAG_GTYPE:
*((ffi_arg *) resp) = arg->v_size;
break
;
case
GI_TYPE_TAG_UNICHAR:
*((ffi_arg *) resp) = arg->v_uint32;
break
;
case
GI_TYPE_TAG_INTERFACE:
{
GIBaseInfo *interface_info;
interface_info = g_type_info_get_interface (return_info);
switch
(g_base_info_get_type (interface_info)) {
case
GI_INFO_TYPE_ENUM:
*(ffi_sarg *) resp = arg->v_int;
break
;
case
GI_INFO_TYPE_FLAGS:
*(ffi_arg *) resp = arg->v_uint;
break
;
default
:
*(ffi_arg *) resp = (ffi_arg) arg->v_pointer;
break
;
}
g_base_info_unref (interface_info);
break
;
}
default
:
*(ffi_arg *) resp = (ffi_arg) arg->v_pointer;
break
;
}
}