glib/girepository/gicallableinfo.c
Philip Withnall bf4cf2d464 gibaseinfo: Fix use of stack-allocated GIBaseInfos
Most of the code for handling stack-allocated infos was correct, it was
just missing code to initialise the `GTypeInstance` member.

Since `GTypeInstance` isn’t really designed for stack allocation, this
is a little hacky — it requires setting up the member within
`GTypeInstance` manually. It works, though.

The externally visible consequence of this, though, is that
stack-allocated `GIBaseInfo`s now need to be cleared when they’re
finished being used. This allows the `GTypeClass` ref to be dropped.

All users of the stack-allocated APIs in libgirepository will need to
adapt to this change.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Fixes: #3218
2024-01-23 18:07:17 +00:00

843 lines
25 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
* GObject introspection: Callable implementation
*
* Copyright (C) 2005 Matthias Clasen
* Copyright (C) 2008,2009 Red Hat, Inc.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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 Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <glib.h>
#include <girepository/girepository.h>
#include "gibaseinfo-private.h"
#include "girepository-private.h"
#include "gitypelib-internal.h"
#include "girffi.h"
#include "gicallableinfo.h"
/* GICallableInfo functions */
/**
* GICallableInfo:
*
* `GICallableInfo` represents an entity which is callable.
*
* Examples of callable are:
*
* - functions ([class@GIRepository.FunctionInfo])
* - virtual functions ([class@GIRepository.VFuncInfo])
* - callbacks ([class@GIRepository.CallbackInfo]).
*
* A callable has a list of arguments ([class@GIRepository.ArgInfo]), a return
* type, direction and a flag which decides if it returns `NULL`.
*
* Since: 2.80
*/
static uint32_t
signature_offset (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo*)info;
int sigoff = -1;
switch (gi_base_info_get_info_type ((GIBaseInfo *) info))
{
case GI_INFO_TYPE_FUNCTION:
sigoff = G_STRUCT_OFFSET (FunctionBlob, signature);
break;
case GI_INFO_TYPE_VFUNC:
sigoff = G_STRUCT_OFFSET (VFuncBlob, signature);
break;
case GI_INFO_TYPE_CALLBACK:
sigoff = G_STRUCT_OFFSET (CallbackBlob, signature);
break;
case GI_INFO_TYPE_SIGNAL:
sigoff = G_STRUCT_OFFSET (SignalBlob, signature);
break;
default:
g_assert_not_reached ();
}
if (sigoff >= 0)
return *(uint32_t *)&rinfo->typelib->data[rinfo->offset + sigoff];
return 0;
}
/**
* gi_callable_info_can_throw_gerror:
* @info: a #GICallableInfo
*
* Whether the callable can throw a [type@GLib.Error]
*
* Returns: `TRUE` if this `GICallableInfo` can throw a [type@GLib.Error]
* Since: 2.80
*/
gboolean
gi_callable_info_can_throw_gerror (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo*)info;
SignatureBlob *signature;
signature = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)];
if (signature->throws)
return TRUE;
/* Functions and VFuncs store "throws" in their own blobs.
* This info was additionally added to the SignatureBlob
* to support the other callables. For Functions and VFuncs,
* also check their legacy flag for compatibility.
*/
switch (gi_base_info_get_info_type ((GIBaseInfo *) info)) {
case GI_INFO_TYPE_FUNCTION:
{
FunctionBlob *blob;
blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset];
return blob->throws;
}
case GI_INFO_TYPE_VFUNC:
{
VFuncBlob *blob;
blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset];
return blob->throws;
}
case GI_INFO_TYPE_CALLBACK:
case GI_INFO_TYPE_SIGNAL:
return FALSE;
default:
g_assert_not_reached ();
}
}
/**
* gi_callable_info_is_method:
* @info: a #GICallableInfo
*
* Determines if the callable info is a method.
*
* For [class@GIRepository.VFuncInfo]s, [class@GIRepository.CallbackInfo]s, and
* [class@GIRepository.SignalInfo]s, this is always true. Otherwise, this looks
* at the `GI_FUNCTION_IS_METHOD` flag on the [class@GIRepository.FunctionInfo].
*
* Concretely, this function returns whether
* [method@GIRepository.CallableInfo.get_n_args] matches the number of arguments
* in the raw C method. For methods, there is one more C argument than is
* exposed by introspection: the `self` or `this` object.
*
* Returns: `TRUE` if @info is a method, `FALSE` otherwise
* Since: 2.80
*/
gboolean
gi_callable_info_is_method (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo*)info;
switch (gi_base_info_get_info_type ((GIBaseInfo *) info)) {
case GI_INFO_TYPE_FUNCTION:
{
FunctionBlob *blob;
blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset];
return (!blob->constructor && !blob->is_static);
}
case GI_INFO_TYPE_VFUNC:
case GI_INFO_TYPE_SIGNAL:
return TRUE;
case GI_INFO_TYPE_CALLBACK:
return FALSE;
default:
g_assert_not_reached ();
}
}
/**
* gi_callable_info_get_return_type:
* @info: a #GICallableInfo
*
* Obtain the return type of a callable item as a [class@GIRepository.TypeInfo].
*
* Returns: (transfer full): the [class@GIRepository.TypeInfo]. Free the struct
* by calling [method@GIRepository.BaseInfo.unref] when done.
* Since: 2.80
*/
GITypeInfo *
gi_callable_info_get_return_type (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
uint32_t offset;
g_return_val_if_fail (info != NULL, NULL);
g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), NULL);
offset = signature_offset (info);
return gi_type_info_new ((GIBaseInfo*)info, rinfo->typelib, offset);
}
/**
* gi_callable_info_load_return_type:
* @info: a #GICallableInfo
* @type: (out caller-allocates): Initialized with return type of @info
*
* Obtain information about a return value of callable; this
* function is a variant of [method@GIRepository.CallableInfo.get_return_type]
* designed for stack allocation.
*
* The initialized @type must not be referenced after @info is deallocated.
*
* Once you are done with @type, it must be cleared using
* [method@GIRepository.BaseInfo.clear].
*
* Since: 2.80
*/
void
gi_callable_info_load_return_type (GICallableInfo *info,
GITypeInfo *type)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
uint32_t offset;
g_return_if_fail (info != NULL);
g_return_if_fail (GI_IS_CALLABLE_INFO (info));
offset = signature_offset (info);
gi_type_info_init (type, (GIBaseInfo*)info, rinfo->typelib, offset);
}
/**
* gi_callable_info_may_return_null:
* @info: a #GICallableInfo
*
* See if a callable could return `NULL`.
*
* Returns: `TRUE` if callable could return `NULL`
* Since: 2.80
*/
gboolean
gi_callable_info_may_return_null (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
SignatureBlob *blob;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), FALSE);
blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)];
return blob->may_return_null;
}
/**
* gi_callable_info_skip_return:
* @info: a #GICallableInfo
*
* See if a callables return value is only useful in C.
*
* Returns: `TRUE` if return value is only useful in C.
* Since: 2.80
*/
gboolean
gi_callable_info_skip_return (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
SignatureBlob *blob;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), FALSE);
blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)];
return blob->skip_return;
}
/**
* gi_callable_info_get_caller_owns:
* @info: a #GICallableInfo
*
* See whether the caller owns the return value of this callable.
*
* [type@GIRepository.Transfer] contains a list of possible transfer values.
*
* Returns: the transfer mode for the return value of the callable
* Since: 2.80
*/
GITransfer
gi_callable_info_get_caller_owns (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo*) info;
SignatureBlob *blob;
g_return_val_if_fail (info != NULL, -1);
g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1);
blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)];
if (blob->caller_owns_return_value)
return GI_TRANSFER_EVERYTHING;
else if (blob->caller_owns_return_container)
return GI_TRANSFER_CONTAINER;
else
return GI_TRANSFER_NOTHING;
}
/**
* gi_callable_info_get_instance_ownership_transfer:
* @info: a #GICallableInfo
*
* Obtains the ownership transfer for the instance argument.
*
* [type@GIRepository.Transfer] contains a list of possible transfer values.
*
* Returns: the transfer mode of the instance argument
* Since: 2.80
*/
GITransfer
gi_callable_info_get_instance_ownership_transfer (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo*) info;
SignatureBlob *blob;
g_return_val_if_fail (info != NULL, -1);
g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1);
blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)];
if (blob->instance_transfer_ownership)
return GI_TRANSFER_EVERYTHING;
else
return GI_TRANSFER_NOTHING;
}
/**
* gi_callable_info_get_n_args:
* @info: a #GICallableInfo
*
* Obtain the number of arguments (both in and out) for this callable.
*
* Returns: The number of arguments this callable expects.
* Since: 2.80
*/
unsigned int
gi_callable_info_get_n_args (GICallableInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
uint32_t offset;
SignatureBlob *blob;
g_return_val_if_fail (info != NULL, -1);
g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1);
offset = signature_offset (info);
blob = (SignatureBlob *)&rinfo->typelib->data[offset];
return blob->n_arguments;
}
/**
* gi_callable_info_get_arg:
* @info: a #GICallableInfo
* @n: the argument index to fetch
*
* Obtain information about a particular argument of this callable.
*
* Returns: (transfer full): the [class@GIRepository.ArgInfo]. Free it with
* [method@GIRepository.BaseInfo.unref] when done.
* Since: 2.80
*/
GIArgInfo *
gi_callable_info_get_arg (GICallableInfo *info,
unsigned int n)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
Header *header;
uint32_t offset;
g_return_val_if_fail (info != NULL, NULL);
g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), NULL);
g_return_val_if_fail (n <= G_MAXUINT16, NULL);
offset = signature_offset (info);
header = (Header *)rinfo->typelib->data;
return (GIArgInfo *) gi_info_new (GI_INFO_TYPE_ARG, (GIBaseInfo*)info, rinfo->typelib,
offset + header->signature_blob_size + n * header->arg_blob_size);
}
/**
* gi_callable_info_load_arg:
* @info: a #GICallableInfo
* @n: the argument index to fetch
* @arg: (out caller-allocates): Initialize with argument number @n
*
* Obtain information about a particular argument of this callable; this
* function is a variant of [method@GIRepository.CallableInfo.get_arg] designed
* for stack allocation.
*
* The initialized @arg must not be referenced after @info is deallocated.
*
* Once you are done with @arg, it must be cleared using
* [method@GIRepository.BaseInfo.clear].
*
* Since: 2.80
*/
void
gi_callable_info_load_arg (GICallableInfo *info,
unsigned int n,
GIArgInfo *arg)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
Header *header;
uint32_t offset;
g_return_if_fail (info != NULL);
g_return_if_fail (GI_IS_CALLABLE_INFO (info));
g_return_if_fail (n <= G_MAXUINT16);
offset = signature_offset (info);
header = (Header *)rinfo->typelib->data;
gi_info_init ((GIRealInfo*)arg, GI_TYPE_ARG_INFO, rinfo->repository, (GIBaseInfo*)info, rinfo->typelib,
offset + header->signature_blob_size + n * header->arg_blob_size);
}
/**
* gi_callable_info_get_return_attribute:
* @info: a #GICallableInfo
* @name: a freeform string naming an attribute
*
* Retrieve an arbitrary attribute associated with the return value.
*
* Returns: (nullable): The value of the attribute, or `NULL` if no such
* attribute exists
* Since: 2.80
*/
const char *
gi_callable_info_get_return_attribute (GICallableInfo *info,
const char *name)
{
GIAttributeIter iter = GI_ATTRIBUTE_ITER_INIT;
const char *curname, *curvalue;
while (gi_callable_info_iterate_return_attributes (info, &iter, &curname, &curvalue))
{
if (g_strcmp0 (name, curname) == 0)
return (const char*) curvalue;
}
return NULL;
}
/**
* gi_callable_info_iterate_return_attributes:
* @info: a #GICallableInfo
* @iterator: (inout): a [type@GIRepository.AttributeIter] structure, must be
* initialized; see below
* @name: (out) (transfer none): Returned name, must not be freed
* @value: (out) (transfer none): Returned name, must not be freed
*
* Iterate over all attributes associated with the return value.
*
* The iterator structure is typically stack allocated, and must have its
* first member initialized to `NULL`.
*
* Both the @name and @value should be treated as constants
* and must not be freed.
*
* See [method@GIRepository.BaseInfo.iterate_attributes] for an example of how
* to use a similar API.
*
* Returns: `TRUE` if there are more attributes
* Since: 2.80
*/
gboolean
gi_callable_info_iterate_return_attributes (GICallableInfo *info,
GIAttributeIter *iterator,
const char **name,
const char **value)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
Header *header = (Header *)rinfo->typelib->data;
AttributeBlob *next, *after;
uint32_t blob_offset;
after = (AttributeBlob *) &rinfo->typelib->data[header->attributes +
header->n_attributes * header->attribute_blob_size];
blob_offset = signature_offset (info);
if (iterator->data != NULL)
next = (AttributeBlob *) iterator->data;
else
next = _attribute_blob_find_first ((GIBaseInfo *) info, blob_offset);
if (next == NULL || next->offset != blob_offset || next >= after)
return FALSE;
*name = gi_typelib_get_string (rinfo->typelib, next->name);
*value = gi_typelib_get_string (rinfo->typelib, next->value);
iterator->data = next + 1;
return TRUE;
}
/**
* gi_type_tag_extract_ffi_return_value:
* @return_tag: [type@GIRepository.TypeTag] of the return value
* @interface_type: [type@GIRepository.InfoType] of the underlying interface type
* @ffi_value: pointer to [type@GIRepository.FFIReturnValue] union containing
* the return value from `ffi_call()`
* @arg: (out caller-allocates): pointer to an allocated
* [class@GIRepository.Argument]
*
* Extract the correct bits from an `ffi_arg` return value into
* [class@GIRepository.Argument].
*
* See: https://bugzilla.gnome.org/show_bug.cgi?id=665152
*
* Also see [`ffi_call()`](man:ffi_call(3)): the storage requirements for return
* values are special.
*
* The @interface_type argument only applies if @return_tag is
* `GI_TYPE_TAG_INTERFACE`. Otherwise it is ignored.
*
* Since: 2.80
*/
void
gi_type_tag_extract_ffi_return_value (GITypeTag return_tag,
GIInfoType interface_type,
GIFFIReturnValue *ffi_value,
GIArgument *arg)
{
switch (return_tag) {
case GI_TYPE_TAG_INT8:
arg->v_int8 = (int8_t) ffi_value->v_long;
break;
case GI_TYPE_TAG_UINT8:
arg->v_uint8 = (uint8_t) ffi_value->v_ulong;
break;
case GI_TYPE_TAG_INT16:
arg->v_int16 = (int16_t) ffi_value->v_long;
break;
case GI_TYPE_TAG_UINT16:
arg->v_uint16 = (uint16_t) ffi_value->v_ulong;
break;
case GI_TYPE_TAG_INT32:
arg->v_int32 = (int32_t) ffi_value->v_long;
break;
case GI_TYPE_TAG_UINT32:
case GI_TYPE_TAG_BOOLEAN:
case GI_TYPE_TAG_UNICHAR:
arg->v_uint32 = (uint32_t) ffi_value->v_ulong;
break;
case GI_TYPE_TAG_INT64:
arg->v_int64 = (int64_t) ffi_value->v_int64;
break;
case GI_TYPE_TAG_UINT64:
arg->v_uint64 = (uint64_t) ffi_value->v_uint64;
break;
case GI_TYPE_TAG_FLOAT:
arg->v_float = ffi_value->v_float;
break;
case GI_TYPE_TAG_DOUBLE:
arg->v_double = ffi_value->v_double;
break;
case GI_TYPE_TAG_INTERFACE:
switch(interface_type) {
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
arg->v_int32 = (int32_t) ffi_value->v_long;
break;
default:
arg->v_pointer = (void *) ffi_value->v_pointer;
break;
}
break;
default:
arg->v_pointer = (void *) ffi_value->v_pointer;
break;
}
}
/**
* gi_type_info_extract_ffi_return_value:
* @return_info: [type@GIRepository.TypeInfo] describing the return type
* @ffi_value: pointer to [type@GIRepository.FFIReturnValue] union containing
* the return value from `ffi_call()`
* @arg: (out caller-allocates): pointer to an allocated
* [class@GIRepository.Argument]
*
* Extract the correct bits from an `ffi_arg` return value into
* [class@GIRepository.Argument].
*
* See: https://bugzilla.gnome.org/show_bug.cgi?id=665152
*
* Also see [`ffi_call()`](man:ffi_call(3)): the storage requirements for return
* values are special.
*
* Since: 2.80
*/
void
gi_type_info_extract_ffi_return_value (GITypeInfo *return_info,
GIFFIReturnValue *ffi_value,
GIArgument *arg)
{
GITypeTag return_tag = gi_type_info_get_tag (return_info);
GIInfoType interface_type = GI_INFO_TYPE_INVALID;
if (return_tag == GI_TYPE_TAG_INTERFACE)
{
GIBaseInfo *interface_info = gi_type_info_get_interface (return_info);
interface_type = gi_base_info_get_info_type (interface_info);
gi_base_info_unref (interface_info);
}
gi_type_tag_extract_ffi_return_value (return_tag, interface_type,
ffi_value, arg);
}
/**
* gi_callable_info_invoke:
* @info: a #GICallableInfo
* @function: function pointer to call
* @in_args: (array length=n_in_args): array of in arguments
* @n_in_args: number of arguments in @in_args
* @out_args: (array length=n_out_args): array of out arguments allocated by
* the caller, to be populated with outputted values
* @n_out_args: number of arguments in @out_args
* @return_value: (out caller-allocates) (not optional) (nullable): return
* location for the return value from the callable; `NULL` may be returned if
* the callable returns that
* @error: return location for a [type@GLib.Error], or `NULL`
*
* Invoke the given `GICallableInfo` by calling the given @function pointer.
*
* The set of arguments passed to @function will be constructed according to the
* introspected type of the `GICallableInfo`, using @in_args, @out_args
* and @error.
*
* Returns: `TRUE` if the callable was executed successfully and didnt throw
* a [type@GLib.Error]; `FALSE` if @error is set
* Since: 2.80
*/
gboolean
gi_callable_info_invoke (GICallableInfo *info,
void *function,
const GIArgument *in_args,
size_t n_in_args,
GIArgument *out_args,
size_t n_out_args,
GIArgument *return_value,
GError **error)
{
ffi_cif cif;
ffi_type *rtype;
ffi_type **atypes;
GITypeInfo *tinfo;
GITypeInfo *rinfo;
GITypeTag rtag;
GIArgInfo *ainfo;
size_t n_args, n_invoke_args, in_pos, out_pos, i;
void **args;
gboolean success = FALSE;
GError *local_error = NULL;
void *error_address = &local_error;
GIFFIReturnValue ffi_return_value;
void *return_value_p; /* Will point inside the union return_value */
gboolean is_method, throws;
rinfo = gi_callable_info_get_return_type ((GICallableInfo *)info);
rtype = gi_type_info_get_ffi_type (rinfo);
rtag = gi_type_info_get_tag(rinfo);
is_method = gi_callable_info_is_method (info);
throws = gi_callable_info_can_throw_gerror (info);
in_pos = 0;
out_pos = 0;
n_args = gi_callable_info_get_n_args ((GICallableInfo *)info);
if (is_method)
{
if (n_in_args == 0)
{
g_set_error (error,
GI_INVOKE_ERROR,
GI_INVOKE_ERROR_ARGUMENT_MISMATCH,
"Too few \"in\" arguments (handling this)");
goto out;
}
n_invoke_args = n_args+1;
in_pos++;
}
else
n_invoke_args = n_args;
if (throws)
/* Add an argument for the GError */
n_invoke_args ++;
atypes = g_alloca (sizeof (ffi_type*) * n_invoke_args);
args = g_alloca (sizeof (void *) * n_invoke_args);
if (is_method)
{
atypes[0] = &ffi_type_pointer;
args[0] = (void *) &in_args[0];
}
for (i = 0; i < n_args; i++)
{
int offset = (is_method ? 1 : 0);
ainfo = gi_callable_info_get_arg ((GICallableInfo *)info, i);
switch (gi_arg_info_get_direction (ainfo))
{
case GI_DIRECTION_IN:
tinfo = gi_arg_info_get_type_info (ainfo);
atypes[i+offset] = gi_type_info_get_ffi_type (tinfo);
gi_base_info_unref ((GIBaseInfo *)ainfo);
gi_base_info_unref ((GIBaseInfo *)tinfo);
if (in_pos >= n_in_args)
{
g_set_error (error,
GI_INVOKE_ERROR,
GI_INVOKE_ERROR_ARGUMENT_MISMATCH,
"Too few \"in\" arguments (handling in)");
goto out;
}
args[i+offset] = (void *)&in_args[in_pos];
in_pos++;
break;
case GI_DIRECTION_OUT:
atypes[i+offset] = &ffi_type_pointer;
gi_base_info_unref ((GIBaseInfo *)ainfo);
if (out_pos >= n_out_args)
{
g_set_error (error,
GI_INVOKE_ERROR,
GI_INVOKE_ERROR_ARGUMENT_MISMATCH,
"Too few \"out\" arguments (handling out)");
goto out;
}
args[i+offset] = (void *)&out_args[out_pos];
out_pos++;
break;
case GI_DIRECTION_INOUT:
atypes[i+offset] = &ffi_type_pointer;
gi_base_info_unref ((GIBaseInfo *)ainfo);
if (in_pos >= n_in_args)
{
g_set_error (error,
GI_INVOKE_ERROR,
GI_INVOKE_ERROR_ARGUMENT_MISMATCH,
"Too few \"in\" arguments (handling inout)");
goto out;
}
if (out_pos >= n_out_args)
{
g_set_error (error,
GI_INVOKE_ERROR,
GI_INVOKE_ERROR_ARGUMENT_MISMATCH,
"Too few \"out\" arguments (handling inout)");
goto out;
}
args[i+offset] = (void *)&in_args[in_pos];
in_pos++;
out_pos++;
break;
default:
gi_base_info_unref ((GIBaseInfo *)ainfo);
g_assert_not_reached ();
}
}
if (throws)
{
args[n_invoke_args - 1] = &error_address;
atypes[n_invoke_args - 1] = &ffi_type_pointer;
}
if (in_pos < n_in_args)
{
g_set_error (error,
GI_INVOKE_ERROR,
GI_INVOKE_ERROR_ARGUMENT_MISMATCH,
"Too many \"in\" arguments (at end)");
goto out;
}
if (out_pos < n_out_args)
{
g_set_error (error,
GI_INVOKE_ERROR,
GI_INVOKE_ERROR_ARGUMENT_MISMATCH,
"Too many \"out\" arguments (at end)");
goto out;
}
if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_invoke_args, rtype, atypes) != FFI_OK)
goto out;
g_return_val_if_fail (return_value, FALSE);
/* See comment for GIFFIReturnValue above */
switch (rtag)
{
case GI_TYPE_TAG_FLOAT:
return_value_p = &ffi_return_value.v_float;
break;
case GI_TYPE_TAG_DOUBLE:
return_value_p = &ffi_return_value.v_double;
break;
case GI_TYPE_TAG_INT64:
case GI_TYPE_TAG_UINT64:
return_value_p = &ffi_return_value.v_uint64;
break;
default:
return_value_p = &ffi_return_value.v_long;
}
ffi_call (&cif, function, return_value_p, args);
if (local_error)
{
g_propagate_error (error, local_error);
success = FALSE;
}
else
{
gi_type_info_extract_ffi_return_value (rinfo, &ffi_return_value, return_value);
success = TRUE;
}
out:
gi_base_info_unref ((GIBaseInfo *)rinfo);
return success;
}
void
gi_callable_info_class_init (gpointer g_class,
gpointer class_data)
{
GIBaseInfoClass *info_class = g_class;
info_class->info_type = GI_INFO_TYPE_CALLABLE;
}