diff --git a/girepository/gicallableinfo.c b/girepository/gicallableinfo.c
index 23d31f1a4..4f1439c28 100644
--- a/girepository/gicallableinfo.c
+++ b/girepository/gicallableinfo.c
@@ -35,6 +35,8 @@
#include "girffi.h"
#include "gicallableinfo.h"
+#define GET_BLOB(Type, rinfo) ((Type *) &rinfo->typelib->data[rinfo->offset])
+
/* GICallableInfo functions */
/**
@@ -839,3 +841,192 @@ gi_callable_info_class_init (gpointer g_class,
info_class->info_type = GI_INFO_TYPE_CALLABLE;
}
+
+static GICallableInfo *
+get_method_callable_info_for_index (GIBaseInfo *rinfo, unsigned int index)
+{
+ GIBaseInfo *container = rinfo->container;
+
+ g_assert (container);
+
+ if (GI_IS_OBJECT_INFO (container))
+ return (GICallableInfo *) gi_object_info_get_method ((GIObjectInfo *) container, index);
+
+ if (GI_IS_INTERFACE_INFO (container))
+ return (GICallableInfo *) gi_interface_info_get_method ((GIInterfaceInfo *) container,
+ index);
+
+ return NULL;
+}
+
+static GICallableInfo *
+get_callable_info_for_index (GIBaseInfo *rinfo,
+ unsigned int index)
+{
+ if (!rinfo->container)
+ {
+ GIBaseInfo *info = gi_info_from_entry (rinfo->repository, rinfo->typelib, index);
+
+ g_assert (GI_IS_CALLABLE_INFO (info));
+
+ return (GICallableInfo *) info;
+ }
+
+ return get_method_callable_info_for_index (rinfo, index);
+}
+
+/**
+ * gi_callable_info_get_async_function
+ * @info: a callable info structure
+ *
+ * Gets the callable info for the callable's asynchronous version
+ *
+ * Returns: (transfer full) (nullable): a [class@GIRepository.CallableInfo] for the
+ * async function or `NULL` if not defined.
+ * Since: 2.80
+ */
+GICallableInfo *
+gi_callable_info_get_async_function (GICallableInfo *info)
+{
+ GIBaseInfo *rinfo = (GIBaseInfo *) info;
+
+ switch (gi_base_info_get_info_type (rinfo))
+ {
+ case GI_INFO_TYPE_FUNCTION:
+ {
+ FunctionBlob *blob = GET_BLOB (FunctionBlob, rinfo);
+ if (blob->is_async || blob->sync_or_async == ASYNC_SENTINEL)
+ return NULL;
+
+ return get_callable_info_for_index (rinfo, blob->sync_or_async);
+ }
+ case GI_INFO_TYPE_VFUNC:
+ {
+ VFuncBlob *blob = GET_BLOB (VFuncBlob, rinfo);
+ if (blob->is_async || blob->sync_or_async == ASYNC_SENTINEL)
+ return NULL;
+
+ return get_method_callable_info_for_index (rinfo, blob->sync_or_async);
+ }
+ case GI_INFO_TYPE_CALLBACK:
+ case GI_INFO_TYPE_SIGNAL:
+ return NULL;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/**
+ * gi_callable_info_get_sync_function
+ * @info: a callable info structure
+ *
+ * Gets the callable info for the callable's synchronous version
+ *
+ * Returns: (transfer full) (nullable): a [class@GIRepository.CallableInfo] for the
+ * sync function or `NULL` if not defined.
+ * Since: 2.80
+ */
+GICallableInfo *
+gi_callable_info_get_sync_function (GICallableInfo *info)
+{
+ GIBaseInfo *rinfo = (GIBaseInfo *) info;
+
+ switch (gi_base_info_get_info_type (rinfo))
+ {
+ case GI_INFO_TYPE_FUNCTION:
+ {
+ FunctionBlob *blob = GET_BLOB (FunctionBlob, rinfo);
+ if (!blob->is_async || blob->sync_or_async == ASYNC_SENTINEL)
+ return NULL;
+
+ return get_callable_info_for_index (rinfo, blob->sync_or_async);
+ }
+ case GI_INFO_TYPE_VFUNC:
+ {
+ VFuncBlob *blob = GET_BLOB (VFuncBlob, rinfo);
+ if (!blob->is_async || blob->sync_or_async == ASYNC_SENTINEL)
+ return NULL;
+
+ return get_method_callable_info_for_index (rinfo, blob->sync_or_async);
+ }
+ case GI_INFO_TYPE_CALLBACK:
+ case GI_INFO_TYPE_SIGNAL:
+ return NULL;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/**
+ * gi_callable_info_get_finish_function
+ * @info: a callable info structure
+ *
+ * Gets the info for an async function's corresponding finish function
+ *
+ * Returns: (transfer full) (nullable): a [class@GIRepository.CallableInfo] for the
+ * finish function or `NULL` if not defined.
+ * Since: 2.80
+ */
+GICallableInfo *
+gi_callable_info_get_finish_function (GICallableInfo *info)
+{
+ GIBaseInfo *rinfo = (GIBaseInfo *) info;
+
+ switch (gi_base_info_get_info_type (rinfo))
+ {
+ case GI_INFO_TYPE_FUNCTION:
+ {
+ FunctionBlob *blob = GET_BLOB (FunctionBlob, rinfo);
+ if (!blob->is_async || blob->finish == ASYNC_SENTINEL)
+ return NULL;
+
+ return get_callable_info_for_index (rinfo, blob->finish);
+ }
+ case GI_INFO_TYPE_VFUNC:
+ {
+ VFuncBlob *blob = GET_BLOB (VFuncBlob, rinfo);
+ if (!blob->is_async || blob->finish == ASYNC_SENTINEL)
+ return NULL;
+
+ return get_method_callable_info_for_index (rinfo, blob->finish);
+ }
+ case GI_INFO_TYPE_CALLBACK:
+ case GI_INFO_TYPE_SIGNAL:
+ return NULL;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/**
+ * gi_callable_info_is_async
+ * @info: a callable info structure
+ *
+ * Gets whether a callable is ‘async’. Async callables have a
+ * [type@Gio.AsyncReadyCallback] parameter and user data.
+ *
+ * Returns: true if the callable is async
+ * Since: 2.80
+ */
+gboolean
+gi_callable_info_is_async (GICallableInfo *callable_info)
+{
+ GIBaseInfo *info = (GIBaseInfo *) callable_info;
+ switch (gi_base_info_get_info_type ((GIBaseInfo *) callable_info))
+ {
+ case GI_INFO_TYPE_FUNCTION:
+ {
+ return GET_BLOB (FunctionBlob, info)->is_async;
+ }
+ case GI_INFO_TYPE_VFUNC:
+ {
+ return GET_BLOB (VFuncBlob, info)->is_async;
+ }
+ case GI_INFO_TYPE_CALLBACK:
+ case GI_INFO_TYPE_SIGNAL:
+ return FALSE;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
diff --git a/girepository/gicallableinfo.h b/girepository/gicallableinfo.h
index 293f52104..37d64a2c8 100644
--- a/girepository/gicallableinfo.h
+++ b/girepository/gicallableinfo.h
@@ -116,4 +116,16 @@ gboolean gi_callable_info_invoke (GICallableInfo *info
GI_AVAILABLE_IN_ALL
GITransfer gi_callable_info_get_instance_ownership_transfer (GICallableInfo *info);
+GI_AVAILABLE_IN_ALL
+GICallableInfo * gi_callable_info_get_async_function (GICallableInfo *info);
+
+GI_AVAILABLE_IN_ALL
+GICallableInfo * gi_callable_info_get_sync_function (GICallableInfo *info);
+
+GI_AVAILABLE_IN_ALL
+GICallableInfo * gi_callable_info_get_finish_function (GICallableInfo *info);
+
+GI_AVAILABLE_IN_ALL
+gboolean gi_callable_info_is_async (GICallableInfo *info);
+
G_END_DECLS
diff --git a/girepository/gifunctioninfo.c b/girepository/gifunctioninfo.c
index e50cc007e..79afc26fd 100644
--- a/girepository/gifunctioninfo.c
+++ b/girepository/gifunctioninfo.c
@@ -140,6 +140,9 @@ gi_function_info_get_flags (GIFunctionInfo *info)
if (blob->wraps_vfunc)
flags = flags | GI_FUNCTION_WRAPS_VFUNC;
+ if (blob->is_async)
+ flags = flags | GI_FUNCTION_IS_ASYNC;
+
return flags;
}
diff --git a/girepository/girnode-private.h b/girepository/girnode-private.h
index 353d6d6ca..652eb92a0 100644
--- a/girepository/girnode-private.h
+++ b/girepository/girnode-private.h
@@ -123,9 +123,13 @@ struct _GIIrNodeFunction
uint8_t wraps_vfunc : 1;
uint8_t throws : 1;
uint8_t instance_transfer_full : 1;
+ uint8_t is_async : 1;
char *symbol; /* (owned) */
char *property; /* (owned) */
+ char *finish_func; /* (owned) */
+ char *sync_func; /* (owned) */
+ char *async_func; /* (owned) */
GIIrNodeParam *result; /* (owned) */
GList *parameters; /* (element-type GIIrNode) (owned) */
@@ -237,8 +241,12 @@ struct _GIIrNodeVFunc
uint8_t is_class_closure : 1;
uint8_t throws : 1;
uint8_t instance_transfer_full : 1;
+ uint8_t is_async : 1;
char *invoker; /* (owned) */
+ char *finish_func; /* (owned) */
+ char *sync_func; /* (owned) */
+ char *async_func; /* (owned) */
GList *parameters; /* (element-type GIIrNode) (owned) */
GIIrNodeParam *result; /* (owned) */
diff --git a/girepository/girnode.c b/girepository/girnode.c
index aacc2a0d3..50a798ab5 100644
--- a/girepository/girnode.c
+++ b/girepository/girnode.c
@@ -223,6 +223,9 @@ gi_ir_node_free (GIIrNode *node)
g_free (node->name);
g_free (function->symbol);
g_free (function->property);
+ g_free (function->async_func);
+ g_free (function->sync_func);
+ g_free (function->finish_func);
gi_ir_node_free ((GIIrNode *)function->result);
for (l = function->parameters; l; l = l->next)
gi_ir_node_free ((GIIrNode *)l->data);
@@ -281,6 +284,9 @@ gi_ir_node_free (GIIrNode *node)
GIIrNodeVFunc *vfunc = (GIIrNodeVFunc *)node;
g_free (node->name);
+ g_free (vfunc->async_func);
+ g_free (vfunc->sync_func);
+ g_free (vfunc->finish_func);
g_free (vfunc->invoker);
for (l = vfunc->parameters; l; l = l->next)
gi_ir_node_free ((GIIrNode *)l->data);
@@ -1218,6 +1224,25 @@ get_index_of_member_type (GIIrNodeInterface *node,
return index;
}
+static int
+get_index_for_function (GIIrTypelibBuild * build,
+ GIIrNode *parent,
+ const char *name)
+{
+ if (parent == NULL)
+ {
+ uint16_t index = find_entry(build, name);
+ if (index == 0) return -1;
+
+ return index;
+ }
+
+ int index = get_index_of_member_type((GIIrNodeInterface *) parent, GI_IR_NODE_FUNCTION, name);
+ if (index != -1) return index;
+
+ return -1;
+}
+
static void
serialize_type (GIIrTypelibBuild *build,
GIIrNodeType *node,
@@ -1706,6 +1731,61 @@ gi_ir_node_build_typelib (GIIrNode *node,
blob->name = gi_ir_write_string (node->name, strings, data, offset2);
blob->symbol = gi_ir_write_string (function->symbol, strings, data, offset2);
blob->signature = signature;
+ blob->finish = ASYNC_SENTINEL;
+ blob->sync_or_async = ASYNC_SENTINEL;
+ blob->is_async = function->is_async;
+
+ if (function->is_async)
+ {
+ if (function->sync_func != NULL)
+ {
+ int sync_index =
+ get_index_for_function (build,
+ parent,
+ function->sync_func);
+
+ if (sync_index == -1)
+ {
+ g_error ("Unknown sync function %s:%s",
+ parent->name, function->sync_func);
+ }
+
+ blob->sync_or_async = (uint16_t) sync_index;
+ }
+
+ if (function->finish_func != NULL)
+ {
+ int finish_index =
+ get_index_for_function (build,
+ parent,
+ function->finish_func);
+
+ if (finish_index == -1)
+ {
+ g_error ("Unknown finish function %s:%s", parent->name, function->finish_func);
+ }
+
+ blob->finish = (uint16_t) finish_index;
+ }
+ }
+ else
+ {
+ if (function->async_func != NULL)
+ {
+ int async_index =
+ get_index_for_function (build,
+ parent,
+ function->async_func);
+
+ if (async_index == -1)
+ {
+ g_error ("Unknown async function %s:%s", parent->name, function->async_func);
+ }
+
+ blob->sync_or_async = (uint16_t) async_index;
+ }
+ }
+
if (function->is_setter || function->is_getter)
{
@@ -1876,6 +1956,9 @@ gi_ir_node_build_typelib (GIIrNode *node,
blob->class_closure = 0; /* FIXME */
blob->throws = vfunc->throws; /* Deprecated. Also stored in SignatureBlob. */
blob->reserved = 0;
+ blob->is_async = vfunc->is_async;
+ blob->finish = ASYNC_SENTINEL;
+ blob->sync_or_async = ASYNC_SENTINEL;
if (vfunc->invoker)
{
@@ -1889,6 +1972,58 @@ gi_ir_node_build_typelib (GIIrNode *node,
else
blob->invoker = 0x3ff; /* max of 10 bits */
+ if (vfunc->is_async)
+ {
+ if (vfunc->sync_func != NULL)
+ {
+ int sync_index =
+ get_index_of_member_type ((GIIrNodeInterface *) parent,
+ GI_IR_NODE_VFUNC,
+ vfunc->sync_func);
+
+ if (sync_index == -1)
+ {
+ g_error ("Unknown sync vfunc %s:%s for accessor %s",
+ parent->name, vfunc->sync_func, vfunc->invoker);
+ }
+
+ blob->sync_or_async = (uint16_t) sync_index;
+ }
+
+ if (vfunc->finish_func != NULL)
+ {
+ int finish_index =
+ get_index_of_member_type ((GIIrNodeInterface *) parent,
+ GI_IR_NODE_VFUNC,
+ vfunc->finish_func);
+
+ if (finish_index == -1)
+ {
+ g_error ("Unknown finish vfunc %s:%s for function %s",
+ parent->name, vfunc->finish_func, vfunc->invoker);
+ }
+
+ blob->finish = (uint16_t) finish_index;
+ }
+ }
+ else
+ {
+ if (vfunc->async_func != NULL)
+ {
+ int async_index =
+ get_index_of_member_type ((GIIrNodeInterface *) parent,
+ GI_IR_NODE_VFUNC,
+ vfunc->async_func);
+ if (async_index == -1)
+ {
+ g_error ("Unknown async vfunc %s:%s for accessor %s",
+ parent->name, vfunc->async_func, vfunc->invoker);
+ }
+
+ blob->sync_or_async = (uint16_t) async_index;
+ }
+ }
+
blob->struct_offset = vfunc->offset;
blob->reserved2 = 0;
blob->signature = signature;
diff --git a/girepository/girparser.c b/girepository/girparser.c
index dd0fb48b2..bae5229f0 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -391,6 +391,17 @@ locate_gir (GIIrParser *parser,
line_number, char_number, attribute, element); \
} while (0)
+#define INVALID_ATTRIBUTE(context,error,element,attribute,reason) \
+ do { \
+ int line_number, char_number; \
+ g_markup_parse_context_get_position (context, &line_number, &char_number); \
+ g_set_error (error, \
+ G_MARKUP_ERROR, \
+ G_MARKUP_ERROR_INVALID_CONTENT, \
+ "Line %d, character %d: The attribute '%s' on the element '%s' is not valid: %s", \
+ line_number, char_number, attribute, element, reason); \
+ } while (0)
+
static const char *
find_attribute (const char *name,
const char **attribute_names,
@@ -901,6 +912,9 @@ start_function (GMarkupParseContext *context,
const char *throws;
const char *set_property;
const char *get_property;
+ const char *finish_func;
+ const char *async_func;
+ const char *sync_func;
GIIrNodeFunction *function;
gboolean found = FALSE;
ParseState in_embedded_state = STATE_NONE;
@@ -951,6 +965,9 @@ start_function (GMarkupParseContext *context,
throws = find_attribute ("throws", attribute_names, attribute_values);
set_property = find_attribute ("glib:set-property", attribute_names, attribute_values);
get_property = find_attribute ("glib:get-property", attribute_names, attribute_values);
+ finish_func = find_attribute ("glib:finish-func", attribute_names, attribute_values);
+ sync_func = find_attribute ("glib:sync-func", attribute_names, attribute_values);
+ async_func = find_attribute ("glib:async-func", attribute_names, attribute_values);
if (name == NULL)
{
@@ -977,6 +994,55 @@ start_function (GMarkupParseContext *context,
else
function->deprecated = FALSE;
+ function->is_async = FALSE;
+ function->async_func = NULL;
+ function->sync_func = NULL;
+ function->finish_func = NULL;
+
+ // Only asynchronous functions have a glib:sync-func defined
+ if (sync_func != NULL)
+ {
+ if (G_UNLIKELY (async_func != NULL))
+ {
+ INVALID_ATTRIBUTE (context, error, element_name, "glib:sync-func", "glib:sync-func should only be defined with asynchronous "
+ "functions");
+
+ return FALSE;
+ }
+
+ function->is_async = TRUE;
+ function->sync_func = g_strdup (sync_func);
+ }
+
+ // Only synchronous functions have a glib:async-func defined
+ if (async_func != NULL)
+ {
+ if (G_UNLIKELY (sync_func != NULL))
+ {
+ INVALID_ATTRIBUTE (context, error, element_name, "glib:async-func", "glib:async-func should only be defined with synchronous "
+ "functions");
+
+ return FALSE;
+ }
+
+ function->is_async = FALSE;
+ function->async_func = g_strdup (async_func);
+ }
+
+ if (finish_func != NULL)
+ {
+ if (G_UNLIKELY (async_func != NULL))
+ {
+ INVALID_ATTRIBUTE (context, error, element_name, "glib:finish-func", "glib:finish-func should only be defined with asynchronous "
+ "functions");
+
+ return FALSE;
+ }
+
+ function->is_async = TRUE;
+ function->finish_func = g_strdup (finish_func);
+ }
+
if (strcmp (element_name, "method") == 0 ||
strcmp (element_name, "constructor") == 0)
{
@@ -2594,6 +2660,9 @@ start_vfunc (GMarkupParseContext *context,
const char *offset;
const char *invoker;
const char *throws;
+ const char *finish_func;
+ const char *async_func;
+ const char *sync_func;
GIIrNodeInterface *iface;
GIIrNodeVFunc *vfunc;
guint64 parsed_offset;
@@ -2613,6 +2682,9 @@ start_vfunc (GMarkupParseContext *context,
offset = find_attribute ("offset", attribute_names, attribute_values);
invoker = find_attribute ("invoker", attribute_names, attribute_values);
throws = find_attribute ("throws", attribute_names, attribute_values);
+ finish_func = find_attribute ("glib:finish-func", attribute_names, attribute_values);
+ sync_func = find_attribute ("glib:sync-func", attribute_names, attribute_values);
+ async_func = find_attribute ("glib:async-func", attribute_names, attribute_values);
if (name == NULL)
{
@@ -2666,6 +2738,56 @@ start_vfunc (GMarkupParseContext *context,
return FALSE;
}
+ vfunc->is_async = FALSE;
+ vfunc->async_func = NULL;
+ vfunc->sync_func = NULL;
+ vfunc->finish_func = NULL;
+
+ // Only asynchronous functions have a glib:sync-func defined
+ if (sync_func != NULL)
+ {
+ if (G_UNLIKELY (async_func != NULL))
+ {
+ INVALID_ATTRIBUTE (context, error, element_name, "glib:sync-func", "glib:sync-func should only be defined with asynchronous "
+ "functions");
+
+ return FALSE;
+ }
+
+ vfunc->is_async = TRUE;
+ vfunc->sync_func = g_strdup (sync_func);
+ }
+
+ // Only synchronous functions have a glib:async-func defined
+ if (async_func != NULL)
+ {
+ if (G_UNLIKELY (sync_func != NULL))
+ {
+ INVALID_ATTRIBUTE (context, error, element_name, "glib:async-func", "glib:async-func should only be defined with synchronous "
+ "functions");
+
+ return FALSE;
+ }
+
+ vfunc->is_async = FALSE;
+ vfunc->async_func = g_strdup (async_func);
+ }
+
+ if (finish_func != NULL)
+ {
+ if (G_UNLIKELY (async_func != NULL))
+ {
+ INVALID_ATTRIBUTE (context, error, element_name, "glib:finish-func", "glib:finish-func should only be defined with asynchronous "
+ "functions");
+
+ return FALSE;
+ }
+
+ vfunc->is_async = TRUE;
+ vfunc->finish_func = g_strdup (finish_func);
+ }
+
+
vfunc->invoker = g_strdup (invoker);
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
diff --git a/girepository/girwriter.c b/girepository/girwriter.c
index d0b5d0803..770cd0920 100644
--- a/girepository/girwriter.c
+++ b/girepository/girwriter.c
@@ -461,6 +461,9 @@ write_callable_info (const char *ns,
Xml *file)
{
GITypeInfo *type;
+ GICallableInfo* async_func;
+ GICallableInfo* sync_func;
+ GICallableInfo* finish_func;
if (gi_callable_info_can_throw_gerror (info))
xml_printf (file, " throws=\"1\"");
@@ -468,6 +471,18 @@ write_callable_info (const char *ns,
write_attributes (file, (GIBaseInfo*) info);
type = gi_callable_info_get_return_type (info);
+ async_func = gi_callable_info_get_async_function (info);
+ sync_func = gi_callable_info_get_sync_function (info);
+ finish_func = gi_callable_info_get_finish_function (info);
+
+ if (sync_func)
+ xml_printf (file, " glib:sync-func=\"%s\"", gi_base_info_get_name((GIBaseInfo*) sync_func));
+
+ if (finish_func)
+ xml_printf (file, " glib:finish-func=\"%s\"", gi_base_info_get_name((GIBaseInfo*) finish_func));
+
+ if (async_func)
+ xml_printf (file, " glib:async-func=\"%s\"", gi_base_info_get_name((GIBaseInfo*) async_func));
xml_start_element (file, "return-value");
diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h
index 68388e811..d66e2f068 100644
--- a/girepository/gitypelib-internal.h
+++ b/girepository/gitypelib-internal.h
@@ -598,7 +598,11 @@ typedef struct {
* return value type.
* @is_static: The function is a "static method"; in other words it's a pure
* function whose name is conceptually scoped to the object.
+ * @is_async: Whether the function is asynchronous
+ * @sync_or_async: The index of the synchronous version of the function if is_async is TRUE,
+ * otherwise, the index of the asynchronous version.
* @reserved: Reserved for future use.
+ * @finish: The index of the finish function if is_async is TRUE, otherwise ASYNC_SENTINEL
* @reserved2: Reserved for future use.
*
* TODO
@@ -614,7 +618,7 @@ typedef struct {
uint16_t constructor : 1;
uint16_t wraps_vfunc : 1;
uint16_t throws : 1;
- uint16_t index :10;
+ uint16_t index : 10;
/* Note the bits above need to match CommonBlob
* and are thus exhausted, extend things using
* the reserved block below. */
@@ -623,9 +627,13 @@ typedef struct {
uint32_t symbol;
uint32_t signature;
- uint16_t is_static : 1;
- uint16_t reserved : 15;
- uint16_t reserved2 : 16;
+ uint16_t is_static : 1;
+ uint16_t is_async : 1;
+ uint16_t sync_or_async : 10;
+ uint16_t reserved : 4;
+
+ uint16_t finish : 10;
+ uint16_t reserved2 : 6;
} FunctionBlob;
/**
@@ -993,6 +1001,7 @@ typedef struct {
} EnumBlob;
#define ACCESSOR_SENTINEL 0x3ff
+#define ASYNC_SENTINEL 0x3ff
/**
* PropertyBlob:
@@ -1098,7 +1107,9 @@ typedef struct {
* @class_closure: Set if this virtual function is the class closure of a
* signal.
* @throws: This is now additionally stored in the #SignatureBlob. (deprecated)
- * @reserved: Reserved for future use.
+ * @is_async: Whether the virtual function is asynchronous
+ * @sync_or_async: The index of the synchronous version of the virtual function if is_async is TRUE,
+ * otherwise, the index of the asynchronous version.
* @signal: The index of the signal in the list of signals of the object or
* interface to which this virtual function belongs.
* @struct_offset: The offset of the function pointer in the class struct.
@@ -1106,6 +1117,8 @@ typedef struct {
* @invoker: If a method invoker for this virtual exists, this is the offset
* in the class structure of the method. If no method is known, this value
* will be 0x3ff.
+ * @reserved: Reserved for future use.
+ * @finish: The index of the finish function if is_async is TRUE, otherwise ASYNC_SENTINEL
* @reserved2: Reserved for future use.
* @reserved3: Reserved for future use.
* @signature: Offset of the SignatureBlob describing the parameter types and
@@ -1123,14 +1136,18 @@ typedef struct {
uint16_t must_not_be_implemented : 1;
uint16_t class_closure : 1;
uint16_t throws : 1;
- uint16_t reserved :11;
+ uint16_t is_async : 1;
+ uint16_t sync_or_async : 10;
uint16_t signal;
uint16_t struct_offset;
- uint16_t invoker : 10; /* Number of bits matches @index in FunctionBlob */
- uint16_t reserved2 : 6;
+ uint16_t invoker : 10; /* Number of bits matches @index in FunctionBlob */
+ uint16_t reserved : 6;
+
+ uint16_t finish : 10;
+ uint16_t reserved2 : 6;
+ uint16_t reserved3 : 16;
- uint32_t reserved3;
uint32_t signature;
} VFuncBlob;
diff --git a/girepository/gitypes.h b/girepository/gitypes.h
index 21c873b2e..03fb738ee 100644
--- a/girepository/gitypes.h
+++ b/girepository/gitypes.h
@@ -416,6 +416,7 @@ typedef enum
GI_FUNCTION_IS_GETTER = 1 << 2,
GI_FUNCTION_IS_SETTER = 1 << 3,
GI_FUNCTION_WRAPS_VFUNC = 1 << 4,
+ GI_FUNCTION_IS_ASYNC = 1 << 8,
} GIFunctionInfoFlags;
G_END_DECLS
diff --git a/girepository/tests/callable-info.c b/girepository/tests/callable-info.c
new file mode 100644
index 000000000..97b9e6183
--- /dev/null
+++ b/girepository/tests/callable-info.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2024 GNOME Foundation, Inc.
+ * Copyright 2024 Evan Welsh
+ *
+ * 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.1 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, see .
+ *
+ * Author: Evan Welsh
+ */
+
+#include "girepository.h"
+#include "test-common.h"
+
+static void
+test_callable_get_sync_function_for_async_function (RepositoryFixture *fx,
+ const void *unused)
+{
+ GIBaseInfo *info;
+
+ info = gi_repository_find_by_name (fx->repository, "Gio", "File");
+ g_assert_nonnull (info);
+ g_assert_true (GI_IS_INTERFACE_INFO (info));
+
+ GICallableInfo *callable_info = (GICallableInfo *) gi_interface_info_find_method ((GIInterfaceInfo *) info, "load_contents_async");
+ g_assert_nonnull (callable_info);
+
+ g_assert_true (gi_callable_info_is_async (callable_info));
+
+ GICallableInfo *sync_info = gi_callable_info_get_sync_function (callable_info);
+ g_assert_nonnull (sync_info);
+
+ GICallableInfo *finish_info = gi_callable_info_get_finish_function (callable_info);
+ g_assert_nonnull (finish_info);
+
+ g_assert_cmpstr (gi_base_info_get_name ((GIBaseInfo *) sync_info), ==, "load_contents");
+ g_assert_cmpstr (gi_base_info_get_name ((GIBaseInfo *) finish_info), ==, "load_contents_finish");
+
+ GICallableInfo *async_info = gi_callable_info_get_async_function (sync_info);
+
+ g_assert_cmpstr (gi_base_info_get_name ((GIBaseInfo *) async_info), ==, "load_contents_async");
+
+ gi_base_info_unref (async_info);
+ gi_base_info_unref (sync_info);
+ gi_base_info_unref (finish_info);
+ gi_base_info_unref (callable_info);
+ gi_base_info_unref (info);
+}
+
+static void
+test_callable_get_async_function_for_sync_function (RepositoryFixture *fx,
+ const void *unused)
+{
+ GIBaseInfo *info;
+
+ info = gi_repository_find_by_name (fx->repository, "Gio", "File");
+ g_assert_nonnull (info);
+ g_assert_true (g_type_is_a (G_TYPE_FROM_INSTANCE (info), gi_interface_info_get_type ()));
+
+ GICallableInfo *callable_info = (GICallableInfo *) gi_interface_info_find_method ((GIInterfaceInfo *) info, "load_contents");
+
+ {
+ GICallableInfo *async_func = gi_callable_info_get_async_function (callable_info);
+ g_assert_nonnull (async_func);
+
+ GICallableInfo *finish_func = gi_callable_info_get_finish_function (callable_info);
+ g_assert_null (finish_func);
+
+ GICallableInfo *sync_func = gi_callable_info_get_sync_function (callable_info);
+ g_assert_null (sync_func);
+
+ gi_base_info_unref ((GIBaseInfo *) async_func);
+ }
+
+ GICallableInfo *async_info = gi_callable_info_get_async_function (callable_info);
+
+ {
+ GICallableInfo *async_func = gi_callable_info_get_async_function (async_info);
+ g_assert_null (async_func);
+
+ GICallableInfo *finish_func = gi_callable_info_get_finish_function (async_info);
+ g_assert_nonnull (finish_func);
+
+ GICallableInfo *sync_func = gi_callable_info_get_sync_function (async_info);
+ g_assert_nonnull (sync_func);
+
+ gi_base_info_unref ((GIBaseInfo *) finish_func);
+ gi_base_info_unref ((GIBaseInfo *) sync_func);
+ }
+
+ g_assert_cmpstr (gi_base_info_get_name ((GIBaseInfo *) async_info), ==, "load_contents_async");
+
+ GICallableInfo *sync_info = gi_callable_info_get_sync_function (async_info);
+
+ {
+ GICallableInfo *async_func = gi_callable_info_get_async_function (sync_info);
+ g_assert_nonnull (async_func);
+
+ GICallableInfo *finish_func = gi_callable_info_get_finish_function (sync_info);
+ g_assert_null (finish_func);
+
+ GICallableInfo *sync_func = gi_callable_info_get_sync_function (sync_info);
+ g_assert_null (sync_func);
+
+ gi_base_info_unref ((GIBaseInfo *) async_func);
+ }
+
+ g_assert_cmpstr (gi_base_info_get_name ((GIBaseInfo *) sync_info), ==, "load_contents");
+
+ gi_base_info_unref ((GIBaseInfo *) async_info);
+ gi_base_info_unref ((GIBaseInfo *) sync_info);
+ gi_base_info_unref ((GIBaseInfo *) callable_info);
+ gi_base_info_unref (info);
+}
+
+int
+main (int argc, char **argv)
+{
+ repository_init (&argc, &argv);
+
+ ADD_REPOSITORY_TEST ("/callable-info/sync-function", test_callable_get_sync_function_for_async_function, &typelib_load_spec_gio);
+ ADD_REPOSITORY_TEST ("/callable-info/async-function", test_callable_get_async_function_for_sync_function, &typelib_load_spec_gio);
+
+ return g_test_run ();
+}
diff --git a/girepository/tests/meson.build b/girepository/tests/meson.build
index 59a41227c..d3c7ea05a 100644
--- a/girepository/tests/meson.build
+++ b/girepository/tests/meson.build
@@ -43,6 +43,9 @@ if enable_gir
]
girepository_tests += {
+ 'callable-info' : {
+ 'depends': gio_gir_testing_dep,
+ },
'function-info' : {
'dependencies': [libffi_dep],
'depends': glib_gir_testing_dep,