gobject/gparam.c: In function ‘g_param_type_register_static’:
gobject/gparam.c:1434:3: error: missing initializer for field ‘value_table’ of ‘GTypeInfo’ {aka ‘struct _GTypeInfo’}
1434 | };
| ^
In file included from gobject/gvalue.h:26,
from gobject/gparam.h:26,
from gobject/gparam.c:26:
gobject/gtype.h:1063:26: note: ‘value_table’ declared here
1063 | const GTypeValueTable *value_table;
| ^~~~~~~~~~~
gobject/gtypemodule.c: In function ‘g_type_module_get_type’:
gobject/gtypemodule.c:154:7: error: missing initializer for field ‘value_table’ of ‘GTypeInfo’ {aka ‘const struct _GTypeInfo’}
154 | };
| ^
In file included from gobject/gtypeplugin.h:24,
from gobject/gtypemodule.c:22:
gobject/gtype.h:1063:26: note: ‘value_table’ declared here
1063 | const GTypeValueTable *value_table;
| ^~~~~~~~~~~
gobject/gtypeplugin.c: In function ‘g_type_plugin_get_type’:
gobject/gtypeplugin.c:91:7: error: missing initializer for field ‘class_init’ of ‘GTypeInfo’ {aka ‘const struct _GTypeInfo’}
91 | };
| ^
In file included from gobject/gtypeplugin.h:24,
from gobject/gtypeplugin.c:20:
gobject/gtype.h:1053:26: note: ‘class_init’ declared here
1053 | GClassInitFunc class_init;
| ^~~~~~~~~~
gobject/gtype.c: In function ‘iface_node_has_available_offset_L’:
gobject/gtype.c:1288:42: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘int’
1288 | if (G_ATOMIC_ARRAY_DATA_SIZE (offsets) <= offset)
| ^~
The version of `black` on the CI server wanted these changes. Make them
to keep the `style-check-diff` CI job from constantly failing.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
This commit only looks at the `Returns:` lines in the documentation, and
has examined all of them in the file. Function arguments have not been
checked.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2227
This commit only looks at the `Returns:` lines in the documentation, and
has examined all of them in the file. Function arguments have not been
checked.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2227
This commit only looks at the `Returns:` lines in the documentation, and
has examined all of them in the file. Function arguments have not been
checked.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2227
When this is called on the source or target, the weak notify of the
corresponding object is called without the GWeakRef being cleared.
See https://gitlab.gnome.org/GNOME/glib/-/issues/2266 for that issue.
This means that a strong reference to these zombie objects can be
retrieved from the GWeakRefs and the previous assumption that this can't
happen was wrong. Remove the assertion for that accordingly and handle
this case.
Specifically, all signal handlers and weak notifies of the object are
already gone and must not be disconnected/removed a second time, or
otherwise memory corruption would be caused. Instead just set the
GWeakRef to NULL and handle it otherwise as if the GWeakRef didn't give
a strong reference to begin with.
Fixes https://gitlab.gnome.org/GNOME/glib/-/issues/2265
This especially has the effect that any GWeakRefs to the object will not
necessarily be set to NULL yet if called as part of
g_object_run_dispose() and not as part of g_object_unref().
gobject/tests/value.c: In function ‘test_valuearray_basic’:
gobject/tests/value.c:253:17: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’}
253 | for (i = 0; i < a->n_values - 1; i++)
| ^
gobject/tests/value.c:257:17: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’}
257 | for (i = 0; i < a->n_values; i++)
| ^
This was inconsistently handled before and only explicit unbinding or
finalizing the binding would've previously released the transform
function. If the source/target were finalized while more strong
references to the binding still existed then the transform function
would stay alive and only the binding itself would be deactivated.
Unbinding can happen from one thread while a property notification is
being handled concurrently in another one.
To solve this, introduce a reference counter for the transform function
that ensures that it always stays valid while in use and protect access
to the one stored inside the binding with the unbind mutex.
It's possible for g_binding_unbind() to be called at the same time as
one (or both) of source and target are being finalized. The resulting
unbinding needs to be protected with a mutex to ensure that it only
happens exactly once.
As the first reference is owned by both weak notifies and the caller of
g_object_bind_property(), additional indirections are needed to ensure that
unreffing the first reference after creation still unbinds the binding
as before. This seems to be a common code pattern and how this was
intended to be used, but is only safe in single-threaded contexts as it
relies on both the source and target object to be still alive.
Add a lot of comments to the code about all these dependencies and a
couple of assertions to ensure they hold valid.
Also document that inconsistent reference ownership handling of
g_binding_unbind() that makes it unfit for automatically generated
language bindings.
gobject/gobject.c: In function ‘g_object_new_internal’:
gobject/gobject.c:1962:25: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’}
1962 | for (j = 0; j < n_params; j++)
| ^
gobject/gobject.c:1989:21: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’}
1989 | for (i = 0; i < n_params; i++)
| ^
gobject/gobject.c: In function ‘g_object_new_with_custom_constructor’:
gobject/gobject.c:1836:21: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’}
1836 | for (j = 0; j < n_params; j++)
| ^
gobject/gobject.c:1914:17: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’}
1914 | for (i = 0; i < n_params; i++)
| ^
gobject/gobject.c: In function ‘g_object_class_install_properties’:
gobject/gobject.c:766:17: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’}
766 | for (i = 1; i < n_pspecs; i++)
| ^
gobject/gtype.c: In function ‘g_type_interface_add_prerequisite’:
gobject/gtype.c:1607:21: error: comparison of integer expressions of different signedness: ‘guint’ {aka ‘unsigned int’} and ‘int’
1607 | for (i = 0; i < prerequisite_node->n_supers + 1; i++)
| ^
The problem occurs because we keep a pointer inside the allocated block,
instead of a pointer to the start of the block. This memory exists for
the lifetime of the application, so let's silence it.
This is probably abuse of VALGRIND_MALLOCLIKE_BLOCK(), which is really
intended for use in memory allocators, but gtype.c already uses it in
two other places, and it's a practical solution. I wrote another larger
fix for this issue that involves keeping an array of extra pointers when
running under valgrind. This is simpler.
Fix suggested by Philip Withnall
```
==180238== 16 bytes in 1 blocks are possibly lost in loss record 3,078 of 16,075
==180238== at 0x483BB1A: calloc (vg_replace_malloc.c:762)
==180238== by 0x5489495: g_malloc0 (gmem.c:132)
==180238== by 0x5489754: g_malloc0_n (gmem.c:364)
==180238== by 0x53FDBEE: type_set_qdata_W (gtype.c:3722)
==180238== by 0x53FDEE8: type_add_flags_W (gtype.c:3787)
==180238== by 0x53FC348: g_type_register_fundamental (gtype.c:2662)
==180238== by 0x53D969B: _g_enum_types_init (genums.c:124)
==180238== by 0x53FF058: gobject_init (gtype.c:4432)
==180238== by 0x53FF082: gobject_init_ctor (gtype.c:4493)
==180238== by 0x4010F29: call_init.part.0 (dl-init.c:72)
==180238== by 0x4011030: call_init (dl-init.c:30)
==180238== by 0x4011030: _dl_init (dl-init.c:119)
==180238== by 0x4002149: ??? (in /usr/lib64/ld-2.30.so)
```
Fixes#2076
The problem occurs because we keep a pointer inside the allocated block,
instead of a pointer to the start of the block:
```
==180238== 16 bytes in 1 blocks are possibly lost in loss record 3,086 of 16,075
==180238== at 0x483980B: malloc (vg_replace_malloc.c:309)
==180238== by 0x548942C: g_malloc (gmem.c:102)
==180238== by 0x54A4748: g_slice_alloc (gslice.c:1025)
==180238== by 0x53D0AAF: freelist_alloc (gatomicarray.c:77)
==180238== by 0x53D0B85: _g_atomic_array_copy (gatomicarray.c:133)
==180238== by 0x53F8E6D: iface_node_set_offset_L (gtype.c:1347)
==180238== by 0x53F91F1: type_node_add_iface_entry_W (gtype.c:1444)
==180238== by 0x53F93DF: type_add_interface_Wm (gtype.c:1477)
==180238== by 0x53FC946: g_type_add_interface_static (gtype.c:2852)
==180238== by 0x4A3D53A: gtk_menu_shell_accessible_get_type_once (gtkmenushellaccessible.c:26)
==180238== by 0x4A3D495: gtk_menu_shell_accessible_get_type (gtkmenushellaccessible.c:26)
==180238== by 0x4C8AC44: gtk_menu_shell_class_init (gtkmenushell.c:424)
```
Note we cannot use VALGRIND_FREELIKE_BLOCK() in freelist_free() because we
have not actually freed the FreeListNode and need to dereference it in
freelist_alloc() to decide whether to reuse the block. That would result
in a use-after-free warning before we would get a chance to call
VALGRIND_MALLOCLIKE_BLOCK() in the reuse path.
Also note that this free list only ever grows: it never shrinks for the
lifetime of the application, so nothing here will ever be truely freed,
although unused elements are eligible for reuse.
Fix suggested by Philip Withnall
Related: #2076
gobject/gtype.c: In function ‘type_node_add_iface_entry_W’:
gobject/gtype.c:1379:21: error: comparison of integer expressions of different signedness: ‘guint’ {aka ‘unsigned int’} and ‘int’
1379 | for (i = 0; i < num_entries; i++)
| ^
gobject/gtype.c: In function ‘lookup_iface_entry_I’:
gobject/gtype.c:599:17: error: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’
599 | if (index < IFACE_ENTRIES_N_ENTRIES (entries))
| ^
gobject/gatomicarray.h:51:8: note: in definition of macro ‘G_ATOMIC_ARRAY_DO_TRANSACTION’
51 | {_C_;} \
| ^~~
Half of the references to `init_state` in `gtype.c` already correctly
accessed it atomically, but a couple didn’t. Drop the `volatile`
qualifier from its declaration, as that’s not necessary for atomic
access.
Note that this is the `init_state` in `TypeData`, *not* the `init_state`
in `IFaceEntry`.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #600
These variables were already (correctly) accessed atomically. The
`volatile` qualifier doesn’t help with that.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #600
This is an API break, but no third party code should be touching
`GObject.ref_count`, let alone in a way which would be changed by the
removal of the `volatile` qualifier.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #600
This is an API break, but it should not affect third party code since
that code should not be interacting with the `data` member in a way that
invokes its `volatile` qualifier (such as copying to an intermediate
variable).
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #600
These variables were already (correctly) accessed atomically. The
`volatile` qualifier doesn’t help with that.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #600
http://isvolatileusefulwiththreads.in/c/
It’s possible that the variables here are only marked as volatile
because they’re arguments to `g_once_*()`. Those arguments will be
modified in a subsequent commit.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #600
The previous code consumed a larger additional amount of stack space.
That is because it would allocate the temporary buffer for GValues on
the stack with "g_newa (GValue, 1)" and thus the required stack
space grew with the number of arguments. Granted, this is already
a variadic C function, so the caller already placed that many elements
on the stack. For example, on the stack there are the property names
and the pointers to the arguments, which should amount to roughly
O(n_args * 16) (on 64 bit, with pointers being 8 bytes large).
That is not bad, because it means in the previous version the stack space
would grow linear with the already used stack space. However, a GValue is
an additional 24 bytes (on 64 bit), which probably more than doubles the
required stack space. Let's avoid that, by allocating the temporary list
on the heap after a certain threshold. This probably more than doubles the
number of possible arguments before the stack overflows.
Also, previously the heap allocated "params" array only grew one element
per iteration. Of course, it is likely that libc anyway reallocates
the buffers by growing the space exponentially. So realloc(ptr, 1)
probably does not O() scale worse than doubling the buffer sizes ourselves.
However, it seems clearer to keep track of the allocated sizes ourself, and
only call realloc() when we determine that we are out of space.
Especially because we need to update the value pointers on reallocation.
Note that we now require a heap allocation both for the "params" and the
"values" list. Theoretically that could be combined by using one buffer
for both. But that would make the code more complicated.
Now we pre-allocate buffers for 16 elements on the stack. That
is (16 * (16 + 24) bytes (or 640 bytes) on the stack. I think that
is still acceptable.
Two out of three callers pass the count argument from a variable
of type guint. And the third is currently an always positive gint.
We should use the correct integer type that matches the type as it
used otherwise.
This commit is the unmodified results of running
```
black $(git ls-files '*.py')
```
with black version 19.10b0. See #2046.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Add a test for a signal returning interface types, using
the generic marshaller. This will hopefully exercise newly
added code in value_from_ffi_type().
This tests the new functionality that
g_type_interface_instantiable_prerequisite
was added for.
Before the changes, this fails with
GObject-WARNING **: Unable to convert a value of type \
GObject to a value of type Foo
We do the same test with g_object_bind_property_with_closures
as well, to exercise g_cclosure_marshal_generic.
Use the new g_type_interface_instantiable_prerequisite() to check
compatibility for transform functions.
In particular, this allows interfaces (in my case GDK_TYPE_PAINTABLE) to
be transformed to/from any GObject type (in my case G_TYPE_OBJECT) using
the transform function registered to transform between any 2 objects
(g_value_object_transform_value() does a type check and uses NULL if the
types don't match).
And this in turn allows be to g_object_bind_property() a gobject-typed
generic property (GtkListItem::item) to a GtkImage::paintable.
Tests for the new functionality are included.
Use the newly added g_type_interface_instantiable_prerequisite() to
allow closure return values being interfaces by looking up the
instantiable type for the interface and using its GValue accessors.
This function returns the most specific instantiatable type
that is a prerequisite for a given interface.
This type is necessary in particular when dealing with GValues
because a GValue contains an instance of a type.
This commit includes tests for the new API.
Rather than using a mixture of ‘instantiable’ and ‘instantiatable’
everywhere, standardise on the term which is already in the public API.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
g_has_typeof macro is wrongly in the public g_ namespace, internaly
symbols are usually in the glib_ namespace. This will also allow to
define glib_typeof differently on non-GNUC compilers (e.g. c++11
decltype).
commit 916297be79 added a hash table
to provide constant time lookups of signal handlers.
Unfortunately, that commit neglected to remove handlers from
g_signal_connect_object calls from the hash table that are
disconnected implicitly when the associated object goes away.
This commit addresses that bug by changing the closure invalidate
handler associated with the signal connection to properly remove the
handler from the hash table.
It may be defined by the environment (we document that as being allowed)
— if so, individual files should not try to redefine it, as that causes
a preprocessor warning.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Add a set of new URI parsing and generating functions, including a new
parsed-URI type GUri. Move all the code from gurifuncs.c into guri.c,
reimplementing some of those functions (and
g_string_append_uri_encoded()) in terms of the new code.
Fixes:
https://gitlab.gnome.org/GNOME/glib/issues/110
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
The G_VALUE_NOCOPY_CONTENTS for strings can only be used when collecting them
and not when copying them.
Instead only avoid copies for strings that are interned.
Fixes#2141
This was mostly machine generated with the following command:
```
codespell \
--builtin clear,rare,usage \
--skip './po/*' --skip './.git/*' --skip './NEWS*' \
--write-changes .
```
using the latest git version of `codespell` as per [these
instructions](https://github.com/codespell-project/codespell#user-content-updating).
Then I manually checked each change using `git add -p`, made a few
manual fixups and dropped a load of incorrect changes.
There are still some outdated or loaded terms used in GLib, mostly to do
with git branch terminology. They will need to be changed later as part
of a wider migration of git terminology.
If I’ve missed anything, please file an issue!
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Some editors automatically remove trailing blank lines, or
automatically add a trailing newline to avoid having a trailing
non-blank line that is not terminated by a newline. To avoid unrelated
whitespace changes when users of such editors contribute to GLib,
let's pre-emptively normalize all files.
Unlike more intrusive whitespace normalization like removing trailing
whitespace from each line, this seems unlikely to cause significant
issues with cherry-picking changes to stable branches.
Implemented by:
find . -name '*.[ch]' -print0 | \
xargs -0 perl -0777 -p -i -e 's/\n+\z//g; s/\z/\n/g'
Signed-off-by: Simon McVittie <smcv@collabora.com>
The various `g_strdup_printf()` returns values in the implementations of GValue
lcopy_func are runtime checks which could be disabled if one wants and therefore
should be handled as such with g_return_val_if_fail()
The property strings are interned already, so this potentially allows for faster
comparisons. The property strings were already not copied, as they were tagged
as static.
This adds support to be able to explicitely stored interned strings into
G_TYPE_STRING GValue.
This is useful for cases where the user:
* *knows* the string to be stored in the GValue is canonical
* Wants to know whther the string stored is canonical
This allows:
* zero-cost GValue copy (the content is guaranteed to be unique and exist
throughout the process life)
* zero-cost string equality checks (if both string GValue are interned, you just
need to check the pointers for equality or not, instead of doing a strcmp).
Fixes#2109
The glib-mkenums program allows generating code to handle enums/flags
with very different purposes. One of its purposes could be generating
per-enum/flag methods to be exposed in a library API, and while doing
that, it would be nice to have a way to specify in which API version
the enum/flag was introduced, so that the same version could be shown
in the generated API methods.
E.g. From the following code:
/**
* QmiWmsMessageProtocol:
* @QMI_WMS_MESSAGE_PROTOCOL_CDMA: CDMA.
* @QMI_WMS_MESSAGE_PROTOCOL_WCDMA: WCDMA.
*
* Type of message protocol.
*
* Since: 1.0
*/
typedef enum { /*< since=1.0 >*/
QMI_WMS_MESSAGE_PROTOCOL_CDMA = 0x00,
QMI_WMS_MESSAGE_PROTOCOL_WCDMA = 0x01
} QmiWmsMessageProtocol;
The template would allow us to generate a method documented like this,
including the Since tag with the value given in the mkenums 'since' tag.
/**
* qmi_wms_message_protocol_get_string:
* @val: a QmiWmsMessageProtocol.
*
* Gets the nickname string for the #QmiWmsMessageProtocol specified at @val.
*
* Returns: (transfer none): a string with the nickname, or %NULL if not found. Do not free the returned value.
* Since: 1.0
*/
const gchar *qmi_wms_message_protocol_get_string (QmiWmsMessageProtocol val);
Signed-off-by: Aleksander Morgado <aleksander@aleksander.es>
Meson 0.54.0 added a new method meson.override_dependency() that must be
used to ensure dependency consistency. This patch ensures a project that
depends on glib will never link to a mix of system and subproject
libraries. It would happen in such cases:
The system has glib 2.40 installed, and a project does:
dependency('glib-2.0', version: '>=2.60',
fallback: ['glib', 'glib_dep'])
dependency('gobject-2.0')
The first call will configure glib subproject because the system libglib
is too old, but the 2nd call will return system libgobject.
By overriding 'gobject-2.0' dependency while configuring glib subproject
during the first call, meson knows that on the 2nd call it must return
the subproject dependency instead of system dependency.
This also has the nice side effect that with Meson >0.54.0 an
application depending on glib can declare the fallback without knowing
the dependency variable name: dependency('glib-2.0', fallback: 'glib').
The __declspec(dllexport) attribute in itself doesn't imply
'extern' - thus any intended variable declaration with
GLIB_VAR/GOBJECT_VAR would actually be a variable definition. With
C compilers defaulting to -fcommon, this isn't an issue, but
upcoming compilers (GCC 10 and Clang 11) will default to -fno-common,
ending up with duplicate definitions of these variables.
Rename the variables involved so that people get a slightly more
obvious critical warning when they try to ref an object which has
already been finalised.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Making this validation code public allows projects to validate a
GParamSpec name before creating it. While hard-coded GParamSpec don't
need this, we can't afford crashing the main program for dynamically
generated GParamSpec from user-created data.
In such case, we will need to validate the param names and return errors
instead of trying to create a GParamSpec with invalid names.
Includes modifications from Philip Withnall and Emmanuele Bassi to
rearrange the new function addition and split it into one function for
GParamSpecs and one for GSignals.
When calling `g_set_object()` for a type derived from `GObject`, GCC 9.2
was giving the following strict aliasing warning:
```
../../source/malcontent/libmalcontent-ui/user-controls.c:1001:21: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
1001 | if (g_set_object (&self->user, user))
/opt/gnome/install/include/glib-2.0/gobject/gobject.h:744:33: note: in definition of macro ‘g_set_object’
744 | (g_set_object) ((GObject **) (object_ptr), (GObject *) (new_object)) \
| ^~~~~~~~~~
```
This was due to the `(GObject **)` cast.
Pass the pointer through a union to squash this warning. We already do
some size and type checks of the dereferenced type, which should catch
casual errors. The `g_object_ref()` and `g_object_unref()` calls which
subsequently happen inside the `g_set_object()` function also do some
dynamic type checks.
Add a test.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
As with `g_variant_new()` (or any varargs function which takes integer
literals of differing widths), callers need to be careful to ensure
their integer literals have the right width.
Tweak the documentation for `g_object_new()`, `g_object_set()` and
`g_object_get()` to clarify this. The documentation for `g_object_get()`
shows that it is not subject to the same caveats, since it operates on
pointers.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Closes: #833
While we automatically define cleanup functions for the module, we don't
do it for the module class.
This will allow to manage the ownership of the class when reffing it
without having to cast it to GTypeClass.
The `G_TYPE_IS_INSTANTIATABLE()` check is redundant with a precondition
on the function.
The `g_type_class_peek()` check seems like a pointless restriction: it
should be possible to check for a signal from a class init function.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Fixes: #650
Use the new g_type_interface_instantiable_prerequisite() to check
compatibility for transform functions.
In particular, this allows interfaces (in my case GDK_TYPE_PAINTABLE) to
be transformed to/from any GObject type (in my case G_TYPE_OBJECT) using
the transform function registered to tranform between any 2 objects
(g_value_object_transform_value() does a type check and uses NULL if the
types don't match).
And this in turn allows be to g_object_bind_property() a gobject-typed
generic property (GtkListItem::item) to a GtkImage::paintable.
Use the newly added g_type_interface_instantiable_prerequisite() to
allow closure return values being interfaces by looking up the
instantiable type for the interface and usings its GValue accessors.
There is (at most) a single GType that is instantiable and a
prerequisite for an interface. This function returns that type.
This type is necessary in particular when dealing with GValues because a
GValue contains an instance of a type.
The format has never previously been specified. It can be anything, but
for sanity’s sake disallow empty strings.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Helps: #358
It provides more useful output on failure, and isn’t compiled out when
building with `G_DISABLE_ASSERT`.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
It provides more useful output on failure, and isn’t compiled out when
building with `G_DISABLE_ASSERT`.
Signed-off-by: Philip Withnall <withnall@endlessm.com>