From 453dd4be9e906078a7fb7466819ce1876a629a45 Mon Sep 17 00:00:00 2001 From: Evan Welsh Date: Tue, 7 May 2024 06:46:13 -0700 Subject: [PATCH] girepository: Add APIs for sync, async, and finish function annotations --- girepository/gicallableinfo.c | 191 +++++++++++++++++++++++++++++ girepository/gicallableinfo.h | 12 ++ girepository/gifunctioninfo.c | 3 + girepository/girnode-private.h | 8 ++ girepository/girnode.c | 135 ++++++++++++++++++++ girepository/girparser.c | 122 ++++++++++++++++++ girepository/girwriter.c | 15 +++ girepository/gitypelib-internal.h | 35 ++++-- girepository/gitypes.h | 1 + girepository/tests/callable-info.c | 136 ++++++++++++++++++++ girepository/tests/meson.build | 3 + 11 files changed, 652 insertions(+), 9 deletions(-) create mode 100644 girepository/tests/callable-info.c 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,